Index: ps/trunk/binaries/data/mods/public/gui/summary/counters.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/counters.js (revision 19583) +++ ps/trunk/binaries/data/mods/public/gui/summary/counters.js (revision 19584) @@ -1,475 +1,480 @@ var g_TeamHelperData = []; function resetDataHelpers() { g_TeamHelperData = []; } function calculatePercent(divident, divisor) { return { "percent": divisor ? Math.floor(100 * divident / divisor) : 0 }; } function calculateRatio(divident, divisor) { return divident ? +((divident / divisor).toFixed(2)) : 0; } function formatSummaryValue(values) { if (typeof values != "object") return values === Infinity ? g_InfinitySymbol : values; let ret = ""; for (let type in values) ret += (g_SummaryTypes[type].color ? '[color="' + g_SummaryTypes[type].color + '"]' + values[type] + '[/color]' : values[type]) + g_SummaryTypes[type].postfix; return ret; } /** * Remove color tags, whitespace, + and % to read numerical values from the GUI objects. * Remove \n only when removeLineFeed == true * TODO: access the data directly instead of this ugly hack. */ function cleanGUICaption(team, player, counter, removeLineFeed = true) { let caption = Engine.GetGUIObjectByName("valueDataTeam[" + team + "][" + player + "][" + counter + "]").caption; if (removeLineFeed) return caption.replace(/\[([\w\' \\\"\/\=]*)\]|\+|\%|\s/g, ""); else return caption.replace(/\[([\w\' \\\"\/\=]*)\]|[\t\r \f]/g, ""); } function updateCountersPlayer(playerState, counters, headings, idGUI) { let index = playerState.sequences.time.length - 1; for (let w in counters) { let fn = counters[w].fn; Engine.GetGUIObjectByName(idGUI + "[" + w + "]").caption = formatSummaryValue(fn && fn(playerState, index, headings[+w+1].identifier)); } } /** * Add two arrays element-wise. So addArray([1, 2], [7, 42]) will result in [8, 44]. * * @param {Array} array1 - first summand array. * @param {Array} array2 - second summand array. * @returns {Array} the element-wise sum of array1 and array2. */ function addArray(array1, array2) { array1 = array1.map((value, index) => value + array2[index]); } // Updates g_TeamHelperData by appending some data from playerState function calculateTeamCounters(playerState) { if (!g_TeamHelperData[playerState.team]) { g_TeamHelperData[playerState.team] = {}; for (let value of ["food", "vegetarianFood", "femaleCitizen", "worker", "enemyUnitsKilled", "unitsLost", "percentMapControlled", "peakPercentMapControlled", "percentMapExplored", "totalBought", "totalSold"]) g_TeamHelperData[playerState.team][value] = new Array(playerState.sequences.time.length).fill(0); } addArray(g_TeamHelperData[playerState.team].food, playerState.sequences.resourcesGathered.food); addArray(g_TeamHelperData[playerState.team].vegetarianFood, playerState.sequences.resourcesGathered.vegetarianFood); addArray(g_TeamHelperData[playerState.team].femaleCitizen, playerState.sequences.unitsTrained.FemaleCitizen); addArray(g_TeamHelperData[playerState.team].worker, playerState.sequences.unitsTrained.Worker); addArray(g_TeamHelperData[playerState.team].enemyUnitsKilled, playerState.sequences.enemyUnitsKilled.total); addArray(g_TeamHelperData[playerState.team].unitsLost, playerState.sequences.unitsLost.total); g_TeamHelperData[playerState.team].percentMapControlled = playerState.sequences.teamPercentMapControlled; g_TeamHelperData[playerState.team].peakPercentMapControlled = playerState.sequences.teamPeakPercentMapControlled; g_TeamHelperData[playerState.team].percentMapExplored = playerState.sequences.teamPercentMapExplored; for (let type in playerState.sequences.resourcesBought) addArray(g_TeamHelperData[playerState.team].totalBought, playerState.sequences.resourcesBought[type]); for (let type in playerState.sequences.resourcesSold) addArray(g_TeamHelperData[playerState.team].totalSold, playerState.sequences.resourcesSold[type]); } function calculateEconomyScore(playerState, index) { let total = 0; - for (let type in playerState.sequences.resourcesGathered) + for (let type of g_ResourceData.GetCodes()) total += playerState.sequences.resourcesGathered[type][index]; + // Subtract costs for sheep/goats/pigs to get the net food gain for corralling + total -= playerState.sequences.domesticUnitsTrainedValue[index]; + + total += playerState.sequences.tradeIncome[index]; return Math.round(total / 10); } function calculateMilitaryScore(playerState, index) { return Math.round((playerState.sequences.enemyUnitsKilledValue[index] + + playerState.sequences.unitsCapturedValue[index] + playerState.sequences.enemyBuildingsDestroyedValue[index] + playerState.sequences.buildingsCapturedValue[index]) / 10); } function calculateExplorationScore(playerState, index) { return playerState.sequences.percentMapExplored[index] * 10; } function calculateScoreTotal(playerState, index) { return calculateEconomyScore(playerState, index) + calculateMilitaryScore(playerState, index) + calculateExplorationScore(playerState, index); } function calculateScoreTeam(counters, index) { for (let t in g_Teams) { if (t == -1) continue; let teamTotalScore = 0; for (let w in counters) { let total = 0; if (w == 2) // Team exploration score (not additive) total = g_TeamHelperData[t].percentMapExplored[index] * 10; else for (let p = 0; p < g_Teams[t]; ++p) total += +Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + p + "][" + w + "]").caption; if (w < 3) { teamTotalScore += total; Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = total; } else Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotalScore; } } } function calculateBuildings(playerState, index, type) { return { "constructed": playerState.sequences.buildingsConstructed[type][index], "destroyed": playerState.sequences.enemyBuildingsDestroyed[type][index], "captured": playerState.sequences.buildingsCaptured[type][index], "lost": playerState.sequences.buildingsLost[type][index] }; } function calculateBuildingsTeam(counters, index) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { let total = { "constructed" : 0, "destroyed" : 0, "captured" : 0, "lost" : 0 }; for (let p = 0; p < g_Teams[t]; ++p) { let splitCaption = cleanGUICaption(t, p, w, false).split("\n"); let first = splitCaption[0].split("/"); let second = splitCaption[1].split("/"); total.constructed += +first[0]; total.destroyed += +first[1]; total.captured += +second[0]; total.lost += +second[1]; } Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(total); } } } function calculateUnitsTeam(counters, index) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { let total = { "trained": 0, "killed": 0, "captured" : 0, "lost": 0 }; for (let p = 0; p < g_Teams[t]; ++p) { let splitCaption = cleanGUICaption(t, p, w, false).split("\n"); let first = splitCaption[0].split("/"); total.trained += +first[0]; total.killed += +first[1]; if (w == 0 || w == 6) { let second = splitCaption[1].split("/"); total.captured += +second[0]; total.lost += +second[1]; } else total.lost += +splitCaption[1]; } if (w != 0 && w != 6) delete total.captured; Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(total); } } } function calculateUnitsWithCaptured(playerState, index, type) { return { "trained": playerState.sequences.unitsTrained[type][index], "killed": playerState.sequences.enemyUnitsKilled[type][index], "captured": playerState.sequences.unitsCaptured[type][index], "lost": playerState.sequences.unitsLost[type][index] }; } function calculateUnits(playerState, index, type) { return { "trained": playerState.sequences.unitsTrained[type][index], "killed": playerState.sequences.enemyUnitsKilled[type][index], "lost": playerState.sequences.unitsLost[type][index] }; } function calculateResources(playerState, index, type) { return { "gathered": playerState.sequences.resourcesGathered[type][index], "used": playerState.sequences.resourcesUsed[type][index] - playerState.sequences.resourcesSold[type][index] }; } function calculateTotalResources(playerState, index) { let totalGathered = 0; let totalUsed = 0; for (let type of g_ResourceData.GetCodes()) { totalGathered += playerState.sequences.resourcesGathered[type][index]; totalUsed += playerState.sequences.resourcesUsed[type][index] - playerState.sequences.resourcesSold[type][index]; } return { "gathered": totalGathered, "used": totalUsed }; } function calculateTreasureCollected(playerState, index) { return playerState.sequences.treasuresCollected[index]; } function calculateLootCollected(playerState, index) { return playerState.sequences.lootCollected[index]; } function calculateTributeSent(playerState, index) { return { "sent": playerState.sequences.tributesSent[index], "received": playerState.sequences.tributesReceived[index] }; } function calculateResourcesTeam(counters, index) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { let total = { "income": 0, "outcome": 0 }; for (let p = 0; p < g_Teams[t]; ++p) { let caption = cleanGUICaption(t, p, w); if (w >= 6) total.income += +caption; else { let splitCaption = caption.split("/"); total.income += +splitCaption[0]; total.outcome += +splitCaption[1]; } } let teamTotal; if (w >= 6) teamTotal = total.income; else if (w == 5) teamTotal = { "sent": total.income, "received": total.outcome }; else teamTotal = { "gathered": total.income, "used": total.outcome }; Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(teamTotal); } } } function calculateResourceExchanged(playerState, index, type) { return { "bought": playerState.sequences.resourcesBought[type][index], "sold": playerState.sequences.resourcesSold[type][index] }; } function calculateBarterEfficiency(playerState, index) { let totalBought = 0; let totalSold = 0; for (let type in playerState.sequences.resourcesBought) totalBought += playerState.sequences.resourcesBought[type][index]; for (let type in playerState.sequences.resourcesSold) totalSold += playerState.sequences.resourcesSold[type][index]; return calculatePercent(totalBought, totalSold); } function calculateTradeIncome(playerState, index) { return playerState.sequences.tradeIncome[index]; } function calculateMarketTeam(counters, index) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { let total = { "income": 0, "outcome": 0 }; for (let p = 0; p < g_Teams[t]; ++p) { let caption = cleanGUICaption(t, p, w); if (w >= 4) total.income += +caption; else { let splitCaption = caption.split("/"); total.income += +splitCaption[0]; total.outcome += +splitCaption[1]; } } let teamTotal; if (w == 4) teamTotal = calculatePercent(g_TeamHelperData[t].totalBought[index], g_TeamHelperData[t].totalSold[index]); else if (w > 4) teamTotal = total.income; else teamTotal = total; Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(teamTotal); } } } function calculateVegetarianRatio(playerState, index) { return calculatePercent( playerState.sequences.resourcesGathered.vegetarianFood[index], playerState.sequences.resourcesGathered.food[index]); } function calculateFeminization(playerState, index) { return calculatePercent( playerState.sequences.unitsTrained.FemaleCitizen[index], playerState.sequences.unitsTrained.Worker[index]); } function calculateKillDeathRatio(playerState, index) { return calculateRatio( playerState.sequences.enemyUnitsKilled.total[index], playerState.sequences.unitsLost.total[index]); } function calculateMapExploration(playerState, index) { return { "percent": playerState.sequences.percentMapExplored[index] }; } function calculateMapFinalControl(playerState, index) { return { "percent": playerState.sequences.percentMapControlled[index] }; } function calculateMapPeakControl(playerState, index) { return { "percent": playerState.sequences.peakPercentMapControlled[index] }; } function calculateMiscellaneousTeam(counters, index) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { let teamTotal; if (w == 0) teamTotal = calculatePercent(g_TeamHelperData[t].vegetarianFood[index], g_TeamHelperData[t].food[index]); else if (w == 1) teamTotal = calculatePercent(g_TeamHelperData[t].femaleCitizen[index], g_TeamHelperData[t].worker[index]); else if (w == 2) teamTotal = calculateRatio(g_TeamHelperData[t].enemyUnitsKilled[index], g_TeamHelperData[t].unitsLost[index]); else if (w == 3) teamTotal = { "percent": g_TeamHelperData[t].percentMapExplored[index] }; else if (w == 4) teamTotal = { "percent": g_TeamHelperData[t].peakPercentMapControlled[index] }; else if (w == 5) teamTotal = { "percent": g_TeamHelperData[t].percentMapControlled[index] }; Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(teamTotal); } } } Index: ps/trunk/binaries/data/mods/public/simulation/components/StatisticsTracker.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/StatisticsTracker.js (revision 19583) +++ ps/trunk/binaries/data/mods/public/simulation/components/StatisticsTracker.js (revision 19584) @@ -1,552 +1,562 @@ function StatisticsTracker() {} const g_UpdateSequenceInterval = 30 * 1000; StatisticsTracker.prototype.Schema = ""; StatisticsTracker.prototype.Init = function() { this.unitsClasses = [ "Infantry", "Worker", "FemaleCitizen", "Cavalry", "Champion", "Hero", "Siege", "Ship", "Trader" ]; this.unitsTrained = { "Infantry": 0, "Worker": 0, "FemaleCitizen": 0, "Cavalry": 0, "Champion": 0, "Hero": 0, "Siege": 0, "Ship": 0, "Trader": 0, "total": 0 }; + this.domesticUnitsTrainedValue = 0; this.unitsLost = { "Infantry": 0, "Worker": 0, "FemaleCitizen": 0, "Cavalry": 0, "Champion": 0, "Hero": 0, "Siege": 0, "Ship": 0, "Trader": 0, "total": 0 }; this.unitsLostValue = 0; this.enemyUnitsKilled = { "Infantry": 0, "Worker": 0, "FemaleCitizen": 0, "Cavalry": 0, "Champion": 0, "Hero": 0, "Siege": 0, "Ship": 0, "Trader": 0, "total": 0 }; this.enemyUnitsKilledValue = 0; this.unitsCaptured = { "Infantry": 0, "Worker": 0, "FemaleCitizen": 0, "Cavalry": 0, "Champion": 0, "Hero": 0, "Siege": 0, "Ship": 0, "Trader": 0, "total": 0 }; this.unitsCapturedValue = 0; this.buildingsClasses = [ "House", "Economic", "Outpost", "Military", "Fortress", "CivCentre", "Wonder" ]; this.buildingsConstructed = { "House": 0, "Economic": 0, "Outpost": 0, "Military": 0, "Fortress": 0, "CivCentre": 0, "Wonder": 0, "total": 0 }; this.buildingsLost = { "House": 0, "Economic": 0, "Outpost": 0, "Military": 0, "Fortress": 0, "CivCentre": 0, "Wonder": 0, "total": 0 }; this.buildingsLostValue = 0; this.enemyBuildingsDestroyed = { "House": 0, "Economic": 0, "Outpost": 0, "Military": 0, "Fortress": 0, "CivCentre": 0, "Wonder": 0, "total": 0 }; this.enemyBuildingsDestroyedValue = 0; this.buildingsCaptured = { "House": 0, "Economic": 0, "Outpost": 0, "Military": 0, "Fortress": 0, "CivCentre": 0, "Wonder": 0, "total": 0 }; this.buildingsCapturedValue = 0; this.resourcesGathered = { "vegetarianFood": 0 }; this.resourcesUsed = {}; this.resourcesSold = {}; this.resourcesBought = {}; for (let res of Resources.GetCodes()) { this.resourcesGathered[res] = 0; this.resourcesUsed[res] = 0; this.resourcesSold[res] = 0; this.resourcesBought[res] = 0; } this.tributesSent = 0; this.tributesReceived = 0; this.tradeIncome = 0; this.treasuresCollected = 0; this.lootCollected = 0; this.peakPercentMapControlled = 0; this.teamPeakPercentMapControlled = 0; let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); this.updateTimer = cmpTimer.SetInterval( this.entity, IID_StatisticsTracker, "UpdateSequences", 0, g_UpdateSequenceInterval); }; /** * Returns a subset of statistics that will be added to the simulation state, * thus called each turn. Basic statistics should not contain data that would * be expensive to compute. * * Note: as of now, nothing in the game needs that, but some AIs developed by * modders need it in the API. */ StatisticsTracker.prototype.GetBasicStatistics = function() { return { "resourcesGathered": this.resourcesGathered, "percentMapExplored": this.GetPercentMapExplored() }; }; StatisticsTracker.prototype.GetStatistics = function() { return { "unitsTrained": this.unitsTrained, + "domesticUnitsTrainedValue": this.domesticUnitsTrainedValue, "unitsLost": this.unitsLost, "unitsLostValue": this.unitsLostValue, "enemyUnitsKilled": this.enemyUnitsKilled, "enemyUnitsKilledValue": this.enemyUnitsKilledValue, "unitsCaptured": this.unitsCaptured, "unitsCapturedValue": this.unitsCapturedValue, "buildingsConstructed": this.buildingsConstructed, "buildingsLost": this.buildingsLost, "buildingsLostValue": this.buildingsLostValue, "enemyBuildingsDestroyed": this.enemyBuildingsDestroyed, "enemyBuildingsDestroyedValue": this.enemyBuildingsDestroyedValue, "buildingsCaptured": this.buildingsCaptured, "buildingsCapturedValue": this.buildingsCapturedValue, "resourcesGathered": this.resourcesGathered, "resourcesUsed": this.resourcesUsed, "resourcesSold": this.resourcesSold, "resourcesBought": this.resourcesBought, "tributesSent": this.tributesSent, "tributesReceived": this.tributesReceived, "tradeIncome": this.tradeIncome, "treasuresCollected": this.treasuresCollected, "lootCollected": this.lootCollected, "percentMapExplored": this.GetPercentMapExplored(), "teamPercentMapExplored": this.GetTeamPercentMapExplored(), "percentMapControlled": this.GetPercentMapControlled(), "teamPercentMapControlled": this.GetTeamPercentMapControlled(), "peakPercentMapControlled": this.peakPercentMapControlled, "teamPeakPercentMapControlled": this.teamPeakPercentMapControlled }; }; StatisticsTracker.prototype.GetSequences = function() { if (!this.sequences) return {}; let ret = clone(this.sequences); let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); ret.time.push(cmpTimer.GetTime() / 1000); this.PushValue(this.GetStatistics(), ret); return ret; }; /** * Increments counter associated with certain entity/counter and type of given entity. * @param cmpIdentity - the entity identity component. * @param counter - the name of the counter to increment (e.g. "unitsTrained"). * @param type - the type of the counter (e.g. "workers"). */ StatisticsTracker.prototype.CounterIncrement = function(cmpIdentity, counter, type) { var classes = cmpIdentity.GetClassesList(); if (!classes) return; if (classes.indexOf(type) != -1) ++this[counter][type]; }; /** * Counts the total number of units trained as well as an individual count for * each unit type. Based on templates. */ StatisticsTracker.prototype.IncreaseTrainedUnitsCounter = function(trainedUnit) { - var cmpUnitEntityIdentity = Engine.QueryInterface(trainedUnit, IID_Identity); + let cmpUnitEntityIdentity = Engine.QueryInterface(trainedUnit, IID_Identity); if (!cmpUnitEntityIdentity) return; + let cmpCost = Engine.QueryInterface(trainedUnit, IID_Cost); + let costs = cmpCost && cmpCost.GetResourceCosts(); + for (let type of this.unitsClasses) this.CounterIncrement(cmpUnitEntityIdentity, "unitsTrained", type); ++this.unitsTrained.total; + + if (cmpUnitEntityIdentity.HasClass("Domestic") && costs) + for (let type in costs) + this.domesticUnitsTrainedValue += costs[type]; + }; /** * Counts the total number of buildings constructed as well as an individual count for * each building type. Based on templates. */ StatisticsTracker.prototype.IncreaseConstructedBuildingsCounter = function(constructedBuilding) { var cmpBuildingEntityIdentity = Engine.QueryInterface(constructedBuilding, IID_Identity); if (!cmpBuildingEntityIdentity) return; for (let type of this.buildingsClasses) this.CounterIncrement(cmpBuildingEntityIdentity, "buildingsConstructed", type); ++this.buildingsConstructed.total; }; StatisticsTracker.prototype.KilledEntity = function(targetEntity) { var cmpTargetEntityIdentity = Engine.QueryInterface(targetEntity, IID_Identity); if (!cmpTargetEntityIdentity) return; var cmpCost = Engine.QueryInterface(targetEntity, IID_Cost); var costs = cmpCost && cmpCost.GetResourceCosts(); var cmpTargetOwnership = Engine.QueryInterface(targetEntity, IID_Ownership); // Ignore gaia if (cmpTargetOwnership.GetOwner() == 0) return; if (cmpTargetEntityIdentity.HasClass("Unit") && !cmpTargetEntityIdentity.HasClass("Domestic")) { for (let type of this.unitsClasses) this.CounterIncrement(cmpTargetEntityIdentity, "enemyUnitsKilled", type); ++this.enemyUnitsKilled.total; if (costs) for (let type in costs) this.enemyUnitsKilledValue += costs[type]; } let cmpFoundation = Engine.QueryInterface(targetEntity, IID_Foundation); if (cmpTargetEntityIdentity.HasClass("Structure") && !cmpFoundation) { for (let type of this.buildingsClasses) this.CounterIncrement(cmpTargetEntityIdentity, "enemyBuildingsDestroyed", type); ++this.enemyBuildingsDestroyed.total; if (costs) for (let type in costs) this.enemyBuildingsDestroyedValue += costs[type]; } }; StatisticsTracker.prototype.LostEntity = function(lostEntity) { var cmpLostEntityIdentity = Engine.QueryInterface(lostEntity, IID_Identity); if (!cmpLostEntityIdentity) return; var cmpCost = Engine.QueryInterface(lostEntity, IID_Cost); var costs = cmpCost && cmpCost.GetResourceCosts(); if (cmpLostEntityIdentity.HasClass("Unit") && !cmpLostEntityIdentity.HasClass("Domestic")) { for (let type of this.unitsClasses) this.CounterIncrement(cmpLostEntityIdentity, "unitsLost", type); ++this.unitsLost.total; if (costs) for (let type in costs) this.unitsLostValue += costs[type]; } let cmpFoundation = Engine.QueryInterface(lostEntity, IID_Foundation); if (cmpLostEntityIdentity.HasClass("Structure") && !cmpFoundation) { for (let type of this.buildingsClasses) this.CounterIncrement(cmpLostEntityIdentity, "buildingsLost", type); ++this.buildingsLost.total; if (costs) for (let type in costs) this.buildingsLostValue += costs[type]; } }; StatisticsTracker.prototype.CapturedEntity = function(capturedEntity) { let cmpCapturedEntityIdentity = Engine.QueryInterface(capturedEntity, IID_Identity); if (!cmpCapturedEntityIdentity) return; let cmpCost = Engine.QueryInterface(capturedEntity, IID_Cost); let costs = cmpCost && cmpCost.GetResourceCosts(); if (cmpCapturedEntityIdentity.HasClass("Unit")) { for (let type of this.unitsClasses) this.CounterIncrement(cmpCapturedEntityIdentity, "unitsCaptured", type); ++this.unitsCaptured.total; if (costs) for (let type in costs) this.unitsCapturedValue += costs[type]; } if (cmpCapturedEntityIdentity.HasClass("Structure")) { for (let type of this.buildingsClasses) this.CounterIncrement(cmpCapturedEntityIdentity, "buildingsCaptured", type); ++this.buildingsCaptured.total; if (costs) for (let type in costs) this.buildingsCapturedValue += costs[type]; } }; /** * @param {string} type - generic type of resource. * @param {number} amount - amount of resource, whick should be added. * @param {string} specificType - specific type of resource. */ StatisticsTracker.prototype.IncreaseResourceGatheredCounter = function(type, amount, specificType) { this.resourcesGathered[type] += amount; if (type == "food" && (specificType == "fruit" || specificType == "grain")) this.resourcesGathered.vegetarianFood += amount; }; /** * @param {string} type - generic type of resource. * @param {number} amount - amount of resource, which should be added. */ StatisticsTracker.prototype.IncreaseResourceUsedCounter = function(type, amount) { this.resourcesUsed[type] += amount; }; StatisticsTracker.prototype.IncreaseTreasuresCollectedCounter = function() { ++this.treasuresCollected; }; StatisticsTracker.prototype.IncreaseLootCollectedCounter = function(amount) { for (let type in amount) this.lootCollected += amount[type]; }; StatisticsTracker.prototype.IncreaseResourcesSoldCounter = function(type, amount) { this.resourcesSold[type] += amount; }; StatisticsTracker.prototype.IncreaseResourcesBoughtCounter = function(type, amount) { this.resourcesBought[type] += amount; }; StatisticsTracker.prototype.IncreaseTributesSentCounter = function(amount) { this.tributesSent += amount; }; StatisticsTracker.prototype.IncreaseTributesReceivedCounter = function(amount) { this.tributesReceived += amount; }; StatisticsTracker.prototype.IncreaseTradeIncomeCounter = function(amount) { this.tradeIncome += amount; }; StatisticsTracker.prototype.GetPercentMapExplored = function() { var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); return cmpRangeManager.GetPercentMapExplored(cmpPlayer.GetPlayerID()); }; /** * Note: cmpRangeManager.GetUnionPercentMapExplored computes statistics from scratch! * As a consequence, this function should not be called too often. */ StatisticsTracker.prototype.GetTeamPercentMapExplored = function() { var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); if (!cmpPlayer) return 0; var team = cmpPlayer.GetTeam(); // If teams are not locked, this statistic won't be displayed, so don't bother computing if (team == -1 || !cmpPlayer.GetLockTeams()) return cmpRangeManager.GetPercentMapExplored(cmpPlayer.GetPlayerID()); var teamPlayers = []; for (var i = 1; i < cmpPlayerManager.GetNumPlayers(); ++i) { let cmpOtherPlayer = QueryPlayerIDInterface(i); if (cmpOtherPlayer && cmpOtherPlayer.GetTeam() == team) teamPlayers.push(i); } return cmpRangeManager.GetUnionPercentMapExplored(teamPlayers); }; StatisticsTracker.prototype.GetPercentMapControlled = function() { var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager); if (!cmpPlayer || !cmpTerritoryManager) return 0; return cmpTerritoryManager.GetTerritoryPercentage(cmpPlayer.GetPlayerID()); }; StatisticsTracker.prototype.GetTeamPercentMapControlled = function() { var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager); if (!cmpPlayer || !cmpTerritoryManager) return 0; var team = cmpPlayer.GetTeam(); if (team == -1 || !cmpPlayer.GetLockTeams()) return cmpTerritoryManager.GetTerritoryPercentage(cmpPlayer.GetPlayerID()); var teamPercent = 0; for (let i = 1; i < cmpPlayerManager.GetNumPlayers(); ++i) { let cmpOtherPlayer = QueryPlayerIDInterface(i); if (cmpOtherPlayer && cmpOtherPlayer.GetTeam() == team) teamPercent += cmpTerritoryManager.GetTerritoryPercentage(i); } return teamPercent; }; StatisticsTracker.prototype.OnTerritoriesChanged = function(msg) { var newPercent = this.GetPercentMapControlled(); if (newPercent > this.peakPercentMapControlled) this.peakPercentMapControlled = newPercent; newPercent = this.GetTeamPercentMapControlled(); if (newPercent > this.teamPeakPercentMapControlled) this.teamPeakPercentMapControlled = newPercent; }; /** * Adds the values of fromData to the end of the arrays of toData. * If toData misses the needed array, one will be created. * * @param fromData - an object of values or a value. * @param toData - an object of arrays or an array. **/ StatisticsTracker.prototype.PushValue = function(fromData, toData) { if (typeof fromData == "object") for (let prop in fromData) { if (typeof toData[prop] != "object") toData[prop] = [fromData[prop]]; else this.PushValue(fromData[prop], toData[prop]); } else toData.push(fromData); }; StatisticsTracker.prototype.UpdateSequences = function() { let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); // Don't do this on Init, because GetStatistics doesn't work in this state of the game // This is because the simulation hasn't totally started/initialized and we query some simulation values if (!this.sequences) { this.sequences = clone(this.GetStatistics()); this.sequences.time = []; } this.sequences.time.push(cmpTimer.GetTime() / 1000); this.PushValue(this.GetStatistics(), this.sequences); }; Engine.RegisterComponentType(IID_StatisticsTracker, "StatisticsTracker", StatisticsTracker);