Ticket #4554: resource_graph.patch
File resource_graph.patch, 62.6 KB (added by , 3 years ago) |
---|
-
binaries/data/mods/public/gui/credits/texts/programming.json
1 1 { 2 2 "Title": "Programming", 3 3 "Content": [ 4 4 { 5 5 "Title": "Programming managers", 6 6 "List": [ 7 7 { "nick": "Acumen", "name": "Stuart Walpole" }, 8 8 { "nick": "Dak Lozar", "name": "Dave Loeser" }, 9 9 { "nick": "h20", "name": "Daniel Wilhelm" }, 10 10 { "nick": "Janwas", "name": "Jan Wassenberg" }, 11 11 { "nick": "Raj", "name": "Raj Sharma" } 12 12 ] 13 13 }, 14 14 { 15 15 "Subtitle": "Special thanks to", 16 16 "List": [ 17 17 { "nick": "leper", "name": "Georg Kilzer" }, 18 18 { "nick": "Ykkrosh", "name": "Philip Taylor" } 19 19 ] 20 20 }, 21 21 { 22 22 "List": [ 23 23 { "nick": "01d55" }, 24 24 { "nick": "aBothe", "name": "Alexander Bothe" }, 25 25 { "nick": "Acumen", "name": "Stuart Walpole" }, 26 26 { "nick": "adrian", "name": "Adrian Boguszewszki" }, 27 27 { "name": "Adrian Fatol" }, 28 28 { "nick": "AI-Amsterdam" }, 29 29 { "nick": "Alan", "name": "Alan Kemp" }, 30 30 { "nick": "Alex", "name": "Alexander Yakobovich" }, 31 31 { "nick": "alpha123", "name": "Peter P. Cannici" }, 32 32 { "nick": "Ampaex", "name": "Antonio Vazquez" }, 33 33 { "name": "André Puel" }, 34 34 { "nick": "andy5995", "name": "Andy Alt" }, 35 35 { "nick": "Angen" }, 36 36 { "nick": "Arfrever", "name": "Arfrever Frehtes Taifersar Arahesis" }, 37 37 { "nick": "ArnH", "name": "Arno Hemelhof" }, 38 38 { "nick": "Aurium", "name": "Aurélio Heckert" }, 39 39 { "nick": "badmadblacksad", "name": "Martin F" }, 40 40 { "nick": "badosu", "name": "Amadeus Folego" }, 41 41 { "nick": "bb", "name": "Bouke Jansen" }, 42 42 { "nick": "Ben", "name": "Ben Vinegar" }, 43 43 { "nick": "Bird" }, 44 44 { "nick": "Blue", "name": "Richard Welsh" }, 45 45 { "nick": "bmwiedemann" }, 46 46 { "nick": "boeseRaupe", "name": "Michael Kluge" }, 47 47 { "nick": "bog_dan_ro", "name": "BogDan Vatra" }, 48 48 { "nick": "Bonk", "name": "Christopher Ebbert" }, 49 49 { "nick": "Boudica" }, 50 50 { "nick": "Caius", "name": "Lars Kemmann" }, 51 51 { "nick": "Calefaction", "name": "Matt Holmes" }, 52 52 { "nick": "Calvinh", "name": "Carl-Johan Höiby" }, 53 53 { "nick": "causative", "name": "Bart Parkis" }, 54 54 { "name": "Cédric Houbart" }, 55 55 { "nick": "Chakakhan", "name": "Kenny Long" }, 56 56 { "nick": "Clockwork-Muse", "name": "Stephen A. Imhoff" }, 57 57 { "nick": "Cracker78", "name": "Chad Heim" }, 58 58 { "nick": "Crynux", "name": "Stephen J. Fewer" }, 59 59 { "nick": "cwprogger" }, 60 60 { "nick": "cygal", "name": "Quentin Pradet" }, 61 61 { "nick": "Dak Lozar", "name": "Dave Loeser" }, 62 62 { "nick": "dalerank", "name": "Sergey Kushnirenko" }, 63 63 { "nick": "dan", "name": "Dan Strandberg" }, 64 64 { "nick": "DanCar", "name": "Daniel Cardenas" }, 65 65 { "nick": "danger89", "name": "Melroy van den Berg" }, 66 66 { "name": "Daniel Trevitz" }, 67 67 { "nick": "Dariost", "name": "Dario Ostuni" }, 68 68 { "nick": "Dave", "name": "David Protasowski" }, 69 69 { "nick": "dax", "name": "Dacian Fiordean" }, 70 70 { "nick": "deebee", "name": "Deepak Anthony" }, 71 71 { "nick": "Deiz" }, 72 72 { "nick": "Dietger", "name": "Dietger van Antwerpen" }, 73 73 { "nick": "DigitalSeraphim", "name": "Nick Owens" }, 74 74 { "nick": "dp304" }, 75 75 { "nick": "dpiquet", "name": "Damien Piquet" }, 76 76 { "nick": "dumbo" }, 77 77 { "nick": "Dunedan", "name": "Daniel Roschka" }, 78 78 { "nick": "dvangennip", "name": "Doménique" }, 79 79 { "nick": "Echelon9", "name": "Rhys Kidd" }, 80 80 { "nick": "echotangoecho" }, 81 81 { "nick": "eihrul", "name": "Lee Salzman" }, 82 82 { "nick": "elexis", "name": "Alexander Heinsius" }, 83 83 { "nick": "EmjeR", "name": "Matthijs de Rijk" }, 84 84 { "nick": "EMontana" }, 85 85 { "nick": "ericb" }, 86 86 { "nick": "evanssthomas", "name": "Evans Thomas" }, 87 87 { "nick": "Evulant", "name": "Alexander S." }, 88 88 { "nick": "fabio", "name": "Fabio Pedretti" }, 89 89 { "nick": "falsevision", "name": "Mahdi Khodadadifard" }, 90 90 { "nick": "fatherbushido", "name": "Nicolas Tisserand" }, 91 91 { "nick": "fcxSanya", "name": "Alexander Olkhovskiy" }, 92 92 { "nick": "FeXoR", "name": "Florian Finke" }, 93 93 { "nick": "Fire Giant", "name": "Malte Schwarzkopf" }, 94 94 { "name": "Fork AD" }, 95 95 { "nick": "fpre", "name": "Frederick Stallmeyer" }, 96 96 { "nick": "Freagarach" }, 97 97 { "nick": "freenity", "name": "Anton Galitch" }, 98 98 { "nick": "Gallaecio", "name": "Adrián Chaves" }, 99 99 { "nick": "gbish (aka Iny)", "name": "Grant Bishop" }, 100 100 { "nick": "Gee", "name": "Gustav Larsson" }, 101 101 { "nick": "Gentz", "name": "Hal Gentz" }, 102 102 { "nick": "gerbilOFdoom" }, 103 103 { "nick": "godlikeldh" }, 104 104 { "nick": "greybeard", "name": "Joe Cocovich" }, 105 105 { "nick": "grillaz" }, 106 106 { "nick": "Grugnas", "name": "Giuseppe Tranchese" }, 107 107 { "nick": "gudo" }, 108 108 { "nick": "Guuts", "name": "Matthew Guttag" }, 109 109 { "nick": "h20", "name": "Daniel Wilhelm" }, 110 110 { "nick": "Hannibal_Barca", "name": "Clive Juhász S." }, 111 111 { "nick": "Haommin" }, 112 112 { "nick": "happyconcepts", "name": "Ben Bird" }, 113 113 { "nick": "historic_bruno", "name": "Ben Brian" }, 114 114 { "nick": "idanwin" }, 115 115 { "nick": "Imarok", "name": "J. S." }, 116 116 { "nick": "Inari" }, 117 117 { "nick": "infyquest", "name": "Vijay Kiran Kamuju" }, 118 118 { "nick": "irishninja", "name": "Brian Broll" }, 119 119 { "nick": "IronNerd", "name": "Matthew McMullan" }, 120 120 { "nick": "Itms", "name": "Nicolas Auvray" }, 121 121 { "nick": "Jaison", "name": "Marco tom Suden" }, 122 122 { "nick": "jammus", "name": "James Scott" }, 123 123 { "nick": "Janwas", "name": "Jan Wassenberg" }, 124 124 { "nick": "javiergodas", "name": "Javier Godas Vieitez" }, 125 125 { "nick": "Jgwman" }, 126 126 { "nick": "JonBaer", "name": "Jon Baer" }, 127 127 { "nick": "Josh", "name": "Joshua J. Bakita" }, 128 128 { "nick": "joskar", "name": "Johnny Oskarsson" }, 129 129 { "nick": "jP_wanN", "name": "Jonas Platte" }, 130 130 { "nick": "Jubalbarca", "name": "James Baillie" }, 131 131 { "nick": "JubJub", "name": "Sebastian Vetter" }, 132 132 { "nick": "jurgemaister" }, 133 133 { "nick": "kabzerek", "name": "Grzegorz Kabza" }, 134 134 { "nick": "Kai", "name": "Kai Chen" }, 135 135 { "name": "Kareem Ergawy" }, 136 136 { "nick": "kevmo", "name": "Kevin Caffrey" }, 137 137 { "nick": "kezz", "name": "Graeme Kerry" }, 138 138 { "nick": "kingadami", "name": "Adam Winsor" }, 139 139 { "nick": "kingbasil", "name": "Giannis Fafalios" }, 140 140 { "nick": "Krinkle", "name": "Timo Tijhof" }, 141 141 { "nick": "lafferjm", "name": "Justin Lafferty" }, 142 142 { "nick": "LeanderH", "name": "Leander Hemelhof" }, 143 143 { "nick": "leper", "name": "Georg Kilzer" }, 144 144 { "nick": "Link Mauve", "name": "Emmanuel Gil Peyrot" }, 145 145 { "nick": "LittleDev" }, 146 146 { "nick": "livingaftermidnight", "name": "Will Dull" }, 147 147 { "nick": "lonehawk", "name": "Vignesh Krishnan" }, 148 148 { "nick": "Louhike" }, 149 149 { "nick": "lsdh" }, 150 150 { "nick": "Ludovic", "name": "Ludovic Rousseau" }, 151 151 { "nick": "luiko", "name": "Luis Carlos Garcia Barajas" }, 152 152 { "nick": "m0l0t0ph", "name": "Christoph Gielisch" }, 153 153 { "nick": "madmax", "name": "Abhijit Nandy" }, 154 154 { "nick": "madpilot", "name": "Guido Falsi" }, 155 155 { "nick": "mammadori", "name": "Marco Amadori" }, 156 156 { "nick": "markcho" }, 157 157 { "nick": "MarkT", "name": "Mark Thompson" }, 158 158 { "nick": "Markus" }, 159 159 { "nick": "Mate-86", "name": "Mate Kovacs" }, 160 160 { "nick": "Matei", "name": "Matei Zaharia" }, 161 161 { "nick": "MattDoerksen", "name": "Matt Doerksen" }, 162 162 { "nick": "mattlott", "name": "Matt Lott" }, 163 163 { "nick": "maveric", "name": "Anton Protko" }, 164 164 { "nick": "Micnasty", "name": "Travis Gorkin" }, 165 165 { "name": "Mikołaj \"Bajter\" Korcz" }, 166 166 { "nick": "mimo" }, 167 167 { "nick": "mk12", "name": "Mitchell Kember" }, 168 168 { "nick": "mmayfield45", "name": "Michael Mayfield" }, 169 169 { "nick": "mmoanis", "name": "Mohamed Moanis" }, 170 170 { "nick": "Molotov", "name": "Dario Alvarez" }, 171 171 { "nick": "mpmoreti", "name": "Marcos Paulo Moreti" }, 172 172 { "nick": "mreiland", "name": "Michael Reiland" }, 173 173 { "nick": "myconid" }, 174 174 { "nick": "nani", "name": "S. N." }, 175 175 { "nick": "nd3c3nt", "name": "Gavin Fowler" }, 176 176 { "nick": "nephele" }, 177 177 { "nick": "Nescio" }, 178 178 { "nick": "niektb", "name": "Niek ten Brinke" }, 179 179 { "nick": "nikagra", "name": "Mikita Hradovich" }, 180 180 { "nick": "njm" }, 181 181 { "nick": "NoMonkey", "name": "John Mena" }, 182 182 { "nick": "norsnor" }, 183 183 { "nick": "notpete", "name": "Rich Cross" }, 184 184 { "nick": "nwtour" }, 185 185 { "nick": "odoaker", "name": "Ágoston Sipos" }, 186 186 { "nick": "Offensive ePeen", "name": "Jared Ryan Bills" }, 187 187 { "nick": "Ols", "name": "Oliver Whiteman" }, 188 188 { "nick": "olsner", "name": "Simon Brenner" }, 189 189 { "nick": "OptimusShepard", "name": "Pirmin Stanglmeier" }, 190 190 { "nick": "otero" }, 191 191 { "nick": "Palaxin", "name": "David A. Freitag" }, 192 192 { "name": "Paul Withers" }, 193 193 { "nick": "paulobezerr", "name": "Paulo George Gomes Bezerra" }, 194 194 { "nick": "pcpa", "name": "Paulo Andrade" }, 195 195 { "nick": "Pendingchaos" }, 196 196 { "nick": "PeteVasi", "name": "Pete Vasiliauskas" }, 197 197 { "nick": "pilino1234" }, 198 198 { "nick": "PingvinBetyar", "name": "Schronk Tamás" }, 199 199 { "nick": "plugwash", "name": "Peter Michael Green" }, 200 200 { "nick": "Polakrity" }, 201 201 { "nick": "Poya", "name": "Poya Manouchehri" }, 202 202 { "nick": "prefect", "name": "Nicolai Hähnle" }, 203 203 { "nick": "Prodigal Son" }, 204 204 { "nick": "pstumpf", "name": "Pascal Stumpf" }, 205 205 { "nick": "pyrolink", "name": "Andrew Decker" }, 206 206 { "nick": "quantumstate", "name": "Jonathan Waller" }, 207 207 { "nick": "QuickShot", "name": "Walter Krawec" }, 208 208 { "nick": "quonter" }, 209 209 { "nick": "qwertz" }, 210 210 { "nick": "Radagast" }, 211 211 { "nick": "Raj", "name": "Raj Sharma" }, 212 212 { "nick": "ramtzok1", "name": "Ram" }, 213 213 { "nick": "rapidelectron", "name": "Christian Weihsbach" }, 214 214 { "nick": "r-a-sattarov", "name": "Ramil Sattarov" }, 215 215 { "nick": "RedFox", "name": "Jorma Rebane" }, 216 216 { "nick": "RefinedCode" }, 217 217 { "nick": "Riemer" }, 218 218 { "name": "Rolf Sievers" }, 219 219 { "nick": "s0600204", "name": "Matthew Norwood" }, 220 220 { "nick": "sacha_vrand", "name": "Sacha Vrand" }, 221 221 { "nick": "SafaAlfulaij" }, 222 222 { "name": "Samuel Guarnieri" }, 223 223 { "nick": "Samulis", "name": "Sam Gossner" }, 224 224 { "nick": "Sandarac" }, 225 225 { "nick": "sanderd17", "name": "Sander Deryckere" }, 226 226 { "nick": "sathyam", "name": "Sathyam Vellal" }, 227 227 { "nick": "sbirmi", "name": "Sharad Birmiwal" }, 228 228 { "nick": "sbte", "name": "Sven Baars" }, 229 229 { "nick": "scroogie", "name": "André Gemünd" }, 230 230 { "nick": "scythetwirler", "name": "Casey X." }, 231 231 { "nick": "serveurix" }, 232 232 { "nick": "Shane", "name": "Shane Grant" }, 233 233 { "nick": "shh" }, 234 234 { "nick": "Silk", "name": "Josh Godsiff" }, 235 235 { "nick": "silure" }, 236 236 { "nick": "Simikolon", "name": "Yannick & Simon" }, 237 237 { "nick": "smiley", "name": "M. L." }, 238 238 { "nick": "Spahbod", "name": "Omid Davoodi" }, 239 239 { "nick": "Stan", "name": "Stanislas Dolcini" }, 240 240 { "nick": "Stefan" }, 241 241 { "nick": "StefanBruens", "name": "Stefan Brüns" }, 242 242 { "nick": "stilz", "name": "Sławomir Zborowski" }, 243 243 { "nick": "stwf", "name": "Steven Fuchs" }, 244 244 { "nick": "svott", "name": "Sven Ott" }, 245 245 { "nick": "t4nk004" }, 246 246 { "nick": "tau" }, 247 247 { "nick": "tbm", "name": "Martin Michlmayr" }, 248 248 { "nick": "Teiresias" }, 249 249 { "nick": "temple" }, 250 250 { "nick": "texane" }, 251 251 { "nick": "thamlett", "name": "Timothy Hamlett" }, 252 252 { "nick": "thedrunkyak", "name": "Dan Fuhr" }, 253 253 { "nick": "Tobbi" }, 254 { "nick": "Toonijn", "name": "Toon Baeyens" }, 254 255 { "nick": "TrinityDeath", "name": "Jethro Lu" }, 255 256 { "nick": "triumvir", "name": "Corin Schedler" }, 256 257 { "nick": "trompetin17", "name": "Juan Guillermo" }, 257 258 { "nick": "user1", "name": "A. C." }, 258 259 { "nick": "usey11" }, 259 260 { "nick": "vincent_c", "name": "Vincent Cheng" }, 260 261 { "nick": "vinhig", "name": "Vincent Higginson" }, 261 262 { "nick": "vladislavbelov", "name": "Vladislav Belov" }, 262 263 { "nick": "voroskoi" }, 263 264 { "nick": "vts", "name": "Jeroen DR" }, 264 265 { "nick": "wacko", "name": "Andrew Spiering" }, 265 266 { "nick": "WhiteTreePaladin", "name": "Brian Ashley" }, 266 267 { "nick": "wraitii", "name": "Lancelot de Ferrière le Vayer" }, 267 268 { "nick": "Xentelian", "name": "Mark Strawson" }, 268 269 { "nick": "Xienen", "name": "Dayle Flowers" }, 269 270 { "nick": "xtizer", "name": "Matt Green" }, 270 271 { "nick": "yashi", "name": "Yasushi Shoji" }, 271 272 { "nick": "Ykkrosh", "name": "Philip Taylor" }, 272 273 { "nick": "Yves" }, 273 274 { "nick": "Zeusthor", "name": "Jeffrey Tavares" }, 274 275 { "nick": "zoot" }, 275 276 { "nick": "zsol", "name": "Zsolt Dollenstein" }, 276 277 { "nick": "ztamas", "name": "Tamas Zolnai" }, 277 278 { "nick": "Zyi", "name": "Charles De Meulenaer" } 278 279 ] 279 280 } 280 281 ] 281 282 } -
binaries/data/mods/public/gui/summary/counters.js
1 1 var g_TeamHelperData = []; 2 2 3 3 function calculatePercent(divident, divisor) 4 4 { 5 5 return { "percent": divisor ? Math.floor(100 * divident / divisor) : 0 }; 6 6 } 7 7 8 8 function calculateRatio(divident, divisor) 9 9 { 10 10 return divident ? +((divident / divisor).toFixed(2)) : 0; 11 11 } 12 12 13 13 function formatSummaryValue(values) 14 14 { 15 15 if (typeof values != "object") 16 16 return values === Infinity ? g_InfinitySymbol : values; 17 17 18 18 let ret = ""; 19 19 for (let type in values) 20 ret += (g_SummaryTypes[type].color ? 21 coloredText(values[type], g_SummaryTypes[type].color) : 22 values[type]) + g_SummaryTypes[type].postfix; 20 if(g_SummaryTypes[type].hide !== true) 21 ret += (g_SummaryTypes[type].color ? 22 coloredText(values[type], g_SummaryTypes[type].color) : 23 values[type]) + g_SummaryTypes[type].postfix; 23 24 return ret; 24 25 } 25 26 26 27 function getPlayerValuesPerTeam(team, index, type, counters, headings) 27 28 { 28 29 let fn = counters[headings.map(heading => heading.identifier).indexOf(type) - 1].fn; 29 30 return g_Teams[team].map(player => fn(g_GameData.sim.playerStates[player], index, type)); 30 31 } 31 32 32 33 function updateCountersPlayer(playerState, counters, headings, idGUI, index) 33 34 { 34 35 for (let n in counters) 35 36 { 36 37 let fn = counters[n].fn; 37 38 Engine.GetGUIObjectByName(idGUI + "[" + n + "]").caption = 38 39 formatSummaryValue(fn && fn(playerState, index, headings[+n + 1].identifier)); 39 40 } 40 41 } 41 42 42 43 function updateCountersTeam(teamFn, counters, headings, index) 43 44 { 44 45 for (let team in g_Teams) 45 46 { 46 47 if (team == -1) 47 48 continue; 48 49 49 50 for (let n in counters) 50 51 Engine.GetGUIObjectByName("valueDataTeam[" + team + "][" + n + "]").caption = 51 52 formatSummaryValue(teamFn(team, index, headings[+n + 1].identifier, counters, headings)); 52 53 } 53 54 } 54 55 55 56 /** 56 57 * Add two objects property-wise and writes the result to obj1. 57 58 * So summaryAddObject([1, 2], [7, 42]) will result in [8, 44] and 58 59 * summaryAddObject({ "f": 3, "o", 5 }, { "f": -1, "o", 42 }) will result in { "f": 2, "o", 47 }. 59 60 * 60 61 * @param {Object} obj1 - First summand object. This will be set to the sum of both. 61 62 * @param {Object} obj2 - Second summand object. 62 63 */ 63 64 function summaryAddObject(obj1, obj2) 64 65 { 65 66 for (let p in obj1) 66 67 obj1[p] += obj2[p]; 67 68 } 68 69 69 70 /** 70 71 * The sum of all elements of an array. So summaryArraySum([1, 2]) will be 3 and 71 72 * summaryArraySum([{ "f": 3, "o", 5 }, { "f": -1, "o", 42 }]) will be { "f": 2, "o", 47 }. 72 73 * 73 74 * @param {Array} array - The array to sum up. 74 75 * @returns the sum of all elements. 75 76 */ 76 77 function summaryArraySum(array) 77 78 { 78 79 return array.reduce((sum, val) => { 79 80 if (typeof sum != "object") 80 81 return sum + val; 81 82 summaryAddObject(sum, val); 82 83 return sum; 83 84 }); 84 85 } 85 86 86 87 function calculateTeamCounterDataHelper() 87 88 { 88 89 for (let i = 0; i < g_PlayerCount; ++i) 89 90 { 90 91 let playerState = g_GameData.sim.playerStates[i + 1]; 91 92 92 93 if (!g_TeamHelperData[playerState.team]) 93 94 { 94 95 g_TeamHelperData[playerState.team] = {}; 95 96 for (let value of ["food", "vegetarianFood", "femaleCitizen", "worker", "enemyUnitsKilled", 96 97 "unitsLost", "mapControl", "mapControlPeak", 97 98 "mapExploration", "totalBought", "totalSold"]) 98 99 g_TeamHelperData[playerState.team][value] = new Array(playerState.sequences.time.length).fill(0); 99 100 } 100 101 101 102 summaryAddObject(g_TeamHelperData[playerState.team].food, playerState.sequences.resourcesGathered.food); 102 103 summaryAddObject(g_TeamHelperData[playerState.team].vegetarianFood, playerState.sequences.resourcesGathered.vegetarianFood); 103 104 104 105 summaryAddObject(g_TeamHelperData[playerState.team].femaleCitizen, playerState.sequences.unitsTrained.FemaleCitizen); 105 106 summaryAddObject(g_TeamHelperData[playerState.team].worker, playerState.sequences.unitsTrained.Worker); 106 107 107 108 summaryAddObject(g_TeamHelperData[playerState.team].enemyUnitsKilled, playerState.sequences.enemyUnitsKilled.total); 108 109 summaryAddObject(g_TeamHelperData[playerState.team].unitsLost, playerState.sequences.unitsLost.total); 109 110 110 111 g_TeamHelperData[playerState.team].mapControl = playerState.sequences.teamPercentMapControlled; 111 112 g_TeamHelperData[playerState.team].mapControlPeak = playerState.sequences.teamPeakPercentMapControlled; 112 113 113 114 g_TeamHelperData[playerState.team].mapExploration = playerState.sequences.teamPercentMapExplored; 114 115 115 116 for (let type in playerState.sequences.resourcesBought) 116 117 summaryAddObject(g_TeamHelperData[playerState.team].totalBought, playerState.sequences.resourcesBought[type]); 117 118 118 119 for (let type in playerState.sequences.resourcesSold) 119 120 summaryAddObject(g_TeamHelperData[playerState.team].totalSold, playerState.sequences.resourcesSold[type]); 120 121 } 121 122 } 122 123 123 124 /** 124 125 * Keep this in sync with the score computation in session/ for the lobby rating reports! 125 126 */ 126 127 function calculateEconomyScore(playerState, index) 127 128 { 128 129 let total = 0; 129 130 130 131 // Notice that this skips the vegetarianFood property of resourcesGathered 131 132 for (let type of g_ResourceData.GetCodes()) 132 133 total += playerState.sequences.resourcesGathered[type][index]; 133 134 134 135 total += playerState.sequences.tradeIncome[index]; 135 136 return Math.round(total / 10); 136 137 } 137 138 138 139 /** 139 140 * Keep this in sync with the score computation in session/ for the lobby rating reports! 140 141 */ 141 142 function calculateMilitaryScore(playerState, index) 142 143 { 143 144 return Math.round((playerState.sequences.enemyUnitsKilledValue[index] + 144 145 playerState.sequences.unitsCapturedValue[index] + 145 146 playerState.sequences.enemyBuildingsDestroyedValue[index] + 146 147 playerState.sequences.buildingsCapturedValue[index]) / 10); 147 148 } 148 149 149 150 /** 150 151 * Keep this in sync with the score computation in session/ for the lobby rating reports! 151 152 */ 152 153 function calculateExplorationScore(playerState, index) 153 154 { 154 155 return playerState.sequences.percentMapExplored[index] * 10; 155 156 } 156 157 157 158 /** 158 159 * Keep this in sync with the score computation in session/ for the lobby rating reports! 159 160 */ 160 161 function calculateScoreTotal(playerState, index) 161 162 { 162 163 return calculateEconomyScore(playerState, index) + 163 164 calculateMilitaryScore(playerState, index) + 164 165 calculateExplorationScore(playerState, index); 165 166 } 166 167 167 168 function calculateScoreTeam(team, index, type, counters, headings) 168 169 { 169 170 if (type == "explorationScore") 170 171 return g_TeamHelperData[team].mapExploration[index] * 10; 171 172 if (type == "totalScore") 172 173 return ["economyScore", "militaryScore", "explorationScore"].map( 173 174 t => calculateScoreTeam(team, index, t, counters, headings)).reduce( 174 175 (sum, value) => sum + value); 175 176 176 177 return summaryArraySum(getPlayerValuesPerTeam(team, index, type, counters, headings)); 177 178 } 178 179 179 180 function calculateBuildings(playerState, index, type) 180 181 { 181 182 return { 182 183 "constructed": playerState.sequences.buildingsConstructed[type][index], 183 184 "destroyed": playerState.sequences.enemyBuildingsDestroyed[type][index], 184 185 "captured": playerState.sequences.buildingsCaptured[type][index], 185 186 "lost": playerState.sequences.buildingsLost[type][index] 186 187 }; 187 188 } 188 189 189 190 function calculateBuildingsTeam(team, index, type, counters, headings) 190 191 { 191 192 return summaryArraySum(getPlayerValuesPerTeam(team, index, type, counters, headings)); 192 193 } 193 194 194 195 function calculateUnitsTeam(team, index, type, counters, headings) 195 196 { 196 197 return summaryArraySum(getPlayerValuesPerTeam(team, index, type, counters, headings)); 197 198 } 198 199 199 200 function calculateUnitsWithCaptured(playerState, index, type) 200 201 { 201 202 return { 203 "alive": playerState.sequences.unitsAlive[type][index], 202 204 "trained": playerState.sequences.unitsTrained[type][index], 203 205 "killed": playerState.sequences.enemyUnitsKilled[type][index], 204 206 "captured": playerState.sequences.unitsCaptured[type][index], 205 207 "lost": playerState.sequences.unitsLost[type][index] 206 208 }; 207 209 } 208 210 209 211 function calculateUnits(playerState, index, type) 210 212 { 211 213 return { 214 "alive": playerState.sequences.unitsAlive[type][index], 212 215 "trained": playerState.sequences.unitsTrained[type][index], 213 216 "killed": playerState.sequences.enemyUnitsKilled[type][index], 214 217 "lost": playerState.sequences.unitsLost[type][index] 215 218 }; 216 219 } 217 220 218 221 function calculateResources(playerState, index, type) 219 222 { 220 223 return { 224 "count": playerState.sequences.resourcesCount[type][index], 221 225 "gathered": playerState.sequences.resourcesGathered[type][index], 222 226 "used": playerState.sequences.resourcesUsed[type][index] - playerState.sequences.resourcesSold[type][index] 223 227 }; 224 228 } 225 229 226 230 function calculateTotalResources(playerState, index) 227 231 { 228 232 let totalGathered = 0; 229 233 let totalUsed = 0; 234 let totalCount = 0; 230 235 231 236 for (let type of g_ResourceData.GetCodes()) 232 237 { 238 totalCount += playerState.sequences.resourcesCount[type][index]; 233 239 totalGathered += playerState.sequences.resourcesGathered[type][index]; 234 240 totalUsed += playerState.sequences.resourcesUsed[type][index] - playerState.sequences.resourcesSold[type][index]; 235 241 } 236 242 237 return { " gathered": totalGathered, "used": totalUsed };243 return { "count": totalCount, "gathered": totalGathered, "used": totalUsed }; 238 244 } 239 245 240 246 function calculateTreasureCollected(playerState, index) 241 247 { 242 248 return playerState.sequences.treasuresCollected[index]; 243 249 } 244 250 245 251 function calculateLootCollected(playerState, index) 246 252 { 247 253 return playerState.sequences.lootCollected[index]; 248 254 } 249 255 250 256 function calculateTributeSent(playerState, index) 251 257 { 252 258 return { 253 259 "sent": playerState.sequences.tributesSent[index], 254 260 "received": playerState.sequences.tributesReceived[index] 255 261 }; 256 262 } 257 263 258 264 function calculateLivestockTrained(playerState, index) 259 265 { 260 266 return playerState.sequences.unitsTrained.Domestic[index]; 261 267 } 262 268 263 269 function calculateResourcesTeam(team, index, type, counters, headings) 264 270 { 265 271 return summaryArraySum(getPlayerValuesPerTeam(team, index, type, counters, headings)); 266 272 } 267 273 268 274 function calculateResourceExchanged(playerState, index, type) 269 275 { 270 276 return { 271 277 "bought": playerState.sequences.resourcesBought[type][index], 272 278 "sold": playerState.sequences.resourcesSold[type][index] 273 279 }; 274 280 } 275 281 276 282 function calculateBarterEfficiency(playerState, index) 277 283 { 278 284 let totalBought = 0; 279 285 let totalSold = 0; 280 286 281 287 for (let type in playerState.sequences.resourcesBought) 282 288 totalBought += playerState.sequences.resourcesBought[type][index]; 283 289 284 290 for (let type in playerState.sequences.resourcesSold) 285 291 totalSold += playerState.sequences.resourcesSold[type][index]; 286 292 287 293 return calculatePercent(totalBought, totalSold); 288 294 } 289 295 290 296 function calculateTradeIncome(playerState, index) 291 297 { 292 298 return playerState.sequences.tradeIncome[index]; 293 299 } 294 300 295 301 function calculateMarketTeam(team, index, type, counters, headings) 296 302 { 297 303 if (type == "barterEfficency") 298 304 return calculatePercent(g_TeamHelperData[team].totalBought[index], g_TeamHelperData[team].totalSold[index]); 299 305 300 306 return summaryArraySum(getPlayerValuesPerTeam(team, index, type, counters, headings)); 301 307 } 302 308 303 309 function calculateVegetarianRatio(playerState, index) 304 310 { 305 311 return calculatePercent( 306 312 playerState.sequences.resourcesGathered.vegetarianFood[index], 307 313 playerState.sequences.resourcesGathered.food[index]); 308 314 } 309 315 310 316 function calculateFeminization(playerState, index) 311 317 { 312 318 return calculatePercent( 313 319 playerState.sequences.unitsTrained.FemaleCitizen[index], 314 320 playerState.sequences.unitsTrained.Worker[index]); 315 321 } 316 322 317 323 function calculateKillDeathRatio(playerState, index) 318 324 { 319 325 return calculateRatio( 320 326 playerState.sequences.enemyUnitsKilled.total[index], 321 327 playerState.sequences.unitsLost.total[index]); 322 328 } 323 329 324 330 function calculateMapExploration(playerState, index) 325 331 { 326 332 return { "percent": playerState.sequences.percentMapExplored[index] }; 327 333 } 328 334 329 335 function calculateMapFinalControl(playerState, index) 330 336 { 331 337 return { "percent": playerState.sequences.percentMapControlled[index] }; 332 338 } 333 339 334 340 function calculateMapPeakControl(playerState, index) 335 341 { 336 342 return { "percent": playerState.sequences.peakPercentMapControlled[index] }; 337 343 } 338 344 339 345 function calculateMiscellaneousTeam(team, index, type, counters, headings) 340 346 { 341 347 if (type == "vegetarianRatio") 342 348 return calculatePercent(g_TeamHelperData[team].vegetarianFood[index], g_TeamHelperData[team].food[index]); 343 349 344 350 if (type == "feminization") 345 351 return calculatePercent(g_TeamHelperData[team].femaleCitizen[index], g_TeamHelperData[team].worker[index]); 346 352 347 353 if (type == "killDeath") 348 354 return calculateRatio(g_TeamHelperData[team].enemyUnitsKilled[index], g_TeamHelperData[team].unitsLost[index]); 349 355 350 356 if (type == "bribes") 351 357 return summaryArraySum(getPlayerValuesPerTeam(team, index, type, counters, headings)); 352 358 353 359 return { "percent": g_TeamHelperData[team][type][index] }; 354 360 } 355 361 356 362 function calculateBribes(playerState, index, type) 357 363 { 358 364 return { 359 365 "succeeded": playerState.sequences.successfulBribes[index], 360 366 "failed": playerState.sequences.failedBribes[index] 361 367 }; 362 368 } -
binaries/data/mods/public/gui/summary/summary.js
1 1 const g_CivData = loadCivData(false, false); 2 2 3 3 var g_ScorePanelsData; 4 4 5 5 var g_MaxHeadingTitle = 9; 6 6 var g_LongHeadingWidth = 250; 7 7 var g_PlayerBoxYSize = 40; 8 8 var g_PlayerBoxGap = 2; 9 9 var g_PlayerBoxAlpha = 50; 10 10 var g_TeamsBoxYStart = 40; 11 11 12 12 var g_TypeColors = { 13 13 "blue": "196 198 255", 14 14 "green": "201 255 200", 15 15 "red": "255 213 213", 16 16 "yellow": "255 255 157" 17 17 }; 18 18 19 19 /** 20 20 * Colors, captions and format used for units, structures, etc. types 21 21 */ 22 22 var g_SummaryTypes = { 23 23 "percent": { 24 24 "color": "", 25 25 "caption": "%", 26 26 "postfix": "%" 27 27 }, 28 28 "trained": { 29 29 "color": g_TypeColors.green, 30 30 "caption": translate("Trained"), 31 31 "postfix": " / " 32 32 }, 33 33 "constructed": { 34 34 "color": g_TypeColors.green, 35 35 "caption": translate("Constructed"), 36 36 "postfix": " / " 37 37 }, 38 38 "gathered": { 39 39 "color": g_TypeColors.green, 40 40 "caption": translate("Gathered"), 41 41 "postfix": " / " 42 42 }, 43 "alive": { 44 "caption": translate("Alive"), 45 "hide": true 46 }, 47 "count": { 48 "caption": translate("Count"), 49 "hide": true 50 }, 43 51 "sent": { 44 52 "color": g_TypeColors.green, 45 53 "caption": translate("Sent"), 46 54 "postfix": " / " 47 55 }, 48 56 "bought": { 49 57 "color": g_TypeColors.green, 50 58 "caption": translate("Bought"), 51 59 "postfix": " / " 52 60 }, 53 61 "income": { 54 62 "color": g_TypeColors.green, 55 63 "caption": translate("Income"), 56 64 "postfix": " / " 57 65 }, 58 66 "captured": { 59 67 "color": g_TypeColors.yellow, 60 68 "caption": translate("Captured"), 61 69 "postfix": " / " 62 70 }, 63 71 "succeeded": { 64 72 "color": g_TypeColors.green, 65 73 "caption": translate("Succeeded"), 66 74 "postfix": " / " 67 75 }, 68 76 "destroyed": { 69 77 "color": g_TypeColors.blue, 70 78 "caption": translate("Destroyed"), 71 79 "postfix": "\n" 72 80 }, 73 81 "killed": { 74 82 "color": g_TypeColors.blue, 75 83 "caption": translate("Killed"), 76 84 "postfix": "\n" 77 85 }, 78 86 "lost": { 79 87 "color": g_TypeColors.red, 80 88 "caption": translate("Lost"), 81 89 "postfix": "" 82 90 }, 83 91 "used": { 84 92 "color": g_TypeColors.red, 85 93 "caption": translate("Used"), 86 94 "postfix": "" 87 95 }, 88 96 "received": { 89 97 "color": g_TypeColors.red, 90 98 "caption": translate("Received"), 91 99 "postfix": "" 92 100 }, 93 101 "sold": { 94 102 "color": g_TypeColors.red, 95 103 "caption": translate("Sold"), 96 104 "postfix": "" 97 105 }, 98 106 "outcome": { 99 107 "color": g_TypeColors.red, 100 108 "caption": translate("Outcome"), 101 109 "postfix": "" 102 110 }, 103 111 "failed": { 104 112 "color": g_TypeColors.red, 105 113 "caption": translate("Failed"), 106 114 "postfix": "" 107 115 } 108 116 }; 109 117 110 118 // Translation: Unicode encoded infinity symbol indicating a division by zero in the summary screen. 111 119 var g_InfinitySymbol = translate("\u221E"); 112 120 113 121 var g_Teams = []; 114 122 115 123 var g_PlayerCount; 116 124 117 125 var g_GameData; 118 126 var g_ResourceData = new Resources(); 119 127 120 128 /** 121 129 * Selected chart indexes. 122 130 */ 123 131 var g_SelectedChart = { 124 132 "category": [0, 0], 125 133 "value": [0, 1], 126 134 "type": [0, 0] 127 135 }; 128 136 129 137 function init(data) 130 138 { 131 139 initSummaryData(data); 132 140 initGUISummary(); 133 141 } 134 142 135 143 function initSummaryData(data) 136 144 { 137 145 g_GameData = data; 138 146 g_ScorePanelsData = getScorePanelsData(); 139 147 140 148 let teamCharts = false; 141 149 if (data && data.gui && data.gui.summarySelection) 142 150 { 143 151 g_TabCategorySelected = data.gui.summarySelection.panel; 144 152 g_SelectedChart = data.gui.summarySelection.charts; 145 153 teamCharts = data.gui.summarySelection.teamCharts; 146 154 } 147 155 Engine.GetGUIObjectByName("toggleTeamBox").checked = g_Teams && teamCharts; 148 156 149 157 initTeamData(); 150 158 calculateTeamCounterDataHelper(); 151 159 } 152 160 153 161 function initGUISummary() 154 162 { 155 163 initGUIWindow(); 156 164 initPlayerBoxPositions(); 157 165 initGUICharts(); 158 166 initGUILabels(); 159 167 initGUIButtons(); 160 168 } 161 169 162 170 /** 163 171 * Sets the style and title of the page. 164 172 */ 165 173 function initGUIWindow() 166 174 { 167 175 let summaryWindow = Engine.GetGUIObjectByName("summaryWindow"); 168 176 summaryWindow.sprite = g_GameData.gui.dialog ? "ModernDialog" : "ModernWindow"; 169 177 summaryWindow.size = g_GameData.gui.dialog ? "16 24 100%-16 100%-24" : "0 0 100% 100%"; 170 178 Engine.GetGUIObjectByName("summaryWindowTitle").size = g_GameData.gui.dialog ? "50%-128 -16 50%+128 16" : "50%-128 4 50%+128 36"; 171 179 } 172 180 173 181 function selectPanelGUI(panel) 174 182 { 175 183 adjustTabDividers(Engine.GetGUIObjectByName("tabButton[" + panel + "]").size); 176 184 177 185 let generalPanel = Engine.GetGUIObjectByName("generalPanel"); 178 186 let chartsPanel = Engine.GetGUIObjectByName("chartsPanel"); 179 187 180 188 // We assume all scorePanels come before the charts. 181 189 let chartsHidden = panel < g_ScorePanelsData.length; 182 190 generalPanel.hidden = !chartsHidden; 183 191 chartsPanel.hidden = chartsHidden; 184 192 if (chartsHidden) 185 193 updatePanelData(g_ScorePanelsData[panel]); 186 194 else 187 195 [0, 1].forEach(updateCategoryDropdown); 188 196 } 189 197 190 198 function constructPlayersWithColor(color, playerListing) 191 199 { 192 200 return sprintf(translateWithContext("Player listing with color indicator", 193 201 "%(colorIndicator)s %(playerListing)s"), 194 202 { 195 203 "colorIndicator": setStringTags(translateWithContext( 196 204 "Charts player color indicator", "■"), { "color": color }), 197 205 "playerListing": playerListing 198 206 }); 199 207 } 200 208 201 209 function updateChartColorAndLegend() 202 210 { 203 211 let playerColors = []; 204 212 for (let i = 1; i <= g_PlayerCount; ++i) 205 213 { 206 214 let playerState = g_GameData.sim.playerStates[i]; 207 215 playerColors.push( 208 216 Math.floor(playerState.color.r * 255) + " " + 209 217 Math.floor(playerState.color.g * 255) + " " + 210 218 Math.floor(playerState.color.b * 255) 211 219 ); 212 220 } 213 221 214 222 for (let i = 0; i < 2; ++i) 215 223 Engine.GetGUIObjectByName("chart[" + i + "]").series_color = 216 224 Engine.GetGUIObjectByName("toggleTeamBox").checked ? 217 225 g_Teams.filter(el => el !== null).map(players => playerColors[players[0] - 1]) : 218 226 playerColors; 219 227 220 228 let chartLegend = Engine.GetGUIObjectByName("chartLegend"); 221 229 chartLegend.caption = (Engine.GetGUIObjectByName("toggleTeamBox").checked ? 222 230 g_Teams.filter(el => el !== null).map(players => 223 231 constructPlayersWithColor(playerColors[players[0] - 1], players.map(player => 224 232 g_GameData.sim.playerStates[player].name 225 233 ).join(translateWithContext("Player listing", ", "))) 226 234 ) : 227 235 g_GameData.sim.playerStates.slice(1).map((state, index) => 228 236 constructPlayersWithColor(playerColors[index], state.name)) 229 237 ).join(" "); 230 238 } 231 239 232 240 function initGUICharts() 233 241 { 234 242 updateChartColorAndLegend(); 235 243 let chart1Part = Engine.GetGUIObjectByName("chart[1]Part"); 236 244 let chart1PartSize = chart1Part.size; 237 245 chart1PartSize.rright += 50; 238 246 chart1PartSize.rleft += 50; 239 247 chart1PartSize.right -= 5; 240 248 chart1PartSize.left -= 5; 241 249 chart1Part.size = chart1PartSize; 242 250 Engine.GetGUIObjectByName("toggleTeam").hidden = !g_Teams; 243 251 } 244 252 245 253 function resizeDropdown(dropdown) 246 254 { 247 255 let size = dropdown.size; 248 256 size.bottom = dropdown.size.top + 249 257 (Engine.GetTextWidth(dropdown.font, dropdown.list[dropdown.selected]) > 250 258 dropdown.size.right - dropdown.size.left - 32 ? 42 : 27); 251 259 dropdown.size = size; 252 260 } 253 261 254 262 function updateCategoryDropdown(number) 255 263 { 256 264 let chartCategory = Engine.GetGUIObjectByName("chart[" + number + "]CategorySelection"); 257 265 chartCategory.list_data = g_ScorePanelsData.map((panel, idx) => idx); 258 266 chartCategory.list = g_ScorePanelsData.map(panel => panel.label); 259 267 chartCategory.onSelectionChange = function() { 260 268 if (!this.list_data[this.selected]) 261 269 return; 262 270 if (g_SelectedChart.category[number] != this.selected) 263 271 { 264 272 g_SelectedChart.category[number] = this.selected; 265 273 g_SelectedChart.value[number] = 0; 266 274 g_SelectedChart.type[number] = 0; 267 275 } 268 276 269 277 resizeDropdown(this); 270 278 updateValueDropdown(number, this.list_data[this.selected]); 271 279 }; 272 280 chartCategory.selected = g_SelectedChart.category[number]; 273 281 } 274 282 275 283 function updateValueDropdown(number, category) 276 284 { 277 285 let chartValue = Engine.GetGUIObjectByName("chart[" + number + "]ValueSelection"); 278 286 let list = g_ScorePanelsData[category].headings.map(heading => heading.caption); 279 287 list.shift(); 280 288 chartValue.list = list; 281 289 let list_data = g_ScorePanelsData[category].headings.map(heading => heading.identifier); 282 290 list_data.shift(); 283 291 chartValue.list_data = list_data; 284 292 chartValue.onSelectionChange = function() { 285 293 if (!this.list_data[this.selected]) 286 294 return; 287 295 if (g_SelectedChart.value[number] != this.selected) 288 296 { 289 297 g_SelectedChart.value[number] = this.selected; 290 298 g_SelectedChart.type[number] = 0; 291 299 } 292 300 293 301 resizeDropdown(this); 294 302 updateTypeDropdown(number, category, this.list_data[this.selected], this.selected); 295 303 }; 296 304 chartValue.selected = g_SelectedChart.value[number]; 297 305 } 298 306 299 307 function updateTypeDropdown(number, category, item, itemNumber) 300 308 { 301 309 let testValue = g_ScorePanelsData[category].counters[itemNumber].fn(g_GameData.sim.playerStates[1], 0, item); 302 310 let hide = !g_ScorePanelsData[category].counters[itemNumber].fn || 303 311 typeof testValue != "object" || Object.keys(testValue).length < 2; 304 312 Engine.GetGUIObjectByName("chart[" + number + "]TypeLabel").hidden = hide; 305 313 let chartType = Engine.GetGUIObjectByName("chart[" + number + "]TypeSelection"); 306 314 chartType.hidden = hide; 307 315 if (hide) 308 316 { 309 317 updateChart(number, category, item, itemNumber, Object.keys(testValue)[0] || undefined); 310 318 return; 311 319 } 312 320 313 321 chartType.list = Object.keys(testValue).map(type => g_SummaryTypes[type].caption); 314 322 chartType.list_data = Object.keys(testValue); 315 323 chartType.onSelectionChange = function() { 316 324 if (!this.list_data[this.selected]) 317 325 return; 318 326 g_SelectedChart.type[number] = this.selected; 319 327 resizeDropdown(this); 320 328 updateChart(number, category, item, itemNumber, this.list_data[this.selected]); 321 329 }; 322 330 chartType.selected = g_SelectedChart.type[number]; 323 331 } 324 332 325 333 function updateChart(number, category, item, itemNumber, type) 326 334 { 327 335 if (!g_ScorePanelsData[category].counters[itemNumber].fn) 328 336 return; 329 337 let chart = Engine.GetGUIObjectByName("chart[" + number + "]"); 330 338 chart.format_y = g_ScorePanelsData[category].headings[itemNumber + 1].format || "INTEGER"; 331 339 Engine.GetGUIObjectByName("chart[" + number + "]XAxisLabel").caption = translate("Time elapsed"); 332 340 333 341 let series = []; 334 342 if (Engine.GetGUIObjectByName("toggleTeamBox").checked) 335 343 for (let team in g_Teams) 336 344 { 337 345 let data = []; 338 346 for (let index in g_GameData.sim.playerStates[1].sequences.time) 339 347 { 340 348 let value = g_ScorePanelsData[category].teamCounterFn(team, index, item, 341 349 g_ScorePanelsData[category].counters, g_ScorePanelsData[category].headings); 342 350 if (type) 343 351 value = value[type]; 344 352 data.push([g_GameData.sim.playerStates[1].sequences.time[index], value]); 345 353 } 346 354 series.push(data); 347 355 } 348 356 else 349 357 for (let j = 1; j <= g_PlayerCount; ++j) 350 358 { 351 359 let playerState = g_GameData.sim.playerStates[j]; 352 360 let data = []; 353 361 for (let index in playerState.sequences.time) 354 362 { 355 363 let value = g_ScorePanelsData[category].counters[itemNumber].fn(playerState, index, item); 356 364 if (type) 357 365 value = value[type]; 358 366 data.push([playerState.sequences.time[index], value]); 359 367 } 360 368 series.push(data); 361 369 } 362 370 363 371 chart.series = series; 364 372 } 365 373 366 374 function adjustTabDividers(tabSize) 367 375 { 368 376 let tabButtonsLeft = Engine.GetGUIObjectByName("tabButtonsFrame").size.left; 369 377 370 378 let leftSpacer = Engine.GetGUIObjectByName("tabDividerLeft"); 371 379 let leftSpacerSize = leftSpacer.size; 372 380 leftSpacerSize.right = tabSize.left + tabButtonsLeft + 2; 373 381 leftSpacer.size = leftSpacerSize; 374 382 375 383 let rightSpacer = Engine.GetGUIObjectByName("tabDividerRight"); 376 384 let rightSpacerSize = rightSpacer.size; 377 385 rightSpacerSize.left = tabSize.right + tabButtonsLeft - 2; 378 386 rightSpacer.size = rightSpacerSize; 379 387 } 380 388 381 389 function updatePanelData(panelInfo) 382 390 { 383 391 resetGeneralPanel(); 384 392 updateGeneralPanelHeadings(panelInfo.headings); 385 393 updateGeneralPanelTitles(panelInfo.titleHeadings); 386 394 let rowPlayerObjectWidth = updateGeneralPanelCounter(panelInfo.counters); 387 395 updateGeneralPanelTeams(); 388 396 389 397 let index = g_GameData.sim.playerStates[1].sequences.time.length - 1; 390 398 let playerBoxesCounts = []; 391 399 for (let i = 0; i < g_PlayerCount; ++i) 392 400 { 393 401 let playerState = g_GameData.sim.playerStates[i + 1]; 394 402 395 403 if (!playerBoxesCounts[playerState.team + 1]) 396 404 playerBoxesCounts[playerState.team + 1] = 1; 397 405 else 398 406 playerBoxesCounts[playerState.team + 1] += 1; 399 407 400 408 let positionObject = playerBoxesCounts[playerState.team + 1] - 1; 401 409 let rowPlayer = "playerBox[" + positionObject + "]"; 402 410 let playerOutcome = "playerOutcome[" + positionObject + "]"; 403 411 let playerNameColumn = "playerName[" + positionObject + "]"; 404 412 let playerCivicBoxColumn = "civIcon[" + positionObject + "]"; 405 413 let playerCounterValue = "valueData[" + positionObject + "]"; 406 414 407 415 if (playerState.team != -1) 408 416 { 409 417 rowPlayer = "playerBoxt[" + playerState.team + "][" + positionObject + "]"; 410 418 playerOutcome = "playerOutcomet[" + playerState.team + "][" + positionObject + "]"; 411 419 playerNameColumn = "playerNamet[" + playerState.team + "][" + positionObject + "]"; 412 420 playerCivicBoxColumn = "civIcont[" + playerState.team + "][" + positionObject + "]"; 413 421 playerCounterValue = "valueDataTeam[" + playerState.team + "][" + positionObject + "]"; 414 422 } 415 423 416 424 let colorString = "color: " + 417 425 Math.floor(playerState.color.r * 255) + " " + 418 426 Math.floor(playerState.color.g * 255) + " " + 419 427 Math.floor(playerState.color.b * 255); 420 428 421 429 let rowPlayerObject = Engine.GetGUIObjectByName(rowPlayer); 422 430 rowPlayerObject.hidden = false; 423 431 rowPlayerObject.sprite = colorString + " " + g_PlayerBoxAlpha; 424 432 425 433 let boxSize = rowPlayerObject.size; 426 434 boxSize.right = rowPlayerObjectWidth; 427 435 rowPlayerObject.size = boxSize; 428 436 429 437 setOutcomeIcon(playerState.state, Engine.GetGUIObjectByName(playerOutcome)); 430 438 431 439 playerNameColumn = Engine.GetGUIObjectByName(playerNameColumn); 432 440 playerNameColumn.caption = g_GameData.sim.playerStates[i + 1].name; 433 441 playerNameColumn.tooltip = translateAISettings(g_GameData.sim.mapSettings.PlayerData[i + 1]); 434 442 435 443 let civIcon = Engine.GetGUIObjectByName(playerCivicBoxColumn); 436 444 civIcon.sprite = "stretched:" + g_CivData[playerState.civ].Emblem; 437 445 civIcon.tooltip = g_CivData[playerState.civ].Name; 438 446 439 447 updateCountersPlayer(playerState, panelInfo.counters, panelInfo.headings, playerCounterValue, index); 440 448 } 441 449 442 450 let teamCounterFn = panelInfo.teamCounterFn; 443 451 if (g_Teams && teamCounterFn) 444 452 updateCountersTeam(teamCounterFn, panelInfo.counters, panelInfo.headings, index); 445 453 } 446 454 447 455 function continueButton() 448 456 { 449 457 let summarySelection = { 450 458 "panel": g_TabCategorySelected, 451 459 "charts": g_SelectedChart, 452 460 "teamCharts": Engine.GetGUIObjectByName("toggleTeamBox").checked 453 461 }; 454 462 if (g_GameData.gui.isInGame) 455 463 Engine.PopGuiPage({ 456 464 "summarySelection": summarySelection 457 465 }); 458 466 else if (g_GameData.gui.dialog) 459 467 Engine.PopGuiPage(); 460 468 else if (Engine.HasXmppClient()) 461 469 Engine.SwitchGuiPage("page_lobby.xml", { "dialog": false }); 462 470 else if (g_GameData.gui.isReplay) 463 471 Engine.SwitchGuiPage("page_replaymenu.xml", { 464 472 "replaySelectionData": g_GameData.gui.replaySelectionData, 465 473 "summarySelection": summarySelection 466 474 }); 467 475 else 468 476 Engine.SwitchGuiPage("page_pregame.xml"); 469 477 } 470 478 471 479 function startReplay() 472 480 { 473 481 if (!Engine.StartVisualReplay(g_GameData.gui.replayDirectory)) 474 482 { 475 483 warn("Replay file not found!"); 476 484 return; 477 485 } 478 486 479 487 Engine.SwitchGuiPage("page_loading.xml", { 480 488 "attribs": Engine.GetReplayAttributes(g_GameData.gui.replayDirectory), 481 489 "playerAssignments": { 482 490 "local": { 483 491 "name": singleplayerName(), 484 492 "player": -1 485 493 } 486 494 }, 487 495 "savedGUIData": "", 488 496 "isReplay": true, 489 497 "replaySelectionData": g_GameData.gui.replaySelectionData 490 498 }); 491 499 } 492 500 493 501 function initGUILabels() 494 502 { 495 503 let assignedState = g_GameData.sim.playerStates[g_GameData.gui.assignedPlayer || -1]; 496 504 497 505 Engine.GetGUIObjectByName("summaryText").caption = 498 506 g_GameData.gui.isInGame ? 499 507 translate("Current Scores") : 500 508 g_GameData.gui.isReplay ? 501 509 translate("Scores at the end of the game.") : 502 510 g_GameData.gui.disconnected ? 503 511 translate("You have been disconnected.") : 504 512 !assignedState ? 505 513 translate("You have left the game.") : 506 514 assignedState.state == "won" ? 507 515 translate("You have won the battle!") : 508 516 assignedState.state == "defeated" ? 509 517 translate("You have been defeated…") : 510 518 translate("You have abandoned the game."); 511 519 512 520 Engine.GetGUIObjectByName("timeElapsed").caption = sprintf( 513 521 translate("Game time elapsed: %(time)s"), { 514 522 "time": timeToString(g_GameData.sim.timeElapsed) 515 523 }); 516 524 517 525 let mapType = g_Settings.MapTypes.find(type => type.Name == g_GameData.sim.mapSettings.mapType); 518 526 let mapSize = g_Settings.MapSizes.find(size => size.Tiles == g_GameData.sim.mapSettings.Size || 0); 519 527 520 528 Engine.GetGUIObjectByName("mapName").caption = sprintf( 521 529 translate("%(mapName)s - %(mapType)s"), { 522 530 "mapName": translate(g_GameData.sim.mapSettings.Name), 523 531 "mapType": mapSize ? mapSize.Name : (mapType ? mapType.Title : "") 524 532 }); 525 533 } 526 534 527 535 function initGUIButtons() 528 536 { 529 537 let replayButton = Engine.GetGUIObjectByName("replayButton"); 530 538 replayButton.hidden = g_GameData.gui.isInGame || !g_GameData.gui.replayDirectory; 531 539 532 540 let lobbyButton = Engine.GetGUIObjectByName("lobbyButton"); 533 541 lobbyButton.tooltip = colorizeHotkey(translate("%(hotkey)s: Toggle the multiplayer lobby in a dialog window."), "lobby"); 534 542 lobbyButton.hidden = g_GameData.gui.isInGame || !Engine.HasXmppClient(); 535 543 536 544 // Right-align lobby button 537 545 let lobbyButtonSize = lobbyButton.size; 538 546 let lobbyButtonWidth = lobbyButtonSize.right - lobbyButtonSize.left; 539 547 lobbyButtonSize.right = (replayButton.hidden ? Engine.GetGUIObjectByName("continueButton").size.left : replayButton.size.left) - 10; 540 548 lobbyButtonSize.left = lobbyButtonSize.right - lobbyButtonWidth; 541 549 lobbyButton.size = lobbyButtonSize; 542 550 543 551 let allPanelsData = g_ScorePanelsData.concat(g_ChartPanelsData); 544 552 for (let tab in allPanelsData) 545 553 allPanelsData[tab].tooltip = 546 554 sprintf(translate("Toggle the %(name)s summary tab."), { "name": allPanelsData[tab].label }) + 547 555 colorizeHotkey("\n" + translate("Use %(hotkey)s to move a summary tab right."), "tab.next") + 548 556 colorizeHotkey("\n" + translate("Use %(hotkey)s to move a summary tab left."), "tab.prev"); 549 557 550 558 placeTabButtons( 551 559 allPanelsData, 552 560 true, 553 561 g_TabButtonWidth, 554 562 g_TabButtonDist, 555 563 selectPanel, 556 564 selectPanelGUI); 557 565 } 558 566 559 567 function initTeamData() 560 568 { 561 569 // Panels 562 570 g_PlayerCount = g_GameData.sim.playerStates.length - 1; 563 571 564 572 if (g_GameData.sim.mapSettings.LockTeams) 565 573 { 566 574 // Count teams 567 575 for (let player = 1; player <= g_PlayerCount; ++player) 568 576 { 569 577 let playerTeam = g_GameData.sim.playerStates[player].team; 570 578 if (!g_Teams[playerTeam]) 571 579 g_Teams[playerTeam] = []; 572 580 g_Teams[playerTeam].push(player); 573 581 } 574 582 575 583 if (g_Teams.every(team => team && team.length < 2)) 576 584 g_Teams = false; // Each player has his own team. Displaying teams makes no sense. 577 585 } 578 586 else 579 587 g_Teams = false; 580 588 581 589 // Erase teams data if teams are not displayed 582 590 if (!g_Teams) 583 591 for (let p = 0; p < g_PlayerCount; ++p) 584 592 g_GameData.sim.playerStates[p+1].team = -1; 585 593 } -
binaries/data/mods/public/simulation/components/StatisticsTracker.js
1 1 function StatisticsTracker() {} 2 2 3 3 StatisticsTracker.prototype.Schema = 4 4 "<a:help>This component records statistics over the course of the match, such as the number of trained, lost, captured and destroyed units and buildings The statistics are consumed by the summary screen and lobby rankings.</a:help>" + 5 5 "<a:example>" + 6 6 "<UnitClasses>Infantry FemaleCitizen</UnitClasses>" + 7 7 "<StructureClasses>House Wonder</StructureClasses>" + 8 8 "</a:example>" + 9 9 "<element name='UnitClasses' a:help='The tracker records trained, lost, killed and captured units of entities that match any of these Identity classes.'>" + 10 10 "<attribute name='datatype'>" + 11 11 "<value>tokens</value>" + 12 12 "</attribute>" + 13 13 "<text/>" + 14 14 "</element>" + 15 15 "<element name='StructureClasses' a:help='The tracker records constructed, lost, destroyed and captured structures of entities that match any of these Identity classes.'>" + 16 16 "<attribute name='datatype'>" + 17 17 "<value>tokens</value>" + 18 18 "</attribute>" + 19 19 "<text/>" + 20 20 "</element>"; 21 21 22 22 /** 23 23 * This number specifies the time in milliseconds between consecutive statistics snapshots recorded. 24 24 */ 25 25 StatisticsTracker.prototype.UpdateSequenceInterval = 30 * 1000; 26 26 27 27 StatisticsTracker.prototype.Init = function() 28 28 { 29 29 this.unitsClasses = this.template.UnitClasses._string.split(/\s+/); 30 30 this.buildingsClasses = this.template.StructureClasses._string.split(/\s+/); 31 31 32 this.unitsAlive = {}; 32 33 this.unitsTrained = {}; 33 34 this.unitsLost = {}; 34 35 this.enemyUnitsKilled = {}; 35 36 this.unitsCaptured = {}; 36 37 37 38 this.unitsLostValue = 0; 38 39 this.enemyUnitsKilledValue = 0; 39 40 this.unitsCapturedValue = 0; 40 41 41 for (let counterName of ["units Trained", "unitsLost", "enemyUnitsKilled", "unitsCaptured"])42 for (let counterName of ["unitsAlive", "unitsTrained", "unitsLost", "enemyUnitsKilled", "unitsCaptured"]) 42 43 { 43 44 this[counterName].total = 0; 44 45 for (let unitClass of this.unitsClasses) 45 46 // Domestic units are only counted for training 46 47 if (unitClass != "Domestic" || counterName == "unitsTrained") 47 48 this[counterName][unitClass] = 0; 48 49 } 49 50 50 51 this.buildingsConstructed = {}; 51 52 this.buildingsLost = {}; 52 53 this.enemyBuildingsDestroyed = {}; 53 54 this.buildingsCaptured = {}; 54 55 55 56 this.buildingsLostValue = 0; 56 57 this.enemyBuildingsDestroyedValue = 0; 57 58 this.buildingsCapturedValue = 0; 58 59 59 60 for (let counterName of ["buildingsConstructed", "buildingsLost", "enemyBuildingsDestroyed", "buildingsCaptured"]) 60 61 { 61 62 this[counterName].total = 0; 62 63 for (let unitClass of this.buildingsClasses) 63 64 this[counterName][unitClass] = 0; 64 65 } 65 66 66 67 this.resourcesGathered = { 67 68 "vegetarianFood": 0 68 69 }; 69 70 this.resourcesUsed = {}; 70 71 this.resourcesSold = {}; 71 72 this.resourcesBought = {}; 72 73 for (let res of Resources.GetCodes()) 73 74 { 74 75 this.resourcesGathered[res] = 0; 75 76 this.resourcesUsed[res] = 0; 76 77 this.resourcesSold[res] = 0; 77 78 this.resourcesBought[res] = 0; 78 79 } 79 80 80 81 this.tributesSent = 0; 81 82 this.tributesReceived = 0; 82 83 this.tradeIncome = 0; 83 84 this.treasuresCollected = 0; 84 85 this.lootCollected = 0; 85 86 this.peakPercentMapControlled = 0; 86 87 this.teamPeakPercentMapControlled = 0; 87 88 this.successfulBribes = 0; 88 89 this.failedBribes = 0; 89 90 90 91 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 91 92 this.updateTimer = cmpTimer.SetInterval( 92 93 this.entity, IID_StatisticsTracker, "UpdateSequences", 0, this.UpdateSequenceInterval); 93 94 }; 94 95 95 96 StatisticsTracker.prototype.OnGlobalInitGame = function() 96 97 { 97 98 this.sequences = clone(this.GetStatistics()); 98 99 this.sequences.time = []; 100 this.unitsAlive = this.GetUnitsAlive(); 99 101 }; 100 102 101 103 /** 102 104 * Returns a subset of statistics that will be added to the simulation state, 103 105 * thus called each turn. Basic statistics should not contain data that would 104 106 * be expensive to compute. 105 107 * 106 108 * Note: as of now, nothing in the game needs that, but some AIs developed by 107 109 * modders need it in the API. 108 110 */ 109 111 StatisticsTracker.prototype.GetBasicStatistics = function() 110 112 { 111 113 return { 112 114 "resourcesGathered": this.resourcesGathered, 113 115 "percentMapExplored": this.GetPercentMapExplored() 114 116 }; 115 117 }; 116 118 117 119 StatisticsTracker.prototype.GetStatistics = function() 118 120 { 119 121 return { 122 "unitsAlive": this.unitsAlive, 120 123 "unitsTrained": this.unitsTrained, 121 124 "unitsLost": this.unitsLost, 122 125 "unitsLostValue": this.unitsLostValue, 123 126 "enemyUnitsKilled": this.enemyUnitsKilled, 124 127 "enemyUnitsKilledValue": this.enemyUnitsKilledValue, 125 128 "unitsCaptured": this.unitsCaptured, 126 129 "unitsCapturedValue": this.unitsCapturedValue, 127 130 "buildingsConstructed": this.buildingsConstructed, 128 131 "buildingsLost": this.buildingsLost, 129 132 "buildingsLostValue": this.buildingsLostValue, 130 133 "enemyBuildingsDestroyed": this.enemyBuildingsDestroyed, 131 134 "enemyBuildingsDestroyedValue": this.enemyBuildingsDestroyedValue, 132 135 "buildingsCaptured": this.buildingsCaptured, 133 136 "buildingsCapturedValue": this.buildingsCapturedValue, 137 "resourcesCount": this.GetResourceCounts(), 134 138 "resourcesGathered": this.resourcesGathered, 135 139 "resourcesUsed": this.resourcesUsed, 136 140 "resourcesSold": this.resourcesSold, 137 141 "resourcesBought": this.resourcesBought, 138 142 "tributesSent": this.tributesSent, 139 143 "tributesReceived": this.tributesReceived, 140 144 "tradeIncome": this.tradeIncome, 141 145 "treasuresCollected": this.treasuresCollected, 142 146 "lootCollected": this.lootCollected, 143 147 "percentMapExplored": this.GetPercentMapExplored(), 144 148 "teamPercentMapExplored": this.GetTeamPercentMapExplored(), 145 149 "percentMapControlled": this.GetPercentMapControlled(), 146 150 "teamPercentMapControlled": this.GetTeamPercentMapControlled(), 147 151 "peakPercentMapControlled": this.peakPercentMapControlled, 148 152 "teamPeakPercentMapControlled": this.teamPeakPercentMapControlled, 149 153 "successfulBribes": this.successfulBribes, 150 154 "failedBribes": this.failedBribes 151 155 }; 152 156 }; 153 157 154 158 StatisticsTracker.prototype.GetSequences = function() 155 159 { 156 160 let ret = clone(this.sequences); 157 161 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 158 162 159 163 ret.time.push(cmpTimer.GetTime() / 1000); 160 164 this.PushValue(this.GetStatistics(), ret); 161 165 return ret; 162 166 }; 163 167 164 168 /** 165 169 * Used to print statistics for non-visual autostart games. 166 170 * @return The player's statistics as a JSON string beautified with some indentations. 167 171 */ 168 172 StatisticsTracker.prototype.GetStatisticsJSON = function() 169 173 { 170 174 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 171 175 172 176 let playerStatistics = { 173 177 "playerID": cmpPlayer.GetPlayerID(), 174 178 "playerState": cmpPlayer.GetState(), 175 179 "statistics": this.GetStatistics() 176 180 }; 177 181 178 182 return JSON.stringify(playerStatistics, null, "\t"); 179 183 }; 180 184 181 185 /** 186 * @return Breakdown of the living units 187 */ 188 StatisticsTracker.prototype.GetUnitsAlive = function() { 189 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 190 191 let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 192 let entities = cmpRangeManager.GetEntitiesByPlayer(cmpPlayer.playerID); 193 194 let unitsAlive = { total: 0 }; 195 for (let unitClass of this.unitsClasses) 196 unitsAlive[unitClass] = 0; 197 198 for (let entity of entities) 199 { 200 let cmpEntityIdentity = Engine.QueryInterface(entity, IID_Identity); 201 if (!cmpEntityIdentity) 202 break; 203 204 if (cmpEntityIdentity.HasClass("Unit")) 205 { 206 let classes = cmpEntityIdentity.GetClassesList(); 207 if (!classes) 208 return; 209 210 for(let type in classes) 211 if (unitsAlive[type] !== undefined) 212 { 213 ++unitsAlive[type]; 214 } 215 216 if (!cmpEntityIdentity.HasClass("Domestic")) 217 ++unitsAlive.total; 218 } 219 220 } 221 222 return unitsAlive; 223 } 224 225 /** 182 226 * Increments counter associated with certain entity/counter and type of given entity. 183 227 * @param cmpIdentity - the entity identity component. 184 228 * @param counter - the name of the counter to increment (e.g. "unitsTrained"). 185 229 * @param type - the type of the counter (e.g. "workers"). 230 * @param value - the value to increment the counter with (default: 1) (e.g. -1 to decrement the counter) 186 231 */ 187 StatisticsTracker.prototype.CounterIncrement = function(cmpIdentity, counter, type )232 StatisticsTracker.prototype.CounterIncrement = function(cmpIdentity, counter, type, value=1) 188 233 { 189 234 var classes = cmpIdentity.GetClassesList(); 190 235 if (!classes) 191 236 return; 192 237 193 238 if (classes.indexOf(type) != -1) 194 ++this[counter][type];239 this[counter][type] += value; 195 240 }; 196 241 197 242 /** 198 243 * Counts the total number of units trained as well as an individual count for 199 244 * each unit type. Based on templates. 200 245 */ 201 246 StatisticsTracker.prototype.IncreaseTrainedUnitsCounter = function(trainedUnit) 202 247 { 203 248 let cmpUnitEntityIdentity = Engine.QueryInterface(trainedUnit, IID_Identity); 204 249 205 250 if (!cmpUnitEntityIdentity) 206 251 return; 207 252 208 253 let cmpCost = Engine.QueryInterface(trainedUnit, IID_Cost); 209 254 let costs = cmpCost && cmpCost.GetResourceCosts(); 210 255 211 256 for (let type of this.unitsClasses) 257 { 212 258 this.CounterIncrement(cmpUnitEntityIdentity, "unitsTrained", type); 259 this.CounterIncrement(cmpUnitEntityIdentity, "unitsAlive", type); 260 } 213 261 214 262 if (!cmpUnitEntityIdentity.HasClass("Domestic")) 263 { 215 264 ++this.unitsTrained.total; 265 ++this.unitsAlive.total; 266 } 216 267 217 268 if (cmpUnitEntityIdentity.HasClass("Domestic") && costs) 218 269 { 219 270 // Subtract costs for sheep/goats/pigs to get the net food gain/use for corralling 220 271 this.resourcesUsed.food -= costs.food; 221 272 this.resourcesGathered.food -= costs.food; 222 273 } 223 274 224 275 }; 225 276 226 277 /** 227 278 * Counts the total number of buildings constructed as well as an individual count for 228 279 * each building type. Based on templates. 229 280 */ 230 281 StatisticsTracker.prototype.IncreaseConstructedBuildingsCounter = function(constructedBuilding) 231 282 { 232 283 var cmpBuildingEntityIdentity = Engine.QueryInterface(constructedBuilding, IID_Identity); 233 284 234 285 if (!cmpBuildingEntityIdentity) 235 286 return; 236 287 237 288 for (let type of this.buildingsClasses) 238 289 this.CounterIncrement(cmpBuildingEntityIdentity, "buildingsConstructed", type); 239 290 240 291 ++this.buildingsConstructed.total; 241 292 }; 242 293 243 294 StatisticsTracker.prototype.KilledEntity = function(targetEntity) 244 295 { 245 296 var cmpTargetEntityIdentity = Engine.QueryInterface(targetEntity, IID_Identity); 246 297 if (!cmpTargetEntityIdentity) 247 298 return; 248 299 249 300 var cmpCost = Engine.QueryInterface(targetEntity, IID_Cost); 250 301 var costs = cmpCost && cmpCost.GetResourceCosts(); 251 302 252 303 // Exclude gaia animals but not gaia soldiers. 253 304 if (cmpTargetEntityIdentity.HasClass("Unit") && !cmpTargetEntityIdentity.HasClass("Animal")) 254 305 { 255 306 for (let type of this.unitsClasses) 256 307 this.CounterIncrement(cmpTargetEntityIdentity, "enemyUnitsKilled", type); 257 308 258 309 ++this.enemyUnitsKilled.total; 259 310 260 311 if (costs) 261 312 for (let type in costs) 262 313 this.enemyUnitsKilledValue += costs[type]; 263 314 } 264 315 265 316 let cmpFoundation = Engine.QueryInterface(targetEntity, IID_Foundation); 266 317 if (cmpTargetEntityIdentity.HasClass("Structure") && !cmpFoundation) 267 318 { 268 319 for (let type of this.buildingsClasses) 269 320 this.CounterIncrement(cmpTargetEntityIdentity, "enemyBuildingsDestroyed", type); 270 321 271 322 ++this.enemyBuildingsDestroyed.total; 272 323 273 324 if (costs) 274 325 for (let type in costs) 275 326 this.enemyBuildingsDestroyedValue += costs[type]; 276 327 } 277 328 }; 278 329 279 330 StatisticsTracker.prototype.LostEntity = function(lostEntity) 280 331 { 281 332 var cmpLostEntityIdentity = Engine.QueryInterface(lostEntity, IID_Identity); 282 333 if (!cmpLostEntityIdentity) 283 334 return; 284 335 285 336 var cmpCost = Engine.QueryInterface(lostEntity, IID_Cost); 286 337 var costs = cmpCost && cmpCost.GetResourceCosts(); 287 338 288 339 if (cmpLostEntityIdentity.HasClass("Unit") && !cmpLostEntityIdentity.HasClass("Domestic")) 289 340 { 290 341 for (let type of this.unitsClasses) 342 { 291 343 this.CounterIncrement(cmpLostEntityIdentity, "unitsLost", type); 344 this.CounterIncrement(cmpLostEntityIdentity, "unitsAlive", type, -1); 345 } 292 346 293 347 ++this.unitsLost.total; 348 --this.unitsAlive.total; 294 349 295 350 if (costs) 296 351 for (let type in costs) 297 352 this.unitsLostValue += costs[type]; 298 353 } 299 354 300 355 let cmpFoundation = Engine.QueryInterface(lostEntity, IID_Foundation); 301 356 if (cmpLostEntityIdentity.HasClass("Structure") && !cmpFoundation) 302 357 { 303 358 for (let type of this.buildingsClasses) 304 359 this.CounterIncrement(cmpLostEntityIdentity, "buildingsLost", type); 305 360 306 361 ++this.buildingsLost.total; 307 362 308 363 if (costs) 309 364 for (let type in costs) 310 365 this.buildingsLostValue += costs[type]; 311 366 } 312 367 }; 313 368 314 369 StatisticsTracker.prototype.CapturedEntity = function(capturedEntity) 315 370 { 316 371 let cmpCapturedEntityIdentity = Engine.QueryInterface(capturedEntity, IID_Identity); 317 372 if (!cmpCapturedEntityIdentity) 318 373 return; 319 374 320 375 let cmpCost = Engine.QueryInterface(capturedEntity, IID_Cost); 321 376 let costs = cmpCost && cmpCost.GetResourceCosts(); 322 377 323 378 if (cmpCapturedEntityIdentity.HasClass("Unit")) 324 379 { 325 380 for (let type of this.unitsClasses) 381 { 326 382 this.CounterIncrement(cmpCapturedEntityIdentity, "unitsCaptured", type); 383 this.CounterIncrement(cmpCapturedEntityIdentity, "unitsAlive", type); 384 } 327 385 328 386 ++this.unitsCaptured.total; 387 ++this.unitsAlive.total; 329 388 330 389 if (costs) 331 390 for (let type in costs) 332 391 this.unitsCapturedValue += costs[type]; 333 392 } 334 393 335 394 if (cmpCapturedEntityIdentity.HasClass("Structure")) 336 395 { 337 396 for (let type of this.buildingsClasses) 338 397 this.CounterIncrement(cmpCapturedEntityIdentity, "buildingsCaptured", type); 339 398 340 399 ++this.buildingsCaptured.total; 341 400 342 401 if (costs) 343 402 for (let type in costs) 344 403 this.buildingsCapturedValue += costs[type]; 345 404 } 346 405 }; 347 406 407 StatisticsTracker.prototype.GetResourceCounts = function() { 408 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 409 if (!cmpPlayer) { 410 let empty = {}; 411 for (let res of Resources.GetCodes()) 412 empty[res] = 0; 413 return empty; 414 } 415 return cmpPlayer.GetResourceCounts(); 416 } 417 348 418 /** 349 419 * @param {string} type - generic type of resource. 350 420 * @param {number} amount - amount of resource, whick should be added. 351 421 * @param {string} specificType - specific type of resource. 352 422 */ 353 423 StatisticsTracker.prototype.IncreaseResourceGatheredCounter = function(type, amount, specificType) 354 424 { 355 425 this.resourcesGathered[type] += amount; 356 426 357 427 if (type == "food" && (specificType == "fruit" || specificType == "grain")) 358 428 this.resourcesGathered.vegetarianFood += amount; 359 429 }; 360 430 361 431 /** 362 432 * @param {string} type - generic type of resource. 363 433 * @param {number} amount - amount of resource, which should be added. 364 434 */ 365 435 StatisticsTracker.prototype.IncreaseResourceUsedCounter = function(type, amount) 366 436 { 367 437 this.resourcesUsed[type] += amount; 368 438 }; 369 439 370 440 StatisticsTracker.prototype.IncreaseTreasuresCollectedCounter = function() 371 441 { 372 442 ++this.treasuresCollected; 373 443 }; 374 444 375 445 StatisticsTracker.prototype.IncreaseLootCollectedCounter = function(amount) 376 446 { 377 447 for (let type in amount) 378 448 this.lootCollected += amount[type]; 379 449 }; 380 450 381 451 StatisticsTracker.prototype.IncreaseResourcesSoldCounter = function(type, amount) 382 452 { 383 453 this.resourcesSold[type] += amount; 384 454 }; 385 455 386 456 StatisticsTracker.prototype.IncreaseResourcesBoughtCounter = function(type, amount) 387 457 { 388 458 this.resourcesBought[type] += amount; 389 459 }; 390 460 391 461 StatisticsTracker.prototype.IncreaseTributesSentCounter = function(amount) 392 462 { 393 463 this.tributesSent += amount; 394 464 }; 395 465 396 466 StatisticsTracker.prototype.IncreaseTributesReceivedCounter = function(amount) 397 467 { 398 468 this.tributesReceived += amount; 399 469 }; 400 470 401 471 StatisticsTracker.prototype.IncreaseTradeIncomeCounter = function(amount) 402 472 { 403 473 this.tradeIncome += amount; 404 474 }; 405 475 406 476 StatisticsTracker.prototype.IncreaseSuccessfulBribesCounter = function() 407 477 { 408 478 ++this.successfulBribes; 409 479 }; 410 480 411 481 StatisticsTracker.prototype.IncreaseFailedBribesCounter = function() 412 482 { 413 483 ++this.failedBribes; 414 484 }; 415 485 416 486 StatisticsTracker.prototype.GetPercentMapExplored = function() 417 487 { 418 488 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 419 489 if (!cmpPlayer) 420 490 return 0; 421 491 422 492 return Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetPercentMapExplored(cmpPlayer.GetPlayerID()); 423 493 }; 424 494 425 495 /** 426 496 * Note: cmpRangeManager.GetUnionPercentMapExplored computes statistics from scratch! 427 497 * As a consequence, this function should not be called too often. 428 498 */ 429 499 StatisticsTracker.prototype.GetTeamPercentMapExplored = function() 430 500 { 431 501 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 432 502 if (!cmpPlayer) 433 503 return 0; 434 504 435 505 let team = cmpPlayer.GetTeam(); 436 506 let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 437 507 // If teams are not locked, this statistic won't be displayed, so don't bother computing 438 508 if (team == -1 || !cmpPlayer.GetLockTeams()) 439 509 return cmpRangeManager.GetPercentMapExplored(cmpPlayer.GetPlayerID()); 440 510 441 511 let teamPlayers = []; 442 512 let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); 443 513 for (let i = 1; i < numPlayers; ++i) 444 514 { 445 515 let cmpOtherPlayer = QueryPlayerIDInterface(i); 446 516 if (cmpOtherPlayer && cmpOtherPlayer.GetTeam() == team) 447 517 teamPlayers.push(i); 448 518 } 449 519 450 520 return cmpRangeManager.GetUnionPercentMapExplored(teamPlayers); 451 521 }; 452 522 453 523 StatisticsTracker.prototype.GetPercentMapControlled = function() 454 524 { 455 525 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 456 526 if (!cmpPlayer) 457 527 return 0; 458 528 459 529 return Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager).GetTerritoryPercentage(cmpPlayer.GetPlayerID()); 460 530 }; 461 531 462 532 StatisticsTracker.prototype.GetTeamPercentMapControlled = function() 463 533 { 464 534 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 465 535 if (!cmpPlayer) 466 536 return 0; 467 537 468 538 let team = cmpPlayer.GetTeam(); 469 539 let cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager); 470 540 if (team == -1 || !cmpPlayer.GetLockTeams()) 471 541 return cmpTerritoryManager.GetTerritoryPercentage(cmpPlayer.GetPlayerID()); 472 542 473 543 let teamPercent = 0; 474 544 let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); 475 545 for (let i = 1; i < numPlayers; ++i) 476 546 { 477 547 let cmpOtherPlayer = QueryPlayerIDInterface(i); 478 548 if (cmpOtherPlayer && cmpOtherPlayer.GetTeam() == team) 479 549 teamPercent += cmpTerritoryManager.GetTerritoryPercentage(i); 480 550 } 481 551 482 552 return teamPercent; 483 553 }; 484 554 485 555 StatisticsTracker.prototype.OnTerritoriesChanged = function(msg) 486 556 { 487 557 this.UpdatePeakPercentages(); 488 558 }; 489 559 490 560 StatisticsTracker.prototype.OnGlobalPlayerDefeated = function(msg) 491 561 { 492 562 this.UpdatePeakPercentages(); 493 563 }; 494 564 495 565 StatisticsTracker.prototype.OnGlobalPlayerWon = function(msg) 496 566 { 497 567 this.UpdatePeakPercentages(); 498 568 }; 499 569 500 570 StatisticsTracker.prototype.UpdatePeakPercentages = function() 501 571 { 502 572 this.peakPercentMapControlled = Math.max(this.peakPercentMapControlled, this.GetPercentMapControlled()); 503 573 this.teamPeakPercentMapControlled = Math.max(this.teamPeakPercentMapControlled, this.GetTeamPercentMapControlled()); 504 574 }; 505 575 506 576 /** 507 577 * Adds the values of fromData to the end of the arrays of toData. 508 578 * If toData misses the needed array, one will be created. 509 579 * 510 580 * @param fromData - an object of values or a value. 511 581 * @param toData - an object of arrays or an array. 512 582 **/ 513 583 StatisticsTracker.prototype.PushValue = function(fromData, toData) 514 584 { 515 585 if (typeof fromData == "object") 516 586 for (let prop in fromData) 517 587 { 518 588 if (typeof toData[prop] != "object") 519 589 toData[prop] = [fromData[prop]]; 520 590 else 521 591 this.PushValue(fromData[prop], toData[prop]); 522 592 } 523 593 else 524 594 toData.push(fromData); 525 595 }; 526 596 527 597 StatisticsTracker.prototype.UpdateSequences = function() 528 598 { 529 599 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 530 600 this.sequences.time.push(cmpTimer.GetTime() / 1000); 531 601 this.PushValue(this.GetStatistics(), this.sequences); 532 602 }; 533 603 534 604 Engine.RegisterComponentType(IID_StatisticsTracker, "StatisticsTracker", StatisticsTracker);