Index: binaries/data/mods/public/gui/summary/counters.js =================================================================== --- binaries/data/mods/public/gui/summary/counters.js +++ binaries/data/mods/public/gui/summary/counters.js @@ -334,6 +334,12 @@ return { "population": playerState.sequences.populationCount[index] }; } +function calculateIdleTime(playerState, index) +{ + print(playerState.sequences.idleTime[index]+"\n"); + return { "idleTime": playerState.sequences.idleTime[index]/1000 }; +} + function calculateMapExploration(playerState, index) { return { "percent": playerState.sequences.percentMapExplored[index] }; @@ -360,7 +366,7 @@ if (type == "killDeath") return calculateRatio(g_TeamHelperData[team].enemyUnitsKilled[index], g_TeamHelperData[team].unitsLost[index]); - if (type == "bribes" || type == "population") + if (type == "bribes" || type == "population" || type == "idleTime") return summaryArraySum(getPlayerValuesPerTeam(team, index, type, counters, headings)); return { "percent": g_TeamHelperData[team][type][index] }; Index: binaries/data/mods/public/gui/summary/layout.js =================================================================== --- binaries/data/mods/public/gui/summary/layout.js +++ binaries/data/mods/public/gui/summary/layout.js @@ -194,6 +194,7 @@ { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "identifier": "killDeath", "caption": translate("Kill / Death ratio"), "yStart": 16, "width": 100, "format": "DECIMAL2" }, { "identifier": "population", "caption": translate("Population"), "yStart": 16, "width": 100, "hideInSummary": true }, + { "identifier": "idleTime", "caption": translate("Idle time"), "yStart": 16, "width": 100, "hideInSummary": true, "format": "DURATION_SHORT"}, { "identifier": "mapControlPeak", "caption": translate("Map control (peak)"), "yStart": 16, "width": 100, "format": "PERCENTAGE" }, { "identifier": "mapControl", "caption": translate("Map control (finish)"), "yStart": 16, "width": 100, "format": "PERCENTAGE" }, { "identifier": "mapExploration", "caption": translate("Map exploration"), "yStart": 16, "width": 100, "format": "PERCENTAGE" }, @@ -215,6 +216,7 @@ "counters": [ { "width": 100, "fn": calculateKillDeathRatio, "verticalOffset": 12 }, { "width": 100, "fn": calculatePopulationCount, "verticalOffset": 12, "hideInSummary": true }, + { "width": 100, "fn": calculateIdleTime, "verticalOffset": 12, "hideInSummary": true }, { "width": 100, "fn": calculateMapPeakControl, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapFinalControl, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapExploration, "verticalOffset": 12 }, Index: binaries/data/mods/public/gui/summary/summary.js =================================================================== --- binaries/data/mods/public/gui/summary/summary.js +++ binaries/data/mods/public/gui/summary/summary.js @@ -94,6 +94,11 @@ "caption": translate("Received"), "postfix": "" }, + "idleTime": { + "color": g_TypeColors.red, + "caption": translate("Idle time"), + "postfix": "s" + }, "population": { "color": g_TypeColors.red, "caption": translate("Population"), Index: binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/GuiInterface.js +++ binaries/data/mods/public/simulation/components/GuiInterface.js @@ -1875,6 +1875,20 @@ return cmpRangeManager.GetEntitiesByPlayer(player).some(unit => this.IdleUnitFilter(unit, data.idleClasses, data.excludeUnits).idle); }; +/** + * Count the number of idle units. + * + * @param data.idleClasses Array of class names to include. + * @param data.excludeUnits Array of units to exclude. + * + * Returns the number of idle units + */ +GuiInterface.prototype.CountIdleUnits = function(player, data) +{ + let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + return cmpRangeManager.GetEntitiesByPlayer(player).filter(unit => this.IdleUnitFilter(unit, data.idleClasses, data.excludeUnits).idle).length; +}; + /** * Whether to filter an idle unit * Index: binaries/data/mods/public/simulation/components/StatisticsTracker.js =================================================================== --- binaries/data/mods/public/simulation/components/StatisticsTracker.js +++ binaries/data/mods/public/simulation/components/StatisticsTracker.js @@ -24,6 +24,11 @@ */ StatisticsTracker.prototype.UpdateSequenceInterval = 30 * 1000; +/** + * This number specifies the time in milliseconds between consecutive times the idle units should be counted. + */ +StatisticsTracker.prototype.UpdateIdleUnitsInterval = 1 * 1000; + StatisticsTracker.prototype.Init = function() { this.unitsClasses = this.template.UnitClasses._string.split(/\s+/); @@ -87,9 +92,16 @@ this.successfulBribes = 0; this.failedBribes = 0; + this.prevIdleUnits = 0; + this.idleTime = 0; + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + this.prevIdleUpdate = cmpTimer.GetTime(); this.updateTimer = cmpTimer.SetInterval( this.entity, IID_StatisticsTracker, "UpdateSequences", 0, this.UpdateSequenceInterval); + + this.idleUnitsTimer = cmpTimer.SetInterval( + this.entity, IID_StatisticsTracker, "UpdateIdleUnits", 0, this.UpdateIdleUnitsInterval); }; StatisticsTracker.prototype.OnGlobalInitGame = function() @@ -149,7 +161,8 @@ "peakPercentMapControlled": this.peakPercentMapControlled, "teamPeakPercentMapControlled": this.teamPeakPercentMapControlled, "successfulBribes": this.successfulBribes, - "failedBribes": this.failedBribes + "failedBribes": this.failedBribes, + "idleTime": this.idleTime }; }; @@ -522,6 +535,28 @@ this.teamPeakPercentMapControlled = Math.max(this.teamPeakPercentMapControlled, this.GetTeamPercentMapControlled()); }; +StatisticsTracker.prototype.UpdateIdleUnits = function() +{ + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + let time = cmpTimer.GetTime(); + let passed = time - this.prevIdleUpdate; + this.prevIdleUpdate = time; + + let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); + let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); + let idleUnits = cmpGUIInterface.CountIdleUnits(cmpPlayer.GetPlayerID(), { + "idleClasses": ["FemaleCitizen", "Trader", "FishingBoat", "Citizen"], + "excludeUnits": [] + }); + + let stayedIdle = Math.min(this.prevIdleUnits, idleUnits); + let changedIdle = Math.max(this.prevIdleUnits, idleUnits) - stayedIdle; + + this.prevIdleUnits = idleUnits; + this.idleTime += passed * (stayedIdle + changedIdle / 2); + print(this.entity + ": " + this.idleTime + "\n"); +} + /** * Adds the values of fromData to the end of the arrays of toData. * If toData misses the needed array, one will be created.