Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js +++ binaries/data/mods/public/gui/session/session.js @@ -1449,6 +1449,16 @@ "resourcesBought" ]; + let misc = [ + "tradeIncome", + "tributesSent", + "tributesReceived", + "treasuresCollected", + "lootCollected", + "feminisation", + "percentMapExplored" + ]; + let playerStatistics = {}; // Unit Stats @@ -1483,19 +1493,13 @@ } playerStatistics.resourcesGathered.vegetarianFood = ""; - playerStatistics.tradeIncome = ""; - // Tribute - playerStatistics.tributesSent = ""; - playerStatistics.tributesReceived = ""; + for (let type of misc) + playerStatistics[type] = ""; + // Total playerStatistics.economyScore = ""; playerStatistics.militaryScore = ""; playerStatistics.totalScore = ""; - // Various - playerStatistics.treasuresCollected = ""; - playerStatistics.lootCollected = ""; - playerStatistics.feminisation = ""; - playerStatistics.percentMapExplored = ""; let mapName = g_GameAttributes.settings.Name; let playerStates = ""; @@ -1508,6 +1512,7 @@ for (let i = 1; i < extendedSimState.players.length; ++i) { let player = extendedSimState.players[i]; + let maxIndex = player.sequences.time.length - 1; playerStates += player.state + ","; playerCivs += player.civ + ","; @@ -1515,31 +1520,28 @@ teamsLocked = teamsLocked && player.teamsLocked; for (let resourcesCounterType of resourcesCounterTypes) for (let resourcesType of resourcesTypes) - playerStatistics[resourcesCounterType][resourcesType] += player.statistics[resourcesCounterType][resourcesType] + ","; - playerStatistics.resourcesGathered.vegetarianFood += player.statistics.resourcesGathered.vegetarianFood + ","; + playerStatistics[resourcesCounterType][resourcesType] += player.sequences[resourcesCounterType][resourcesType][maxIndex] + ","; + playerStatistics.resourcesGathered.vegetarianFood += player.sequences.resourcesGathered.vegetarianFood[maxIndex] + ","; for (let unitCounterType of unitsCountersTypes) for (let unitsClass of unitsClasses) - playerStatistics[unitCounterType][unitsClass] += player.statistics[unitCounterType][unitsClass] + ","; + playerStatistics[unitCounterType][unitsClass] += player.sequences[unitCounterType][unitsClass][maxIndex] + ","; for (let buildingCounterType of buildingsCountersTypes) for (let buildingsClass of buildingsClasses) - playerStatistics[buildingCounterType][buildingsClass] += player.statistics[buildingCounterType][buildingsClass] + ","; + playerStatistics[buildingCounterType][buildingsClass] += player.sequences[buildingCounterType][buildingsClass][maxIndex] + ","; let total = 0; - for (let type in player.statistics.resourcesGathered) - total += player.statistics.resourcesGathered[type]; + for (let type in player.sequences.resourcesGathered) + total += player.sequences.resourcesGathered[type][maxIndex]; playerStatistics.economyScore += total + ","; - playerStatistics.militaryScore += Math.round((player.statistics.enemyUnitsKilledValue + - player.statistics.enemyBuildingsDestroyedValue) / 10) + ","; - playerStatistics.totalScore += (total + Math.round((player.statistics.enemyUnitsKilledValue + - player.statistics.enemyBuildingsDestroyedValue) / 10)) + ","; - playerStatistics.tradeIncome += player.statistics.tradeIncome + ","; - playerStatistics.tributesSent += player.statistics.tributesSent + ","; - playerStatistics.tributesReceived += player.statistics.tributesReceived + ","; - playerStatistics.percentMapExplored += player.statistics.percentMapExplored + ","; - playerStatistics.treasuresCollected += player.statistics.treasuresCollected + ","; - playerStatistics.lootCollected += player.statistics.lootCollected + ","; + playerStatistics.militaryScore += Math.round((player.sequences.enemyUnitsKilledValue[maxIndex] + + player.sequences.enemyBuildingsDestroyedValue[maxIndex]) / 10) + ","; + playerStatistics.totalScore += (total + Math.round((player.sequences.enemyUnitsKilledValue[maxIndex] + + player.sequences.enemyBuildingsDestroyedValue[maxIndex]) / 10)) + ","; + + for (let type of misc) + playerStatistics[type] += player.sequences[type][maxIndex] + ","; } // Send the report with serialized data @@ -1577,12 +1579,8 @@ reportObject[(type.substr(0,1)).toLowerCase()+type.substr(1)+"BuildingsLost"] = playerStatistics.buildingsLost[type]; reportObject["enemy"+type+"BuildingsDestroyed"] = playerStatistics.enemyBuildingsDestroyed[type]; } - reportObject.tributesSent = playerStatistics.tributesSent; - reportObject.tributesReceived = playerStatistics.tributesReceived; - reportObject.percentMapExplored = playerStatistics.percentMapExplored; - reportObject.treasuresCollected = playerStatistics.treasuresCollected; - reportObject.lootCollected = playerStatistics.lootCollected; - reportObject.tradeIncome = playerStatistics.tradeIncome; + for (let type of misc) + reportObject[type] = playerStatistics[type]; Engine.SendGameReport(reportObject); } 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 @@ -5,44 +5,51 @@ g_TeamHelperData = []; } -function formatTrained(trained, killed, lost) +function calculatePercent(divident, divisor) { - return g_TrainedColor + trained + '[/color] / ' + - g_KilledColor + killed + '[/color] / ' + - g_LostColor + lost + '[/color]'; + return { "percent": divisor ? Math.floor(100 * divident / divisor) : 0 }; } -function formatCaptured(constructed, destroyed, captured, lost) +function calculateRatio(divident, divisor) { - return g_TrainedColor + constructed + '[/color] / ' + - g_KilledColor + destroyed + '[/color]\n' + - g_CapturedColor + captured + '[/color] / ' + - g_LostColor + lost + '[/color]\n'; + return divident ? Math.round(divident / divisor * 100) / 100 : 0; } -function formatIncome(income, outcome) +function formatValue(values) { - return g_IncomeColor + income + '[/color] / ' + - g_OutcomeColor + outcome + '[/color]'; -} - -function formatPercent(divident, divisor) -{ - if (!divisor) - return "0%"; - - return Math.floor(100 * divident / divisor) + "%"; -} + if (typeof values != "object") + return values === Infinity ? g_InfinitySymbol : values; -function formatRatio(divident, divisor) -{ - if (!divident) - return "0.00"; - - if (!divisor) - return g_InfiniteSymbol; - - return Math.round(divident / divisor * 100) / 100; + let ret = ""; + for (let type in values) + { + let color = g_SummaryColors[type]; + switch (type) + { + case "percent": + ret += values[type] + "%"; + break; + case "constructed": + case "trained": + case "captured": + case "gathered": + case "sent": + case "bought": + case "income": + ret += color + values[type] + '[/color] / '; + break; + case "destroyed": + case "killed": + case "lost": + case "used": + case "received": + case "sold": + case "outcome": + ret += color + values[type] + '[/color]\n'; + break; + } + } + return ret; } /** @@ -59,83 +66,90 @@ return caption.replace(/\[([\w\' \\\"\/\=]*)\]|[\t\r \f]/g, ""); } -function updateCountersPlayer(playerState, counters, idGUI) +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 = fn && fn(playerState, w); + Engine.GetGUIObjectByName(idGUI + "[" + w + "]").caption = formatValue(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. + * @param {Array} array2 - second summand. + * @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] = { - "food": 0, - "vegetarianFood": 0, - "femaleCitizen": 0, - "worker": 0, - "enemyUnitsKilled": 0, - "unitsLost": 0, - "percentMapControlled": 0, - "peakPercentMapControlled": 0, - "percentMapExplored": 0, - "totalBought": 0, - "totalSold": 0 - }; - - g_TeamHelperData[playerState.team].food += playerState.statistics.resourcesGathered.food; - g_TeamHelperData[playerState.team].vegetarianFood += playerState.statistics.resourcesGathered.vegetarianFood; - - g_TeamHelperData[playerState.team].femaleCitizen += playerState.statistics.unitsTrained.FemaleCitizen; - g_TeamHelperData[playerState.team].worker += playerState.statistics.unitsTrained.Worker; - - g_TeamHelperData[playerState.team].enemyUnitsKilled += playerState.statistics.enemyUnitsKilled.total; - g_TeamHelperData[playerState.team].unitsLost += playerState.statistics.unitsLost.total; - - g_TeamHelperData[playerState.team].percentMapControlled = playerState.statistics.teamPercentMapControlled; - g_TeamHelperData[playerState.team].peakPercentMapControlled = playerState.statistics.teamPeakPercentMapControlled; - - g_TeamHelperData[playerState.team].percentMapExplored = playerState.statistics.teamPercentMapExplored; + { + 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); + } - for (let type in playerState.statistics.resourcesBought) - g_TeamHelperData[playerState.team].totalBought += playerState.statistics.resourcesBought[type]; + addArray(g_TeamHelperData[playerState.team].food, playerState.sequences.resourcesGathered.food); + addArray(g_TeamHelperData[playerState.team].vegetarianFood, playerState.sequences.resourcesGathered.vegetarianFood); - for (let type in playerState.statistics.resourcesSold) - g_TeamHelperData[playerState.team].totalSold += playerState.statistics.resourcesSold[type]; + 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) +function calculateEconomyScore(playerState, index) { let total = 0; - for (let type in playerState.statistics.resourcesGathered) - total += playerState.statistics.resourcesGathered[type]; + for (let type in playerState.sequences.resourcesGathered) + total += playerState.sequences.resourcesGathered[type][index]; return Math.round(total / 10); } -function calculateMilitaryScore(playerState) +function calculateMilitaryScore(playerState, index) { - return Math.round((playerState.statistics.enemyUnitsKilledValue + - playerState.statistics.enemyBuildingsDestroyedValue + - playerState.statistics.buildingsCapturedValue) / 10); + return Math.round((playerState.sequences.enemyUnitsKilledValue[index] + + playerState.sequences.enemyBuildingsDestroyedValue[index] + + playerState.sequences.buildingsCapturedValue[index]) / 10); } -function calculateExplorationScore(playerState) +function calculateExplorationScore(playerState, index) { - return playerState.statistics.percentMapExplored * 10; + return playerState.sequences.percentMapExplored[index] * 10; } -function calculateScoreTotal(playerState) +function calculateScoreTotal(playerState, index) { - return calculateEconomyScore(playerState) + - calculateMilitaryScore(playerState) + - calculateExplorationScore(playerState); + return calculateEconomyScore(playerState, index) + + calculateMilitaryScore(playerState, index) + + calculateExplorationScore(playerState, index); } -function calculateScoreTeam(counters) +function calculateScoreTeam(counters, index) { for (let t in g_Teams) { @@ -148,7 +162,7 @@ let total = 0; if (w == 2) // Team exploration score (not additive) - total = g_TeamHelperData[t].percentMapExplored * 10; + 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; @@ -164,17 +178,17 @@ } } -function calculateBuildings(playerState, position) +function calculateBuildings(playerState, index, type) { - let type = g_BuildingsTypes[position]; - return formatCaptured( - playerState.statistics.buildingsConstructed[type], - playerState.statistics.enemyBuildingsDestroyed[type], - playerState.statistics.buildingsCaptured[type], - playerState.statistics.buildingsLost[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) +function calculateBuildingsTeam(counters, index) { for (let t in g_Teams) { @@ -203,12 +217,12 @@ } Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = - formatCaptured(total.constructed, total.destroyed, total.captured, total.lost); + formatValue(total); } } } -function calculateUnitsTeam(counters) +function calculateUnitsTeam(counters, index) { for (let t in g_Teams) { @@ -227,100 +241,89 @@ 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 splitCaption = cleanGUICaption(t, p, w, false).split("\n"); - let first = splitCaption[0].split("/"); let second = splitCaption[1].split("/"); - - total.trained += +first[0]; - total.killed += +first[1]; total.captured += +second[0]; total.lost += +second[1]; } else - { - let splitCaption = cleanGUICaption(t, p, w).split("/"); - total.trained += +splitCaption[0]; - total.killed += +splitCaption[1]; - total.lost += +splitCaption[2]; - } + total.lost += +splitCaption[1]; } - let formattedCaption = ""; + if (w != 0 && w != 6) + delete total.captured; - if (w == 0 || w == 6) - formattedCaption = formatCaptured(total.trained, total.killed, total.captured, total.lost); - else - formattedCaption = formatTrained(total.trained, total.killed, total.lost); - - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formattedCaption; + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatValue(total); } } } -function calculateUnitsWithCaptured(playerState, position) +function calculateUnitsWithCaptured(playerState, index, type) { - let type = g_UnitsTypes[position]; - - return formatCaptured( - playerState.statistics.unitsTrained[type], - playerState.statistics.enemyUnitsKilled[type], - playerState.statistics.unitsCaptured[type], - playerState.statistics.unitsLost[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, position) +function calculateUnits(playerState, index, type) { - let type = g_UnitsTypes[position]; - - return formatTrained( - playerState.statistics.unitsTrained[type], - playerState.statistics.enemyUnitsKilled[type], - playerState.statistics.unitsLost[type]); + return { + "trained": playerState.sequences.unitsTrained[type][index], + "killed": playerState.sequences.enemyUnitsKilled[type][index], + "lost": playerState.sequences.unitsLost[type][index] + }; } -function calculateResources(playerState, position) +function calculateResources(playerState, index, type) { - let type = g_ResourceData.GetCodes()[position]; - - return formatIncome( - playerState.statistics.resourcesGathered[type], - playerState.statistics.resourcesUsed[type] - playerState.statistics.resourcesSold[type]); + return { + "gathered": playerState.sequences.resourcesGathered[type][index], + "used": playerState.sequences.resourcesUsed[type][index] - playerState.sequences.resourcesSold[type][index] + }; } -function calculateTotalResources(playerState) +function calculateTotalResources(playerState, index) { let totalGathered = 0; let totalUsed = 0; for (let type of g_ResourceData.GetCodes()) { - totalGathered += playerState.statistics.resourcesGathered[type]; - totalUsed += playerState.statistics.resourcesUsed[type] - playerState.statistics.resourcesSold[type]; + totalGathered += playerState.sequences.resourcesGathered[type][index]; + totalUsed += playerState.sequences.resourcesUsed[type][index] - playerState.sequences.resourcesSold[type][index]; } - return formatIncome(totalGathered, totalUsed); + return { "gathered": totalGathered, "used": totalUsed }; } -function calculateTreasureCollected(playerState) +function calculateTreasureCollected(playerState, index) { - return playerState.statistics.treasuresCollected; + return playerState.sequences.treasuresCollected[index]; } -function calculateLootCollected(playerState) +function calculateLootCollected(playerState, index) { - return playerState.statistics.lootCollected; + return playerState.sequences.lootCollected[index]; } -function calculateTributeSent(playerState) +function calculateTributeSent(playerState, index) { - return formatIncome( - playerState.statistics.tributesSent, - playerState.statistics.tributesReceived); + return { + "sent": playerState.sequences.tributesSent[index], + "received": playerState.sequences.tributesReceived[index] + }; } -function calculateResourcesTeam(counters) +function calculateResourcesTeam(counters, index) { for (let t in g_Teams) { @@ -352,43 +355,44 @@ let teamTotal; if (w >= 6) teamTotal = total.income; + else if (w == 5) + teamTotal = { "sent": total.income, "received": total.outcome }; else - teamTotal = formatIncome(total.income, total.outcome); + teamTotal = { "gathered": total.income, "used": total.outcome }; - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotal; + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatValue(teamTotal); } } } -function calculateResourceExchanged(playerState, position) +function calculateResourceExchanged(playerState, index, type) { - let type = g_ResourceData.GetCodes()[position]; - - return formatIncome( - playerState.statistics.resourcesBought[type], - playerState.statistics.resourcesSold[type]); + return { + "bought": playerState.sequences.resourcesBought[type][index], + "sold": playerState.sequences.resourcesSold[type][index] + }; } -function calculateBarterEfficiency(playerState) +function calculateBarterEfficiency(playerState, index) { let totalBought = 0; let totalSold = 0; - for (let type in playerState.statistics.resourcesBought) - totalBought += playerState.statistics.resourcesBought[type]; + for (let type in playerState.sequences.resourcesBought) + totalBought += playerState.sequences.resourcesBought[type][index]; - for (let type in playerState.statistics.resourcesSold) - totalSold += playerState.statistics.resourcesSold[type]; + for (let type in playerState.sequences.resourcesSold) + totalSold += playerState.sequences.resourcesSold[type][index]; - return formatPercent(totalBought, totalSold); + return calculatePercent(totalBought, totalSold); } -function calculateTradeIncome(playerState) +function calculateTradeIncome(playerState, index) { - return playerState.statistics.tradeIncome; + return playerState.sequences.tradeIncome[index]; } -function calculateMarketTeam(counters) +function calculateMarketTeam(counters, index) { for (let t in g_Teams) { @@ -418,54 +422,54 @@ let teamTotal; if (w == 4) - teamTotal = formatPercent(g_TeamHelperData[t].totalBought, g_TeamHelperData[t].totalSold); + teamTotal = calculatePercent(g_TeamHelperData[t].totalBought[index], g_TeamHelperData[t].totalSold[index]); else if (w > 4) teamTotal = total.income; else - teamTotal = formatIncome(total.income, total.outcome); + teamTotal = total; - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotal; + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatValue(teamTotal); } } } -function calculateVegetarianRatio(playerState) +function calculateVegetarianRatio(playerState, index) { - return formatPercent( - playerState.statistics.resourcesGathered.vegetarianFood, - playerState.statistics.resourcesGathered.food); + return calculatePercent( + playerState.sequences.resourcesGathered.vegetarianFood[index], + playerState.sequences.resourcesGathered.food[index]); } -function calculateFeminization(playerState) +function calculateFeminization(playerState, index) { - return formatPercent( - playerState.statistics.unitsTrained.FemaleCitizen, - playerState.statistics.unitsTrained.Worker); + return calculatePercent( + playerState.sequences.unitsTrained.FemaleCitizen[index], + playerState.sequences.unitsTrained.Worker[index]); } -function calculateKillDeathRatio(playerState) +function calculateKillDeathRatio(playerState, index) { - return formatRatio( - playerState.statistics.enemyUnitsKilled.total, - playerState.statistics.unitsLost.total); + return calculateRatio( + playerState.sequences.enemyUnitsKilled.total[index], + playerState.sequences.unitsLost.total[index]); } -function calculateMapExploration(playerState) +function calculateMapExploration(playerState, index) { - return playerState.statistics.percentMapExplored + "%"; + return { "percent": playerState.sequences.percentMapExplored[index] }; } -function calculateMapFinalControl(playerState) +function calculateMapFinalControl(playerState, index) { - return playerState.statistics.percentMapControlled + "%"; + return { "percent": playerState.sequences.percentMapControlled[index] }; } -function calculateMapPeakControl(playerState) +function calculateMapPeakControl(playerState, index) { - return playerState.statistics.peakPercentMapControlled + "%"; + return { "percent": playerState.sequences.peakPercentMapControlled[index] }; } -function calculateMiscellaneous(counters) +function calculateMiscellaneousTeam(counters, index) { for (let t in g_Teams) { @@ -477,19 +481,19 @@ let teamTotal; if (w == 0) - teamTotal = formatPercent(g_TeamHelperData[t].vegetarianFood, g_TeamHelperData[t].food); + teamTotal = calculatePercent(g_TeamHelperData[t].vegetarianFood[index], g_TeamHelperData[t].food[index]); else if (w == 1) - teamTotal = formatPercent(g_TeamHelperData[t].femaleCitizen, g_TeamHelperData[t].worker); + teamTotal = calculatePercent(g_TeamHelperData[t].femaleCitizen[index], g_TeamHelperData[t].worker[index]); else if (w == 2) - teamTotal = formatRatio(g_TeamHelperData[t].enemyUnitsKilled, g_TeamHelperData[t].unitsLost); + teamTotal = calculateRatio(g_TeamHelperData[t].enemyUnitsKilled[index], g_TeamHelperData[t].unitsLost[index]); else if (w == 3) - teamTotal = g_TeamHelperData[t].percentMapExplored + "%"; + teamTotal = { "percent": g_TeamHelperData[t].percentMapExplored[index] }; else if (w == 4) - teamTotal = g_TeamHelperData[t].peakPercentMapControlled + "%"; + teamTotal = { "percent": g_TeamHelperData[t].peakPercentMapControlled[index] }; else if (w == 5) - teamTotal = g_TeamHelperData[t].percentMapControlled + "%"; + teamTotal = { "percent": g_TeamHelperData[t].percentMapControlled[index] }; - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotal; + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatValue(teamTotal); } } } 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 @@ -1,11 +1,12 @@ var g_ScorePanelsData = { "score": { + "caption": translate("Score"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, - { "caption": translate("Economy score"), "yStart": 16, "width": 100 }, - { "caption": translate("Military score"), "yStart": 16, "width": 100 }, - { "caption": translate("Exploration score"), "yStart": 16, "width": 100 }, - { "caption": translate("Total score"), "yStart": 16, "width": 100 } + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "economyScore", "caption": translate("Economy score"), "yStart": 16, "width": 100 }, + { "identifier": "militaryScore", "caption": translate("Military score"), "yStart": 16, "width": 100 }, + { "identifier": "explorationScore", "caption": translate("Exploration score"), "yStart": 16, "width": 100 }, + { "identifier": "totalScore", "caption": translate("Total score"), "yStart": 16, "width": 100 } ], "titleHeadings": [], "counters": [ @@ -17,28 +18,29 @@ "teamCounterFn": calculateScoreTeam }, "buildings": { + "caption": translate("Buildings"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, - { "caption": translate("Total"), "yStart": 34, "width": 105 }, - { "caption": translate("Houses"), "yStart": 34, "width": 85 }, - { "caption": translate("Economic"), "yStart": 34, "width": 85 }, - { "caption": translate("Outposts"), "yStart": 34, "width": 85 }, - { "caption": translate("Military"), "yStart": 34, "width": 85 }, - { "caption": translate("Fortresses"), "yStart": 34, "width": 85 }, - { "caption": translate("Civ centers"), "yStart": 34, "width": 85 }, - { "caption": translate("Wonders"), "yStart": 34, "width": 85 } + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "total", "caption": translate("Total"), "yStart": 34, "width": 105 }, + { "identifier": "House", "caption": translate("Houses"), "yStart": 34, "width": 85 }, + { "identifier": "Economic", "caption": translate("Economic"), "yStart": 34, "width": 85 }, + { "identifier": "Outpost", "caption": translate("Outposts"), "yStart": 34, "width": 85 }, + { "identifier": "Military", "caption": translate("Military"), "yStart": 34, "width": 85 }, + { "identifier": "Fortress", "caption": translate("Fortresses"), "yStart": 34, "width": 85 }, + { "identifier": "CivCentre", "caption": translate("Civ centers"), "yStart": 34, "width": 85 }, + { "identifier": "Wonder", "caption": translate("Wonders"), "yStart": 34, "width": 85 } ], "titleHeadings": [ { "caption": sprintf(translate("Buildings Statistics (%(constructed)s / %(destroyed)s / %(captured)s / %(lost)s)"), { - "constructed": g_TrainedColor + translate("Constructed") + '[/color]', - "destroyed": g_KilledColor + translate("Destroyed") + '[/color]', - "captured": g_CapturedColor + translate("Captured") + '[/color]', - "lost": g_LostColor + translate("Lost") + '[/color]' + "constructed": getColoredTypeTranslation("constructed"), + "destroyed": getColoredTypeTranslation("destroyed"), + "captured": getColoredTypeTranslation("captured"), + "lost": getColoredTypeTranslation("lost") }), "yStart": 16, - "width": (85 * 7 + 105) + "width": 85 * 7 + 105 }, // width = 700 ], "counters": [ @@ -54,71 +56,75 @@ "teamCounterFn": calculateBuildingsTeam }, "units": { + "caption": translate("Units"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, - { "caption": translate("Total"), "yStart": 34, "width": 105 }, - { "caption": translate("Infantry"), "yStart": 34, "width": 85 }, - { "caption": translate("Worker"), "yStart": 34, "width": 85 }, - { "caption": translate("Cavalry"), "yStart": 34, "width": 85 }, - { "caption": translate("Champion"), "yStart": 34, "width": 85 }, - { "caption": translate("Heroes"), "yStart": 34, "width": 85 }, - { "caption": translate("Siege"), "yStart": 34, "width": 85 }, - { "caption": translate("Navy"), "yStart": 34, "width": 85 }, - { "caption": translate("Traders"), "yStart": 34, "width": 85 } + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "total", "caption": translate("Total"), "yStart": 34, "width": 105 }, + { "identifier": "Infantry", "caption": translate("Infantry"), "yStart": 34, "width": 85 }, + { "identifier": "Worker", "caption": translate("Worker"), "yStart": 34, "width": 85 }, + { "identifier": "Cavalry", "caption": translate("Cavalry"), "yStart": 34, "width": 85 }, + { "identifier": "Champion", "caption": translate("Champion"), "yStart": 34, "width": 85 }, + { "identifier": "Hero", "caption": translate("Heroes"), "yStart": 34, "width": 85 }, + { "identifier": "Siege", "caption": translate("Siege"), "yStart": 34, "width": 85 }, + { "identifier": "Ship", "caption": translate("Navy"), "yStart": 34, "width": 85 }, + { "identifier": "Trader", "caption": translate("Traders"), "yStart": 34, "width": 85 } ], "titleHeadings": [ { "caption": sprintf(translate("Units Statistics (%(trained)s / %(killed)s / %(captured)s / %(lost)s)"), { - "trained": g_TrainedColor + translate("Trained") + '[/color]', - "killed": g_KilledColor + translate("Killed") + '[/color]', - "captured": g_CapturedColor + translate("Captured") + '[/color]', - "lost": g_LostColor + translate("Lost") + '[/color]' + "trained": getColoredTypeTranslation("trained"), + "killed": getColoredTypeTranslation("killed"), + "captured": getColoredTypeTranslation("captured"), + "lost": getColoredTypeTranslation("lost") }), "yStart": 16, - "width": (100 * 7 + 120) - }, // width = 820 + "width": 85 * 8 + 105 + }, // width = 785 ], "counters": [ { "width": 105, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 105, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 85, "fn": calculateUnits, "verticalOffset": 12 } + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 3 } ], "teamCounterFn": calculateUnitsTeam }, "resources": { + "caption": translate("Resources"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, ...g_ResourceData.GetResources().map(res => ({ + "identifier": res.code, "caption": translateWithContext("firstWord", res.name), "yStart": 34, "width": 100 })), - { "caption": translate("Total"), "yStart": 34, "width": 110 }, + { "identifier": "total", "caption": translate("Total"), "yStart": 34, "width": 110 }, { + "identifier": "tributes", "caption": sprintf(translate("Tributes \n(%(sent)s / %(received)s)"), { - "sent": g_IncomeColor + translate("Sent") + '[/color]', - "received": g_OutcomeColor + translate("Received") + '[/color]' + "sent": getColoredTypeTranslation("sent"), + "received": getColoredTypeTranslation("received") }), "yStart": 16, "width": 121 }, - { "caption": translate("Treasures collected"), "yStart": 16, "width": 100 }, - { "caption": translate("Loot"), "yStart": 16, "width": 100 } + { "identifier": "treasuresCollected", "caption": translate("Treasures collected"), "yStart": 16, "width": 100 }, + { "identifier": "loot", "caption": translate("Loot"), "yStart": 16, "width": 100 } ], "titleHeadings": [ { "caption": sprintf(translate("Resource Statistics (%(gathered)s / %(used)s)"), { - "gathered": g_IncomeColor + translate("Gathered") + '[/color]', - "used": g_OutcomeColor + translate("Used") + '[/color]' + "gathered": getColoredTypeTranslation("gathered"), + "used": getColoredTypeTranslation("used") }), "yStart": 16, "width": 100 * g_ResourceData.GetCodes().length + 110 @@ -138,10 +144,12 @@ "teamCounterFn": calculateResourcesTeam }, "market": { + "caption": translate("Market"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, ...g_ResourceData.GetResources().map(res => { return { + "identifier": res.code, "caption": // Translation: use %(resourceWithinSentence)s if needed sprintf(translate("%(resourceFirstWord)s exchanged"), { @@ -152,8 +160,8 @@ "width": 100 }; }), - { "caption": translate("Barter efficiency"), "yStart": 16, "width": 100 }, - { "caption": translate("Trade income"), "yStart": 16, "width": 100 } + { "identifier": "barterEfficency", "caption": translate("Barter efficiency"), "yStart": 16, "width": 100 }, + { "identifier": "tradeIncome", "caption": translate("Trade income"), "yStart": 16, "width": 100 } ], "titleHeadings": [], "counters": [ @@ -168,18 +176,17 @@ "teamCounterFn": calculateMarketTeam }, "misc": { + "caption": translate("Miscellaneous"), "headings": [ - { "caption": translate("Player name"), "yStart": 26, "width": 200 }, - { "caption": translate("Vegetarian\nratio"), "yStart": 16, "width": 100 }, - { "caption": translate("Feminization"), "yStart": 16, "width": 100 }, - { "caption": translate("Kill / Death\nratio"), "yStart": 16, "width": 100 }, - { "caption": translate("Map\nexploration"), "yStart": 16, "width": 100 }, - { "caption": translate("At peak"), "yStart": 34, "width": 100 }, - { "caption": translate("At finish"), "yStart": 34, "width": 100 } - ], - "titleHeadings": [ - { "caption": translate("Map control"), "xOffset": 400, "yStart": 16, "width": 200 } + { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, + { "identifier": "vegetarianRatio", "caption": translate("Vegetarian ratio"), "yStart": 16, "width": 100 }, + { "identifier": "feminization", "caption": translate("Feminization"), "yStart": 16, "width": 100 }, + { "identifier": "killDeath", "caption": translate("Kill / Death ratio"), "yStart": 16, "width": 100 }, + { "identifier": "mapExploration", "caption": translate("Map exploration"), "yStart": 16, "width": 100 }, + { "identifier": "mapControlPeak", "caption": translate("Map control (peak)"), "yStart": 16, "width": 100 }, + { "identifier": "mapControlFinish", "caption": translate("Map control (finish)"), "yStart": 16, "width": 100 } ], + "titleHeadings": [], "counters": [ { "width": 100, "fn": calculateVegetarianRatio, "verticalOffset": 12 }, { "width": 100, "fn": calculateFeminization, "verticalOffset": 12 }, @@ -188,10 +195,15 @@ { "width": 100, "fn": calculateMapPeakControl, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapFinalControl, "verticalOffset": 12 } ], - "teamCounterFn": calculateMiscellaneous + "teamCounterFn": calculateMiscellaneousTeam } }; +function getColoredTypeTranslation(type) +{ + return g_SummaryColors[type] + g_TypeTranslations[type] + '[/color]'; +} + function resetGeneralPanel() { for (let h = 0; h < g_MaxHeadingTitle; ++h) 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 @@ -10,17 +10,40 @@ const g_TeamsBoxYStart = 40; // Colors used for units and buildings -const g_TrainedColor = '[color="201 255 200"]'; -const g_LostColor = '[color="255 213 213"]'; -const g_KilledColor = '[color="196 198 255"]'; -const g_CapturedColor = '[color="255 255 157"]'; - -const g_BuildingsTypes = [ "total", "House", "Economic", "Outpost", "Military", "Fortress", "CivCentre", "Wonder" ]; -const g_UnitsTypes = [ "total", "Infantry", "Worker", "Cavalry", "Champion", "Hero", "Siege", "Ship", "Trader" ]; - -// Colors used for gathered and traded resources -const g_IncomeColor = '[color="201 255 200"]'; -const g_OutcomeColor = '[color="255 213 213"]'; +const g_SummaryColors = { + "trained": '[color="201 255 200"]', + "constructed": '[color="201 255 200"]', + "lost": '[color="255 213 213"]', + "killed": '[color="196 198 255"]', + "destroyed": '[color="196 198 255"]', + "captured": '[color="255 255 157"]', + "income": '[color="201 255 200"]', + "gathered": '[color="201 255 200"]', + "sent": '[color="201 255 200"]', + "bought": '[color="201 255 200"]', + "outcome": '[color="255 213 213"]', + "used": '[color="255 213 213"]', + "received": '[color="255 213 213"]', + "sold": '[color="255 213 213"]' +}; + +const g_TypeTranslations = { + "percent": "%", + "constructed": translate("Constructed"), + "trained": translate("Trained"), + "captured": translate("Captured"), + "gathered": translate("Gathered"), + "sent": translate("Sent"), + "bought": translate("Bought"), + "income": translate("Income"), + "destroyed": translate("Destroyed"), + "killed": translate("Killed"), + "lost": translate("Lost"), + "used": translate("Used"), + "received": translate("Received"), + "sold": translate("Sold"), + "outcome": translate("Outcome") +}; const g_InfiniteSymbol = "\u221E"; @@ -47,7 +70,134 @@ adjustTabDividers(panel.size); - updatePanelData(g_ScorePanelsData[panel.name.substr(0, panel.name.length - "PanelButton".length)]); + let generalPanel = Engine.GetGUIObjectByName("generalPanel"); + let chartsPanel = Engine.GetGUIObjectByName("chartsPanel"); + if (panel.name != "chartsPanelButton") + { + generalPanel.hidden = false; + chartsPanel.hidden = true; + updatePanelData(g_ScorePanelsData[panel.name.substr(0, panel.name.length - "PanelButton".length)]); + } + else + { + generalPanel.hidden = true; + chartsPanel.hidden = false; + updateCategoryDropdown(1); + updateCategoryDropdown(2); + } +} + +function initCharts() +{ + let player_colors = []; + for (let i = 1; i <= g_PlayerCount; ++i) + { + let playerState = g_GameData.sim.playerStates[i]; + player_colors.push( + Math.floor(playerState.color.r * 255) + " " + + Math.floor(playerState.color.g * 255) + " " + + Math.floor(playerState.color.b * 255) + ); + } + let chart1 = Engine.GetGUIObjectByName("chart1"); + chart1.series_color = player_colors; + let chart2 = Engine.GetGUIObjectByName("chart2"); + chart2.series_color = player_colors; + let chartLegend = Engine.GetGUIObjectByName("chartLegend"); + chartLegend.caption = g_GameData.sim.playerStates.slice(1).map( + (state, index) => '[color="' + player_colors[index] + '"]■[/color] ' + state.name + ).join(" "); +} + +function resizeDropdown(dropdown) +{ + let size = dropdown.size; + size.bottom = dropdown.size.top + + (Engine.GetTextWidth(dropdown.font, dropdown.list[dropdown.selected]) > + dropdown.size.right - dropdown.size.left - 32 ? 42 : 27); + dropdown.size = size; +} + +function updateCategoryDropdown(number) +{ + let chartCategory = Engine.GetGUIObjectByName("chart" + number + "CategorySelection"); + chartCategory.list_data = Object.keys(g_ScorePanelsData); + chartCategory.list = Object.keys(g_ScorePanelsData).map(panel => g_ScorePanelsData[panel].caption); + chartCategory.onSelectionChange = function() { + if (this.list_data[this.selected]) + { + resizeDropdown(this); + updateValueDropdown(number, this.list_data[this.selected]); + } + }; + chartCategory.selected = 0; +} + +function updateValueDropdown(number, category) +{ + let chartValue = Engine.GetGUIObjectByName("chart" + number + "ValueSelection"); + let list = g_ScorePanelsData[category].headings.map(heading => heading.caption); + list.shift(); + chartValue.list = list; + let list_data = g_ScorePanelsData[category].headings.map(heading => heading.identifier); + list_data.shift(); + chartValue.list_data = list_data; + chartValue.onSelectionChange = function() { + if (this.list_data[this.selected]) + { + resizeDropdown(this); + updateTypeDropdown(number, category, this.list_data[this.selected], this.selected); + } + }; + chartValue.selected = 0; +} + +function updateTypeDropdown(number, category, item, itemNumber) +{ + let testValue = g_ScorePanelsData[category].counters[itemNumber].fn(g_GameData.sim.playerStates[1], 0, item); + let hide = !g_ScorePanelsData[category].counters[itemNumber].fn || + typeof testValue != "object" || Object.keys(testValue).length < 2; + Engine.GetGUIObjectByName("chart" + number + "TypeLabel").hidden = hide; + let chartType = Engine.GetGUIObjectByName("chart" + number + "TypeSelection"); + chartType.hidden = hide; + if (hide) + { + updateChart(number, category, item, itemNumber, Object.keys(testValue)[0] || undefined); + return; + } + + chartType.list = Object.keys(testValue).map(type => g_TypeTranslations[type]); + chartType.list_data = Object.keys(testValue); + chartType.onSelectionChange = function() { + if (this.list_data[this.selected]) + { + resizeDropdown(this); + updateChart(number, category, item, itemNumber, this.list_data[this.selected]); + } + }; + chartType.selected = 0; +} + +function updateChart(number, category, item, itemNumber, type) +{ + if (!g_ScorePanelsData[category].counters[itemNumber].fn) + return; + let chart = Engine.GetGUIObjectByName("chart" + number); + let series = []; + for (let j = 1; j <= g_PlayerCount; ++j) + { + let playerState = g_GameData.sim.playerStates[j]; + let data = []; + for (let index in playerState.sequences.time) + { + let value = g_ScorePanelsData[category].counters[itemNumber].fn(playerState, index, item); + if (type) + value = value[type]; + data.push([playerState.sequences.time[index], value]); + } + series.push(data); + } + chart.series = series; } function adjustTabDividers(tabSize) @@ -126,14 +276,14 @@ civIcon.sprite = "stretched:" + g_CivData[playerState.civ].Emblem; civIcon.tooltip = g_CivData[playerState.civ].Name; - updateCountersPlayer(playerState, panelInfo.counters, playerCounterValue); + updateCountersPlayer(playerState, panelInfo.counters, panelInfo.headings, playerCounterValue); calculateTeamCounters(playerState); } let teamCounterFn = panelInfo.teamCounterFn; if (g_Teams && teamCounterFn) - teamCounterFn(panelInfo.counters); + teamCounterFn(panelInfo.counters, g_GameData.sim.playerStates[1].sequences.time.length - 1); } function confirmStartReplay() @@ -256,5 +406,6 @@ g_WithoutTeam -= g_Teams[i] ? g_Teams[i] : 0; } + initCharts(); selectPanel(Engine.GetGUIObjectByName("scorePanelButton")); } Index: binaries/data/mods/public/gui/summary/summary.xml =================================================================== --- binaries/data/mods/public/gui/summary/summary.xml +++ binaries/data/mods/public/gui/summary/summary.xml @@ -99,6 +99,13 @@ + + selectPanel(this); + + Charts + + + @@ -152,6 +159,74 @@ + + + + Category: + + + Category + + + Category: + + + Category + + + + Value: + + + Value + + + Value: + + + Value + + + + Type: + + + + Type: + + + + + + + + + + Replay 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 @@ -194,7 +194,7 @@ let playerEnt = cmpPlayerManager.GetPlayerByID(i); let cmpPlayerStatisticsTracker = Engine.QueryInterface(playerEnt, IID_StatisticsTracker); if (cmpPlayerStatisticsTracker) - ret.players[i].statistics = cmpPlayerStatisticsTracker.GetStatistics(); + ret.players[i].sequences = cmpPlayerStatisticsTracker.GetSequences(); } return ret; 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 @@ -1,5 +1,7 @@ function StatisticsTracker() {} +const g_UpdateSequenceInterval = 30000; // 30 seconds + StatisticsTracker.prototype.Schema = ""; @@ -142,6 +144,10 @@ 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); }; /** @@ -195,11 +201,21 @@ }; }; +StatisticsTracker.prototype.GetSequences = function() +{ + 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") + * @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) { @@ -355,9 +371,9 @@ }; /** - * @param type Generic type of resource (string) - * @param amount Amount of resource, whick should be added (integer) - * @param specificType Specific type of resource (string, optional) + * @param {string} type - Generic type of resource. + * @param {integer} amount - Amount of resource, whick should be added. + * @param {string} specificType - Specific type of resource. */ StatisticsTracker.prototype.IncreaseResourceGatheredCounter = function(type, amount, specificType) { @@ -368,8 +384,8 @@ }; /** - * @param type Generic type of resource (string) - * @param amount Amount of resource, which should be added (integer) + * @param {string} type - Generic type of resource. + * @param {integer} amount - Amount of resource, which should be added. */ StatisticsTracker.prototype.IncreaseResourceUsedCounter = function(type, amount) { @@ -492,4 +508,40 @@ 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 + 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); Index: binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js +++ binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js @@ -144,29 +144,29 @@ "percentMapExplored": 10 }; }, - GetStatistics: function() { + GetSequences: function() { return { - "unitsTrained": 10, - "unitsLost": 9, - "buildingsConstructed": 5, - "buildingsCaptured": 7, - "buildingsLost": 4, - "civCentresBuilt": 1, + "unitsTrained": [0, 10], + "unitsLost": [0, 42], + "buildingsConstructed": [1, 3], + "buildingsCaptured": [3, 7], + "buildingsLost": [3, 10], + "civCentresBuilt": [4, 10], "resourcesGathered": { - "food": 100, - "wood": 0, - "metal": 0, - "stone": 0, - "vegetarianFood": 0, + "food": [5, 100], + "wood": [0, 0], + "metal": [0, 0], + "stone": [0, 0], + "vegetarianFood": [0, 0], }, - "treasuresCollected": 0, - "lootCollected": 0, - "percentMapExplored": 10, - "teamPercentMapExplored": 10, - "percentMapControlled": 10, - "teamPercentMapControlled": 10, - "peakPercentOfMapControlled": 10, - "teamPeakPercentOfMapControlled": 10 + "treasuresCollected": [1, 20], + "lootCollected": [0, 2], + "percentMapExplored": [0, 10], + "teamPercentMapExplored": [0, 10], + "percentMapControlled": [0, 10], + "teamPercentMapControlled": [0, 10], + "peakPercentOfMapControlled": [0, 10], + "teamPeakPercentOfMapControlled": [0, 10] }; }, IncreaseTrainedUnitsCounter: function() { return 1; }, @@ -230,29 +230,29 @@ "percentMapExplored": 10 }; }, - GetStatistics: function() { + GetSequences: function() { return { - "unitsTrained": 10, - "unitsLost": 9, - "buildingsConstructed": 5, - "buildingsCaptured": 7, - "buildingsLost": 4, - "civCentresBuilt": 1, + "unitsTrained": [0, 10], + "unitsLost": [0, 9], + "buildingsConstructed": [0, 5], + "buildingsCaptured": [0, 7], + "buildingsLost": [0, 4], + "civCentresBuilt": [0, 1], "resourcesGathered": { - "food": 100, - "wood": 0, - "metal": 0, - "stone": 0, - "vegetarianFood": 0, + "food": [0, 100], + "wood": [0, 0], + "metal": [0, 0], + "stone": [0, 0], + "vegetarianFood": [0, 0], }, - "treasuresCollected": 0, - "lootCollected": 0, - "percentMapExplored": 10, - "teamPercentMapExplored": 10, - "percentMapControlled": 10, - "teamPercentMapControlled": 10, - "peakPercentOfMapControlled": 10, - "teamPeakPercentOfMapControlled": 10 + "treasuresCollected": [0, 0], + "lootCollected": [0, 0], + "percentMapExplored": [0, 10], + "teamPercentMapExplored": [0, 10], + "percentMapControlled": [0, 10], + "teamPercentMapControlled": [0, 10], + "peakPercentOfMapControlled": [0, 10], + "teamPeakPercentOfMapControlled": [0, 10] }; }, IncreaseTrainedUnitsCounter: function() { return 1; }, @@ -382,128 +382,148 @@ }); TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), { - players: [ + "players": [ { - name: "Player 1", - civ: "gaia", - color: { r:1, g:1, b:1, a:1 }, - controlsAll: false, - popCount: 10, - popLimit: 20, - popMax: 200, - panelEntities: [], - resourceCounts: { food: 100 }, - trainingBlocked: false, - state: "active", - team: -1, - teamsLocked: false, - cheatsEnabled: false, - disabledTemplates: {}, - disabledTechnologies: {}, - hasSharedDropsites: false, - hasSharedLos: false, - spyCostMultiplier: 1, - phase: "village", - isAlly: [false, false], - isMutualAlly: [false, false], - isNeutral: [false, false], - isEnemy: [true, true], - entityLimits: {"Foo": 10}, - entityCounts: {"Foo": 5}, - entityLimitChangers: {"Foo": {}}, - researchQueued: {}, - researchStarted: {}, - researchedTechs: {}, - classCounts: {}, - typeCountsByClass: {}, - canBarter: false, - statistics: { - unitsTrained: 10, - unitsLost: 9, - buildingsConstructed: 5, - buildingsCaptured: 7, - buildingsLost: 4, - civCentresBuilt: 1, - resourcesGathered: { - food: 100, - wood: 0, - metal: 0, - stone: 0, - vegetarianFood: 0, + "name": "Player 1", + "civ": "gaia", + "color": { "r":1, "g":1, "b":1, "a":1 }, + "controlsAll": false, + "popCount": 10, + "popLimit": 20, + "popMax": 200, + "panelEntities": [], + "resourceCounts": { "food": 100 }, + "trainingBlocked": false, + "state": "active", + "team": -1, + "teamsLocked": false, + "cheatsEnabled": false, + "disabledTemplates": {}, + "disabledTechnologies": {}, + "hasSharedDropsites": false, + "hasSharedLos": false, + "spyCostMultiplier": 1, + "phase": "village", + "isAlly": [false, false], + "isMutualAlly": [false, false], + "isNeutral": [false, false], + "isEnemy": [true, true], + "entityLimits": {"Foo": 10}, + "entityCounts": {"Foo": 5}, + "entityLimitChangers": {"Foo": {}}, + "researchQueued": {}, + "researchStarted": {}, + "researchedTechs": {}, + "classCounts": {}, + "typeCountsByClass": {}, + "canBarter": false, + "statistics": { + "resourcesGathered": { + "food": 100, + "wood": 0, + "metal": 0, + "stone": 0, + "vegetarianFood": 0, + }, + "percentMapExplored": 10 + }, + "sequences": { + "unitsTrained": [0, 10], + "unitsLost": [0, 42], + "buildingsConstructed": [1, 3], + "buildingsCaptured": [3, 7], + "buildingsLost": [3, 10], + "civCentresBuilt": [4, 10], + "resourcesGathered": { + "food": [5, 100], + "wood": [0, 0], + "metal": [0, 0], + "stone": [0, 0], + "vegetarianFood": [0, 0], }, - treasuresCollected: 0, - lootCollected: 0, - percentMapExplored: 10, - teamPercentMapExplored: 10, - percentMapControlled: 10, - teamPercentMapControlled: 10, - peakPercentOfMapControlled: 10, - teamPeakPercentOfMapControlled: 10 + "treasuresCollected": [1, 20], + "lootCollected": [0, 2], + "percentMapExplored": [0, 10], + "teamPercentMapExplored": [0, 10], + "percentMapControlled": [0, 10], + "teamPercentMapControlled": [0, 10], + "peakPercentOfMapControlled": [0, 10], + "teamPeakPercentOfMapControlled": [0, 10] }, }, { - name: "Player 2", - civ: "mace", - color: { r:1, g:0, b:0, a:1 }, - controlsAll: true, - popCount: 40, - popLimit: 30, - popMax: 300, - panelEntities: [], - resourceCounts: { food: 200 }, - trainingBlocked: false, - state: "active", - team: -1, - teamsLocked: false, - cheatsEnabled: false, - disabledTemplates: {}, - disabledTechnologies: {}, - hasSharedDropsites: false, - hasSharedLos: false, - spyCostMultiplier: 1, - phase: "village", - isAlly: [true, true], - isMutualAlly: [false, false], - isNeutral: [false, false], - isEnemy: [false, false], - entityLimits: {"Bar": 20}, - entityCounts: {"Bar": 0}, - entityLimitChangers: {"Bar": {}}, - researchQueued: {}, - researchStarted: {}, - researchedTechs: {}, - classCounts: {}, - typeCountsByClass: {}, - canBarter: false, - statistics: { - unitsTrained: 10, - unitsLost: 9, - buildingsConstructed: 5, - buildingsCaptured: 7, - buildingsLost: 4, - civCentresBuilt: 1, - resourcesGathered: { - food: 100, - wood: 0, - metal: 0, - stone: 0, - vegetarianFood: 0, + "name": "Player 2", + "civ": "mace", + "color": { "r":1, "g":0, "b":0, "a":1 }, + "controlsAll": true, + "popCount": 40, + "popLimit": 30, + "popMax": 300, + "panelEntities": [], + "resourceCounts": { "food": 200 }, + "trainingBlocked": false, + "state": "active", + "team": -1, + "teamsLocked": false, + "cheatsEnabled": false, + "disabledTemplates": {}, + "disabledTechnologies": {}, + "hasSharedDropsites": false, + "hasSharedLos": false, + "spyCostMultiplier": 1, + "phase": "village", + "isAlly": [true, true], + "isMutualAlly": [false, false], + "isNeutral": [false, false], + "isEnemy": [false, false], + "entityLimits": {"Bar": 20}, + "entityCounts": {"Bar": 0}, + "entityLimitChangers": {"Bar": {}}, + "researchQueued": {}, + "researchStarted": {}, + "researchedTechs": {}, + "classCounts": {}, + "typeCountsByClass": {}, + "canBarter": false, + "statistics": { + "resourcesGathered": { + "food": 100, + "wood": 0, + "metal": 0, + "stone": 0, + "vegetarianFood": 0, }, - treasuresCollected: 0, - lootCollected: 0, - percentMapExplored: 10, - teamPercentMapExplored: 10, - percentMapControlled: 10, - teamPercentMapControlled: 10, - peakPercentOfMapControlled: 10, - teamPeakPercentOfMapControlled: 10 + "percentMapExplored": 10 + }, + "sequences": { + "unitsTrained": [0, 10], + "unitsLost": [0, 9], + "buildingsConstructed": [0, 5], + "buildingsCaptured": [0, 7], + "buildingsLost": [0, 4], + "civCentresBuilt": [0, 1], + "resourcesGathered": { + "food": [0, 100], + "wood": [0, 0], + "metal": [0, 0], + "stone": [0, 0], + "vegetarianFood": [0, 0], + }, + "treasuresCollected": [0, 0], + "lootCollected": [0, 0], + "percentMapExplored": [0, 10], + "teamPercentMapExplored": [0, 10], + "percentMapControlled": [0, 10], + "teamPercentMapControlled": [0, 10], + "peakPercentOfMapControlled": [0, 10], + "teamPeakPercentOfMapControlled": [0, 10] }, } ], - circularMap: false, - timeElapsed: 0, - gameType: "conquest", - alliedVictory: false, + "circularMap": false, + "timeElapsed": 0, + "gameType": "conquest", + "alliedVictory": false, "barterPrices": { "buy": { "food": 150 }, "sell": { "food": 25 }