// ==UserScript==
// @name Tiberium Alliances Real POI Bonus
// @version 1.0.1
// @namespace https://openuserjs.org/users/petui
// @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @author petui
// @description Displays actual gain/loss for POIs by taking rank multiplier properly into account
// @include http*://prodgame*.alliances.commandandconquer.com/*/index.aspx*
// @updateURL https://openuserjs.org/meta/petui/Tiberium_Alliances_Real_POI_Bonus.meta.js
// ==/UserScript==
'use strict';
(function() {
var main = function() {
'use strict';
function createRealPOIBonus() {
console.log('RealPOIBonus loaded');
qx.Class.define('RealPOIBonus', {
type: 'singleton',
extend: qx.core.Object,
statics: {
PoiTypeToPoiRankingTypeMap: {},
PoiRankingTypeToSortColumnMap: {}
},
defer: function(statics) {
statics.PoiTypeToPoiRankingTypeMap[ClientLib.Base.EPOIType.TiberiumBonus] = ClientLib.Data.Ranking.ERankingType.BonusTiberium;
statics.PoiTypeToPoiRankingTypeMap[ClientLib.Base.EPOIType.CrystalBonus] = ClientLib.Data.Ranking.ERankingType.BonusCrystal;
statics.PoiTypeToPoiRankingTypeMap[ClientLib.Base.EPOIType.PowerBonus] = ClientLib.Data.Ranking.ERankingType.BonusPower;
statics.PoiTypeToPoiRankingTypeMap[ClientLib.Base.EPOIType.InfanteryBonus] = ClientLib.Data.Ranking.ERankingType.BonusInfantry;
statics.PoiTypeToPoiRankingTypeMap[ClientLib.Base.EPOIType.VehicleBonus] = ClientLib.Data.Ranking.ERankingType.BonusVehicles;
statics.PoiTypeToPoiRankingTypeMap[ClientLib.Base.EPOIType.AirBonus] = ClientLib.Data.Ranking.ERankingType.BonusAircraft;
statics.PoiTypeToPoiRankingTypeMap[ClientLib.Base.EPOIType.DefenseBonus] = ClientLib.Data.Ranking.ERankingType.BonusDefense;
statics.PoiRankingTypeToSortColumnMap[ClientLib.Data.Ranking.ERankingType.BonusTiberium] = ClientLib.Data.Ranking.ESortColumn.TiberiumScore;
statics.PoiRankingTypeToSortColumnMap[ClientLib.Data.Ranking.ERankingType.BonusCrystal] = ClientLib.Data.Ranking.ESortColumn.CrystalScore;
statics.PoiRankingTypeToSortColumnMap[ClientLib.Data.Ranking.ERankingType.BonusPower] = ClientLib.Data.Ranking.ESortColumn.PowerScore;
statics.PoiRankingTypeToSortColumnMap[ClientLib.Data.Ranking.ERankingType.BonusInfantry] = ClientLib.Data.Ranking.ESortColumn.InfantryScore;
statics.PoiRankingTypeToSortColumnMap[ClientLib.Data.Ranking.ERankingType.BonusVehicles] = ClientLib.Data.Ranking.ESortColumn.VehicleScore;
statics.PoiRankingTypeToSortColumnMap[ClientLib.Data.Ranking.ERankingType.BonusAircraft] = ClientLib.Data.Ranking.ESortColumn.AircraftScore;
statics.PoiRankingTypeToSortColumnMap[ClientLib.Data.Ranking.ERankingType.BonusDefense] = ClientLib.Data.Ranking.ESortColumn.DefenseScore;
},
members: {
rankingBonusDataCache: {},
container: null,
titleLabel: null,
amountLabel: null,
ownedPoiCount: 0,
initialize: function() {
this.initializeHacks();
this.container = new qx.ui.container.Composite(new qx.ui.layout.HBox(4)).set({
textColor: 'text-region-tooltip',
marginRight: 10
});
this.container.add(this.titleLabel = new qx.ui.basic.Label());
this.container.add(this.amountLabel = new qx.ui.basic.Label());
var poiStatusInfo = webfrontend.gui.region.RegionPointOfInterestStatusInfo.getInstance();
poiStatusInfo.getChildren()[0].addAt(this.container, 4);
poiStatusInfo.addListener('appear', this.onStatusInfoAppear, this);
phe.cnc.Util.attachNetEvent(ClientLib.Data.MainData.GetInstance().get_Alliance(), 'Change', ClientLib.Data.AllianceChange, this, this.onAllianceChange);
this.onAllianceChange();
},
initializeHacks: function() {
if (typeof webfrontend.gui.region.RegionPointOfInterestStatusInfo.prototype.getObject !== 'function') {
var source = webfrontend.gui.region.RegionPointOfInterestStatusInfo.prototype.setObject.toString();
var objectMemberName = source.match(/^function \(([A-Za-z]+)\)\{this\.([A-Za-z_]+)=\1;/)[2];
/**
* @returns {ClientLib.Vis.Region.RegionPointOfInterest}
*/
webfrontend.gui.region.RegionPointOfInterestStatusInfo.prototype.getObject = function() {
return this[objectMemberName];
};
}
},
onAllianceChange: function() {
var alliance = ClientLib.Data.MainData.GetInstance().get_Alliance();
var poiCount = alliance.get_Exists() ? alliance.get_OwnedPOIs().length : 0;
if (poiCount !== this.ownedPoiCount) {
this.ownedPoiCount = poiCount;
this.rankingBonusDataCache = {};
}
},
/**
* @param {qx.event.type.Event} event
*/
onStatusInfoAppear: function(event) {
var visObject = event.getTarget().getObject();
var allianceId = ClientLib.Data.MainData.GetInstance().get_Alliance().get_Id();
if (allianceId > 0 && visObject.get_Type() !== ClientLib.Data.WorldSector.WorldObjectPointOfInterest.EPOIType.TunnelExit) {
var selectedPoiScore = ClientLib.Base.PointOfInterestTypes.GetScoreByLevel(visObject.get_Level());
var poiType = ClientLib.Base.PointOfInterestTypes.GetPOITypeFromWorldPOIType(visObject.get_Type());
var poiRankScore = ClientLib.Data.MainData.GetInstance().get_Alliance().get_POIRankScore()[poiType - ClientLib.Base.EPOIType.RankedTypeBegin];
var allianceRank = poiRankScore.r;
var allianceScore = poiRankScore.s;
var nextAllianceScore = poiRankScore.ns;
var previousAllianceScore = poiRankScore.ps;
var currentTotalBonus = ClientLib.Base.PointOfInterestTypes.GetTotalBonusByType(poiType, allianceRank, allianceScore);
var gainOrLoss = null;
if (visObject.get_OwnerAllianceId() === allianceId) {
this.titleLabel.setValue('Real loss:');
if (previousAllianceScore <= 0) {
// No rank multiplier; no loss by rank
gainOrLoss = currentTotalBonus - ClientLib.Base.PointOfInterestTypes.GetTotalBonusByType(poiType, allianceRank, allianceScore - selectedPoiScore);
}
else if (allianceScore - selectedPoiScore < previousAllianceScore) {
// Falling behind previous alliance; need to use rankings
}
else {
// No loss by rank; if we end up with same score as previous alliance, our rank stays the same and they get same rank
gainOrLoss = currentTotalBonus - ClientLib.Base.PointOfInterestTypes.GetTotalBonusByType(poiType, allianceRank, allianceScore - selectedPoiScore);
}
}
else {
this.titleLabel.setValue('Real gain:');
if (!allianceScore) {
// Zero bonus; need to use rankings
}
else if (nextAllianceScore <= 0 || allianceRank <= 1) {
// Already rank 1; no gain by rank
gainOrLoss = ClientLib.Base.PointOfInterestTypes.GetTotalBonusByType(poiType, allianceRank, allianceScore + selectedPoiScore) - currentTotalBonus;
}
else if (visObject.get_OwnerAllianceId() !== webfrontend.gui.widgets.AllianceLabel.ESpecialNoAllianceName) {
// Current owner of POI will lose score while we gain; need to use rankings
}
else if (allianceScore + selectedPoiScore > nextAllianceScore) {
// Overtaking next alliance; need to use rankings
}
else if (allianceScore + selectedPoiScore < nextAllianceScore) {
// No gain by rank
gainOrLoss = ClientLib.Base.PointOfInterestTypes.GetTotalBonusByType(poiType, allianceRank, allianceScore + selectedPoiScore) - currentTotalBonus;
}
else {
// Same score as next alliance; same rank and same bonus as them
gainOrLoss = ClientLib.Base.PointOfInterestTypes.GetTotalBonusByType(poiType, allianceRank - 1, allianceScore + selectedPoiScore) - currentTotalBonus;
}
}
if (gainOrLoss === null) {
this.amountLabel.setValue('Loading...');
this.fetchAndCalculateBonusWithRankingData(poiType, allianceRank, allianceScore, selectedPoiScore, allianceId, visObject.get_OwnerAllianceId());
}
else {
this.amountLabel.setValue(this.formatGainOrLoss(gainOrLoss, poiType));
}
this.container.setVisibility('visible');
}
else {
this.container.setVisibility('excluded');
}
},
/**
* @param {ClientLib.Base.EPOIType} poiType
* @param {Number} currentRank
* @param {Number} currentScore
* @param {Number} poiScore
* @param {Number} allianceId
* @param {Number} poiOwnerId
*/
fetchAndCalculateBonusWithRankingData: function(poiType, currentRank, currentScore, poiScore, allianceId, poiOwnerId) {
var context = {
poiType: poiType,
currentRank: currentRank,
currentScore: currentScore,
poiScore: poiScore,
allianceId: allianceId,
poiOwnerId: poiOwnerId
};
if (poiType in this.rankingBonusDataCache && this.rankingBonusDataCache[poiType].expire >= Date.now()) {
this.calculateBonus(context, this.rankingBonusDataCache[poiType].results);
}
else {
var lastMultiplierRank = Object.keys(ClientLib.Res.ResMain.GetInstance().GetGamedata().poibmbr).length;
var rankingPoiType = RealPOIBonus.PoiTypeToPoiRankingTypeMap[poiType];
var sortColumn = RealPOIBonus.PoiRankingTypeToSortColumnMap[rankingPoiType];
ClientLib.Net.CommunicationManager.GetInstance().SendSimpleCommand('RankingGetData', {
firstIndex: 0,
lastIndex: lastMultiplierRank,
view: ClientLib.Data.Ranking.EViewType.Alliance,
rankingType: rankingPoiType,
sortColumn: sortColumn,
ascending: true
}, phe.cnc.Util.createEventDelegate(ClientLib.Net.CommandResult, this, this.onRankingGetData), context);
}
},
/**
* @param {Object} context
* @param {Object} results
*/
onRankingGetData: function(context, results) {
if (results === null) {
return;
}
var allianceBonuses = results.a;
// Remove own alliance from list and add missing scores
for (var i = 0; i < allianceBonuses.length; i++) {
if (allianceBonuses[i].a === context.allianceId) {
allianceBonuses.splice(i--, 1);
}
else if (allianceBonuses[i].pois === undefined) {
allianceBonuses[i].pois = 0;
}
}
this.rankingBonusDataCache[context.poiType] = {
expire: Date.now() + 600000,
results: allianceBonuses
};
this.calculateBonus(context, allianceBonuses);
},
/**
* @param {Object} context
* @param {Array} allianceBonuses
*/
calculateBonus: function(context, allianceBonuses) {
var isGain = context.poiOwnerId !== context.allianceId;
var i;
if (isGain && context.poiOwnerId !== webfrontend.gui.widgets.AllianceLabel.ESpecialNoAllianceName) {
// Subtract POI score from current owner
for (i = 0; i < allianceBonuses.length; i++) {
if (allianceBonuses[i].a === context.poiOwnerId) {
// Array can be safely modified after cloning
allianceBonuses = allianceBonuses.map(this.shallowClone);
allianceBonuses[i].pois -= context.poiScore;
allianceBonuses.sort(function(a, b) {
return b.pois - a.pois;
});
break;
}
}
}
var newAllianceScore = context.currentScore + (isGain ? context.poiScore : -context.poiScore);
for (i = 0; i < allianceBonuses.length; i++) {
if (allianceBonuses[i].pois <= newAllianceScore) {
break;
}
}
var currentTotalBonus = ClientLib.Base.PointOfInterestTypes.GetTotalBonusByType(context.poiType, context.currentRank, context.currentScore);
var newTotalBonus = ClientLib.Base.PointOfInterestTypes.GetTotalBonusByType(context.poiType, i + 1, newAllianceScore);
var gainOrLoss = isGain
? newTotalBonus - currentTotalBonus
: currentTotalBonus - newTotalBonus;
this.amountLabel.setValue(this.formatGainOrLoss(gainOrLoss, context.poiType));
},
/**
* @param {Number} gainOrLoss
* @param {ClientLib.Base.EPOIType} poiType
* @returns {String}
*/
formatGainOrLoss: function(gainOrLoss, poiType) {
switch (poiType) {
case ClientLib.Base.EPOIType.TiberiumBonus:
case ClientLib.Base.EPOIType.CrystalBonus:
case ClientLib.Base.EPOIType.PowerBonus:
return phe.cnc.gui.util.Numbers.formatNumbers(gainOrLoss) + '/h';
case ClientLib.Base.EPOIType.InfanteryBonus:
case ClientLib.Base.EPOIType.VehicleBonus:
case ClientLib.Base.EPOIType.AirBonus:
case ClientLib.Base.EPOIType.DefenseBonus:
return phe.cnc.gui.util.Numbers.formatNumbers(gainOrLoss) + '%';
};
},
/**
* @param {Object} object
* @returns {Object}
*/
shallowClone: function(object) {
var clone = new object.constructor;
for (var key in object) {
if (object.hasOwnProperty(key)) {
clone[key] = object[key];
}
}
return clone;
}
}
});
}
function waitForGame() {
try {
if (typeof qx !== 'undefined' && qx.core.Init.getApplication() && qx.core.Init.getApplication().initDone) {
createRealPOIBonus();
RealPOIBonus.getInstance().initialize();
}
else {
setTimeout(waitForGame, 1000);
}
}
catch (e) {
console.log('RealPOIBonus: ', e.toString());
}
}
setTimeout(waitForGame, 1000);
};
var script = document.createElement('script');
script.innerHTML = '(' + main.toString() + ')();';
script.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(script);
})();
Donate for the site OpenUserJS
Are you sure you want to go to an external site to donate a monetary value?
WARNING: Some countries laws may supersede the payment processors policy such as the GDPR and PayPal. While it is highly appreciated to donate, please check with your countries privacy and identity laws regarding privacy of information first. Use at your utmost discretion.