Index: ps/trunk/binaries/data/mods/public/gui/session/session.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/session.js +++ ps/trunk/binaries/data/mods/public/gui/session/session.js @@ -1467,6 +1467,15 @@ "resourcesBought" ]; + let misc = [ + "tradeIncome", + "tributesSent", + "tributesReceived", + "treasuresCollected", + "lootCollected", + "percentMapExplored" + ]; + let playerStatistics = {}; // Unit Stats @@ -1501,19 +1510,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 = ""; @@ -1526,6 +1529,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 + ","; @@ -1533,31 +1537,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 @@ -1595,12 +1596,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: ps/trunk/binaries/data/mods/public/gui/summary/counters.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/counters.js +++ ps/trunk/binaries/data/mods/public/gui/summary/counters.js @@ -5,44 +5,27 @@ 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 ? +((divident / divisor).toFixed(2)) : 0; } -function formatIncome(income, outcome) +function formatSummaryValue(values) { - return g_IncomeColor + income + '[/color] / ' + - g_OutcomeColor + outcome + '[/color]'; -} - -function formatPercent(divident, divisor) -{ - if (!divisor) - return "0%"; + if (typeof values != "object") + return values === Infinity ? g_InfinitySymbol : values; - return Math.floor(100 * divident / divisor) + "%"; -} - -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) + ret += (g_SummaryTypes[type].color ? + '[color="' + g_SummaryTypes[type].color + '"]' + values[type] + '[/color]' : + values[type]) + g_SummaryTypes[type].postfix; + return ret; } /** @@ -59,83 +42,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 = 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] = { - "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); + } + + 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); - for (let type in playerState.statistics.resourcesBought) - g_TeamHelperData[playerState.team].totalBought += playerState.statistics.resourcesBought[type]; + addArray(g_TeamHelperData[playerState.team].enemyUnitsKilled, playerState.sequences.enemyUnitsKilled.total); + addArray(g_TeamHelperData[playerState.team].unitsLost, playerState.sequences.unitsLost.total); - for (let type in playerState.statistics.resourcesSold) - g_TeamHelperData[playerState.team].totalSold += playerState.statistics.resourcesSold[type]; + 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 +138,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 +154,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 +193,12 @@ } Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = - formatCaptured(total.constructed, total.destroyed, total.captured, total.lost); + formatSummaryValue(total); } } } -function calculateUnitsTeam(counters) +function calculateUnitsTeam(counters, index) { for (let t in g_Teams) { @@ -227,100 +217,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) - formattedCaption = formatCaptured(total.trained, total.killed, total.captured, total.lost); - else - formattedCaption = formatTrained(total.trained, total.killed, total.lost); + if (w != 0 && w != 6) + delete total.captured; - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formattedCaption; + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatSummaryValue(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 +331,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 = formatSummaryValue(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 +398,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 = formatSummaryValue(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 +457,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 = formatSummaryValue(teamTotal); } } } Index: ps/trunk/binaries/data/mods/public/gui/summary/layout.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/layout.js +++ ps/trunk/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_SummaryTypes[type].color ? '[color="' + g_SummaryTypes[type].color + '"]' + g_SummaryTypes[type].caption + '[/color]' : g_SummaryTypes[type].caption; +} + function resetGeneralPanel() { for (let h = 0; h < g_MaxHeadingTitle; ++h) Index: ps/trunk/binaries/data/mods/public/gui/summary/summary.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/summary.js +++ ps/trunk/binaries/data/mods/public/gui/summary/summary.js @@ -9,20 +9,95 @@ const g_PlayerColorBoxAlpha = " 255"; 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_TypeColors = { + "blue": "196 198 255", + "green": "201 255 200", + "red": "255 213 213", + "yellow": "255 255 157" +} + +/** + * Colors, captions and format used for units, buildings, etc. types + */ +var g_SummaryTypes = { + "percent": { + "color": "", + "caption": "%", + "postfix": "%" + }, + "trained": { + "color": g_TypeColors.green, + "caption": translate("Trained"), + "postfix": " / " + }, + "constructed": { + "color": g_TypeColors.green, + "caption": translate("Constructed"), + "postfix": " / " + }, + "gathered": { + "color": g_TypeColors.green, + "caption": translate("Gathered"), + "postfix": " / " + }, + "sent": { + "color": g_TypeColors.green, + "caption": translate("Sent"), + "postfix": " / " + }, + "bought": { + "color": g_TypeColors.green, + "caption": translate("Bought"), + "postfix": " / " + }, + "income": { + "color": g_TypeColors.green, + "caption": translate("Income"), + "postfix": " / " + }, + "captured": { + "color": g_TypeColors.yellow, + "caption": translate("Captured"), + "postfix": " / " + }, + "destroyed": { + "color": g_TypeColors.blue, + "caption": translate("Destroyed"), + "postfix": "\n" + }, + "killed": { + "color": g_TypeColors.blue, + "caption": translate("Killed"), + "postfix": "\n" + }, + "lost": { + "color": g_TypeColors.red, + "caption": translate("Lost"), + "postfix": "\n" + }, + "used": { + "color": g_TypeColors.red, + "caption": translate("Used"), + "postfix": "\n" + }, + "received": { + "color": g_TypeColors.red, + "caption": translate("Recieved"), + "postfix": "\n" + }, + "sold": { + "color": g_TypeColors.red, + "caption": translate("Sold"), + "postfix": "\n" + }, + "outcome": { + "color": g_TypeColors.red, + "caption": translate("Outcome"), + "postfix": "\n" + } +}; -const g_InfiniteSymbol = "\u221E"; +const g_InfinitySymbol = "\u221E"; var g_CivData = loadCivData(); var g_Teams = []; @@ -47,7 +122,132 @@ 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"); + let chartsHidden = panel.name != "chartsPanelButton"; + generalPanel.hidden = !chartsHidden; + chartsPanel.hidden = chartsHidden; + if (chartsHidden) + updatePanelData(g_ScorePanelsData[panel.name.substr(0, panel.name.length - "PanelButton".length)]); + else + [0, 1].forEach(updateCategoryDropdown); +} + +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) + ); + } + + [0, 1].forEach(i => Engine.GetGUIObjectByName("chart[" + i + "]").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(" "); + + let chart1Part = Engine.GetGUIObjectByName("chart[1]Part"); + let chart1PartSize = chart1Part.size; + chart1PartSize.rright += 50; + chart1PartSize.rleft += 50; + chart1PartSize.right -= 5; + chart1PartSize.left -= 5; + chart1Part.size = chart1PartSize; +} + +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]) + return; + 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]) + return; + 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_SummaryTypes[type].caption); + chartType.list_data = Object.keys(testValue); + chartType.onSelectionChange = function() { + if (!this.list_data[this.selected]) + return; + 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 +326,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 +456,6 @@ g_WithoutTeam -= g_Teams[i] ? g_Teams[i] : 0; } + initCharts(); selectPanel(Engine.GetGUIObjectByName("scorePanelButton")); } Index: ps/trunk/binaries/data/mods/public/gui/summary/summary.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/summary.xml +++ ps/trunk/binaries/data/mods/public/gui/summary/summary.xml @@ -99,6 +99,13 @@ + + selectPanel(this); + + Charts + + + @@ -152,6 +159,48 @@ + + + + + + Category: + + + Category + + + + Value: + + + Value + + + + Type: + + + + + + + + + + Replay Index: ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js +++ ps/trunk/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: ps/trunk/binaries/data/mods/public/simulation/components/StatisticsTracker.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/StatisticsTracker.js +++ ps/trunk/binaries/data/mods/public/simulation/components/StatisticsTracker.js @@ -1,5 +1,7 @@ function StatisticsTracker() {} +const g_UpdateSequenceInterval = 30 * 1000; + 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) { @@ -356,9 +372,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 {number} amount - amount of resource, whick should be added. + * @param {string} specificType - specific type of resource. */ StatisticsTracker.prototype.IncreaseResourceGatheredCounter = function(type, amount, specificType) { @@ -369,8 +385,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 {number} amount - amount of resource, which should be added. */ StatisticsTracker.prototype.IncreaseResourceUsedCounter = function(type, amount) { @@ -493,4 +509,41 @@ 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 probably, 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); Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js @@ -47,7 +47,7 @@ "aiAnalysisInfluenceGroup": resource == "food" ? "ignore" : resource == "wood" ? "abundant" : "sparse" - }), + }) }; var cmp = ConstructComponent(SYSTEM_ENTITY, "GuiInterface"); @@ -70,22 +70,22 @@ AddMock(SYSTEM_ENTITY, IID_PlayerManager, { GetNumPlayers: function() { return 2; }, - GetPlayerByID: function(id) { TS_ASSERT(id === 0 || id === 1); return 100+id; }, + GetPlayerByID: function(id) { TS_ASSERT(id === 0 || id === 1); return 100+id; } }); AddMock(SYSTEM_ENTITY, IID_RangeManager, { GetLosVisibility: function(ent, player) { return "visible"; }, - GetLosCircular: function() { return false; }, + GetLosCircular: function() { return false; } }); AddMock(SYSTEM_ENTITY, IID_TemplateManager, { GetCurrentTemplateName: function(ent) { return "example"; }, - GetTemplate: function(name) { return ""; }, + GetTemplate: function(name) { return ""; } }); AddMock(SYSTEM_ENTITY, IID_Timer, { GetTime: function() { return 0; }, - SetTimeout: function(ent, iid, funcname, time, data) { return 0; }, + SetTimeout: function(ent, iid, funcname, time, data) { return 0; } }); AddMock(100, IID_Player, { @@ -112,7 +112,7 @@ GetDisabledTechnologies: function() { return {}; }, GetSpyCostMultiplier: function() { return 1; }, HasSharedDropsites: function() { return false; }, - HasSharedLos: function() { return false; }, + HasSharedLos: function() { return false; } }); AddMock(100, IID_EntityLimits, { @@ -128,7 +128,7 @@ GetResearchedTechs: function() { return {}; }, GetClassCounts: function() { return {}; }, GetTypeCountsByClass: function() { return {}; }, - GetTechModifications: function() { return {}; }, + GetTechModifications: function() { return {}; } }); AddMock(100, IID_StatisticsTracker, { @@ -139,39 +139,39 @@ "wood": 0, "metal": 0, "stone": 0, - "vegetarianFood": 0, + "vegetarianFood": 0 }, "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; }, IncreaseConstructedBuildingsCounter: function() { return 1; }, - IncreaseBuiltCivCentresCounter: function() { return 1; }, + IncreaseBuiltCivCentresCounter: function() { return 1; } }); AddMock(101, IID_Player, { @@ -198,7 +198,7 @@ GetDisabledTechnologies: function() { return {}; }, GetSpyCostMultiplier: function() { return 1; }, HasSharedDropsites: function() { return false; }, - HasSharedLos: function() { return false; }, + HasSharedLos: function() { return false; } }); AddMock(101, IID_EntityLimits, { @@ -214,7 +214,7 @@ GetResearchedTechs: function() { return {}; }, GetClassCounts: function() { return {}; }, GetTypeCountsByClass: function() { return {}; }, - GetTechModifications: function() { return {}; }, + GetTechModifications: function() { return {}; } }); AddMock(101, IID_StatisticsTracker, { @@ -225,39 +225,39 @@ "wood": 0, "metal": 0, "stone": 0, - "vegetarianFood": 0, + "vegetarianFood": 0 }, "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; }, IncreaseConstructedBuildingsCounter: function() { return 1; }, - IncreaseBuiltCivCentresCounter: function() { return 1; }, + IncreaseBuiltCivCentresCounter: function() { return 1; } }); // Note: property order matters when using TS_ASSERT_UNEVAL_EQUALS, @@ -305,10 +305,10 @@ wood: 0, metal: 0, stone: 0, - vegetarianFood: 0, + vegetarianFood: 0 }, percentMapExplored: 10 - }, + } }, { name: "Player 2", @@ -350,10 +350,10 @@ wood: 0, metal: 0, stone: 0, - vegetarianFood: 0, + vegetarianFood: 0 }, percentMapExplored: 10 - }, + } } ], circularMap: false, @@ -370,140 +370,160 @@ "food": "Food", "metal": "Metal", "stone": "Stone", - "wood": "Wood", + "wood": "Wood" }, "aiInfluenceGroups": { "food": "ignore", "metal": "sparse", "stone": "sparse", - "wood": "abundant", + "wood": "abundant" } - }, + } }); 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 }, - treasuresCollected: 0, - lootCollected: 0, - percentMapExplored: 10, - teamPercentMapExplored: 10, - percentMapControlled: 10, - teamPercentMapControlled: 10, - peakPercentOfMapControlled: 10, - teamPeakPercentOfMapControlled: 10 + "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": [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 } @@ -514,15 +534,15 @@ "food": "Food", "metal": "Metal", "stone": "Stone", - "wood": "Wood", + "wood": "Wood" }, "aiInfluenceGroups": { "food": "ignore", "metal": "sparse", "stone": "sparse", - "wood": "abundant", + "wood": "abundant" } - }, + } }); @@ -537,7 +557,7 @@ GetMaxHitpoints: function() { return 60; }, IsRepairable: function() { return false; }, IsUnhealable: function() { return false; }, - IsUndeletable: function() { return false; }, + IsUndeletable: function() { return false; } }); AddMock(10, IID_Identity, { @@ -545,7 +565,7 @@ GetVisibleClassesList: function() { return ["class3", "class4"]; }, GetRank: function() { return "foo"; }, GetSelectionGroupName: function() { return "Selection Group Name"; }, - HasClass: function() { return true; }, + HasClass: function() { return true; } }); AddMock(10, IID_Position, { @@ -558,7 +578,7 @@ }, IsInWorld: function() { return true; - }, + } }); AddMock(10, IID_ResourceTrickle, { @@ -583,7 +603,7 @@ rank: "foo", classes: ["class1", "class2"], visibleClasses: ["class3", "class4"], - selectionGroupName: "Selection Group Name", + selectionGroupName: "Selection Group Name" }, fogging: null, foundation: null, @@ -607,7 +627,7 @@ maxHitpoints: 60, needsRepair: false, needsHeal: true, - canDelete: true, + canDelete: true }); TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedEntityState(-1, 10), { @@ -634,5 +654,5 @@ "metal": 9 } }, - speed: null, + speed: null });