Show all summary values as a graph

Reviewed by: bb
Differential Revision: https://code.wildfiregames.com/D144
This was SVN commit r19517.
This commit is contained in:
Imarok 2017-05-05 18:52:20 +00:00
parent 2f67b2da61
commit 4a0673e44e
8 changed files with 795 additions and 483 deletions

View file

@ -1467,6 +1467,15 @@ function reportGame()
"resourcesBought"
];
let misc = [
"tradeIncome",
"tributesSent",
"tributesReceived",
"treasuresCollected",
"lootCollected",
"percentMapExplored"
];
let playerStatistics = {};
// Unit Stats
@ -1501,19 +1510,13 @@ function reportGame()
}
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 @@ function reportGame()
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 @@ function reportGame()
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 @@ function reportGame()
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);
}

View file

@ -5,44 +5,27 @@ function resetDataHelpers()
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]';
}
if (typeof values != "object")
return values === Infinity ? g_InfinitySymbol : values;
function formatPercent(divident, divisor)
{
if (!divisor)
return "0%";
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 @@ function cleanGUICaption(team, player, counter, removeLineFeed = true)
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] = {};
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);
}
g_TeamHelperData[playerState.team].food += playerState.statistics.resourcesGathered.food;
g_TeamHelperData[playerState.team].vegetarianFood += playerState.statistics.resourcesGathered.vegetarianFood;
addArray(g_TeamHelperData[playerState.team].food, playerState.sequences.resourcesGathered.food);
addArray(g_TeamHelperData[playerState.team].vegetarianFood, playerState.sequences.resourcesGathered.vegetarianFood);
g_TeamHelperData[playerState.team].femaleCitizen += playerState.statistics.unitsTrained.FemaleCitizen;
g_TeamHelperData[playerState.team].worker += playerState.statistics.unitsTrained.Worker;
addArray(g_TeamHelperData[playerState.team].femaleCitizen, playerState.sequences.unitsTrained.FemaleCitizen);
addArray(g_TeamHelperData[playerState.team].worker, playerState.sequences.unitsTrained.Worker);
g_TeamHelperData[playerState.team].enemyUnitsKilled += playerState.statistics.enemyUnitsKilled.total;
g_TeamHelperData[playerState.team].unitsLost += playerState.statistics.unitsLost.total;
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.statistics.teamPercentMapControlled;
g_TeamHelperData[playerState.team].peakPercentMapControlled = playerState.statistics.teamPeakPercentMapControlled;
g_TeamHelperData[playerState.team].percentMapControlled = playerState.sequences.teamPercentMapControlled;
g_TeamHelperData[playerState.team].peakPercentMapControlled = playerState.sequences.teamPeakPercentMapControlled;
g_TeamHelperData[playerState.team].percentMapExplored = playerState.statistics.teamPercentMapExplored;
g_TeamHelperData[playerState.team].percentMapExplored = playerState.sequences.teamPercentMapExplored;
for (let type in playerState.statistics.resourcesBought)
g_TeamHelperData[playerState.team].totalBought += playerState.statistics.resourcesBought[type];
for (let type in playerState.sequences.resourcesBought)
addArray(g_TeamHelperData[playerState.team].totalBought, playerState.sequences.resourcesBought[type]);
for (let type in playerState.statistics.resourcesSold)
g_TeamHelperData[playerState.team].totalSold += playerState.statistics.resourcesSold[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 @@ function calculateScoreTeam(counters)
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 calculateScoreTeam(counters)
}
}
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 @@ function calculateBuildingsTeam(counters)
}
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 @@ function calculateUnitsTeam(counters)
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 = 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 @@ function calculateResourcesTeam(counters)
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 @@ function calculateMarketTeam(counters)
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 @@ function calculateMiscellaneous(counters)
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);
}
}
}

View file

@ -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 @@ var g_ScorePanelsData = {
"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 @@ var g_ScorePanelsData = {
"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 @@ var g_ScorePanelsData = {
"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 @@ var g_ScorePanelsData = {
"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 @@ var g_ScorePanelsData = {
"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 @@ var g_ScorePanelsData = {
{ "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)

View file

@ -9,20 +9,95 @@ const g_PlayerBoxAlpha = " 50";
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_TypeColors = {
"blue": "196 198 255",
"green": "201 255 200",
"red": "255 213 213",
"yellow": "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, 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"
}
};
// Colors used for gathered and traded resources
const g_IncomeColor = '[color="201 255 200"]';
const g_OutcomeColor = '[color="255 213 213"]';
const g_InfiniteSymbol = "\u221E";
const g_InfinitySymbol = "\u221E";
var g_CivData = loadCivData();
var g_Teams = [];
@ -47,7 +122,132 @@ function selectPanel(panel)
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 @@ function updatePanelData(panelInfo)
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 @@ function init(data)
g_WithoutTeam -= g_Teams[i] ? g_Teams[i] : 0;
}
initCharts();
selectPanel(Engine.GetGUIObjectByName("scorePanelButton"));
}

View file

@ -99,6 +99,13 @@
</object>
</object>
<object name="chartsPanelButton" type="button" sprite="BackgroundTab" style="TabButton" size="762 92 880 120">
<action on="Press">selectPanel(this);</action>
<object type="text" style="ModernLabelText" ghost="true">
<translatableAttribute id="caption">Charts</translatableAttribute>
</object>
</object>
<object name="generalPanel" type="image" sprite="ForegroundBody" size="20 120 100%-20 100%-54">
<object size="0 0 100% 100%-50">
<object name="playerNameHeading" type="text" style="ModernLeftTabLabelText">
@ -152,6 +159,48 @@
</repeat>
</object>
</object>
<object name="chartsPanel" type="image" sprite="ForegroundBody" size="20 120 100%-20 100%-54">
<repeat count="2" var="k">
<object name="chart[k]Part" size="15 0 50%-10 100%-35">
<object type="text" style="ModernLeftLabelText" size="1 6 145 26">
<translatableAttribute id="caption" context="summary chart">Category:</translatableAttribute>
</object>
<object name="chart[k]CategorySelection"
type="dropdown"
style="ModernDropDown"
size="5 28 145 52">
<translatableAttribute id="tooltip" context="summary chart">Category</translatableAttribute>
</object>
<object type="text" style="ModernLeftLabelText" size="151 6 295 26">
<translatableAttribute id="caption" context="summary chart">Value:</translatableAttribute>
</object>
<object name="chart[k]ValueSelection"
type="dropdown"
style="ModernDropDown"
size="155 28 295 52">
<translatableAttribute id="tooltip" context="summary chart">Value</translatableAttribute>
</object>
<object name="chart[k]TypeLabel" type="text" style="ModernLeftLabelText" size="301 6 445 26">
<translatableAttribute id="caption" context="summary chart">Type:</translatableAttribute>
</object>
<object name="chart[k]TypeSelection"
type="dropdown"
style="ModernDropDown"
hidden="true"
size="305 28 445 52">
<translatableAttribute id="tooltip" context="summary chart">Type</translatableAttribute>
</object>
<object type="image" sprite="color: 255 255 255 20" size="0 75 100% 100%"/>
<object name="chart[k]" type="chart" size="5 80 100%-5 100%-5"/>
</object>
</repeat>
<object name="chartLegend" type="text" style="ModernLabelText" size="15 100%-35 100%-15 100%-5"/>
</object>
<object type="button" name="replayButton" style="ModernButtonRed" size="100%-310 100%-48 100%-170 100%-20">
<translatableAttribute id="caption">Replay</translatableAttribute>

View file

@ -194,7 +194,7 @@ GuiInterface.prototype.GetExtendedSimulationState = function()
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;

View file

@ -1,5 +1,7 @@
function StatisticsTracker() {}
const g_UpdateSequenceInterval = 30 * 1000;
StatisticsTracker.prototype.Schema =
"<empty/>";
@ -142,6 +144,10 @@ StatisticsTracker.prototype.Init = function()
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.GetStatistics = function()
};
};
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 @@ StatisticsTracker.prototype.CapturedEntity = function(capturedEntity)
};
/**
* @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 @@ StatisticsTracker.prototype.IncreaseResourceGatheredCounter = function(type, amo
};
/**
* @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 @@ StatisticsTracker.prototype.OnTerritoriesChanged = function(msg)
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);

View file

@ -47,7 +47,7 @@ Resources = {
"aiAnalysisInfluenceGroup":
resource == "food" ? "ignore" :
resource == "wood" ? "abundant" : "sparse"
}),
})
};
var cmp = ConstructComponent(SYSTEM_ENTITY, "GuiInterface");
@ -70,22 +70,22 @@ AddMock(SYSTEM_ENTITY, IID_EndGameManager, {
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 @@ AddMock(100, IID_Player, {
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 @@ AddMock(100, IID_TechnologyManager, {
GetResearchedTechs: function() { return {}; },
GetClassCounts: function() { return {}; },
GetTypeCountsByClass: function() { return {}; },
GetTechModifications: function() { return {}; },
GetTechModifications: function() { return {}; }
});
AddMock(100, IID_StatisticsTracker, {
@ -139,39 +139,39 @@ AddMock(100, IID_StatisticsTracker, {
"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 @@ AddMock(101, IID_Player, {
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 @@ AddMock(101, IID_TechnologyManager, {
GetResearchedTechs: function() { return {}; },
GetClassCounts: function() { return {}; },
GetTypeCountsByClass: function() { return {}; },
GetTechModifications: function() { return {}; },
GetTechModifications: function() { return {}; }
});
AddMock(101, IID_StatisticsTracker, {
@ -225,39 +225,39 @@ AddMock(101, IID_StatisticsTracker, {
"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 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
wood: 0,
metal: 0,
stone: 0,
vegetarianFood: 0,
vegetarianFood: 0
},
percentMapExplored: 10
},
}
},
{
name: "Player 2",
@ -350,10 +350,10 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
wood: 0,
metal: 0,
stone: 0,
vegetarianFood: 0,
vegetarianFood: 0
},
percentMapExplored: 10
},
}
}
],
circularMap: false,
@ -370,140 +370,160 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
"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 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
"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 @@ AddMock(10, IID_Health, {
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 @@ AddMock(10, IID_Identity, {
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 @@ AddMock(10, IID_Position, {
},
IsInWorld: function() {
return true;
},
}
});
AddMock(10, IID_ResourceTrickle, {
@ -583,7 +603,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetEntityState(-1, 10), {
rank: "foo",
classes: ["class1", "class2"],
visibleClasses: ["class3", "class4"],
selectionGroupName: "Selection Group Name",
selectionGroupName: "Selection Group Name"
},
fogging: null,
foundation: null,
@ -607,7 +627,7 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetEntityState(-1, 10), {
maxHitpoints: 60,
needsRepair: false,
needsHeal: true,
canDelete: true,
canDelete: true
});
TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedEntityState(-1, 10), {
@ -634,5 +654,5 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedEntityState(-1, 10), {
"metal": 9
}
},
speed: null,
speed: null
});