-
diff --git a/binaries/data/mods/public/globalscripts/Resources.js b/binaries/data/mods/public/globalscripts/Resources.js
new file mode 100644
index 0000000..6287897
-
|
+
|
|
| 1 | /** |
| 2 | * Since the AI context can't access JSON functions, it gets passed an object |
| 3 | * containing the information from `GuiInterface.js::GetSimulationState()`. |
| 4 | */ |
| 5 | function Resources() |
| 6 | { |
| 7 | let jsonFiles = []; |
| 8 | // Simulation context |
| 9 | if (Engine.FindJSONFiles) |
| 10 | { |
| 11 | jsonFiles = Engine.FindJSONFiles("resources", false); |
| 12 | for (let file in jsonFiles) |
| 13 | jsonFiles[file] = "resources/" + jsonFiles[file] + ".json"; |
| 14 | } |
| 15 | // GUI context |
| 16 | else if (Engine.BuildDirEntList) |
| 17 | jsonFiles = Engine.BuildDirEntList("simulation/data/resources/", "*.json", false); |
| 18 | else |
| 19 | { |
| 20 | error("Resources: JSON functions are not available"); |
| 21 | return; |
| 22 | } |
| 23 | |
| 24 | this.resourceData = []; |
| 25 | this.resourceCodes = []; |
| 26 | |
| 27 | for (let filename of jsonFiles) |
| 28 | { |
| 29 | let data = Engine.ReadJSONFile(filename); |
| 30 | if (!data) |
| 31 | continue; |
| 32 | |
| 33 | this.resourceData.push(data); |
| 34 | if (data.enabled) |
| 35 | this.resourceCodes.push(data.code); |
| 36 | } |
| 37 | |
| 38 | let resSort = (a, b) => |
| 39 | a.order < b.order ? -1 : |
| 40 | a.order > b.order ? +1 : 0; |
| 41 | |
| 42 | this.resourceData.sort(resSort); |
| 43 | this.resourceCodes.sort((a, b) => resSort( |
| 44 | this.resourceData.find(resource => resource.code == a), |
| 45 | this.resourceData.find(resource => resource.code == b) |
| 46 | )); |
| 47 | }; |
| 48 | |
| 49 | Resources.prototype.GetData = function() |
| 50 | { |
| 51 | return this.resourceData.filter(resource => resource.enabled); |
| 52 | }; |
| 53 | |
| 54 | Resources.prototype.GetResource = function(type) |
| 55 | { |
| 56 | let lType = type.toLowerCase(); |
| 57 | return this.GetData().find(resource => resource.code == lType); |
| 58 | }; |
| 59 | |
| 60 | Resources.prototype.GetCodes = function() |
| 61 | { |
| 62 | return this.resourceCodes; |
| 63 | }; |
| 64 | |
| 65 | /** |
| 66 | * Returns an object containing untranslated resource names mapped to |
| 67 | * resource codes. Includes subtypes. |
| 68 | */ |
| 69 | Resources.prototype.GetNames = function() |
| 70 | { |
| 71 | let names = {}; |
| 72 | for (let res of this.GetData()) |
| 73 | { |
| 74 | names[res.code] = res.name; |
| 75 | for (let subres in res.subtypes) |
| 76 | names[subres] = res.subtypes[subres] |
| 77 | } |
| 78 | return names; |
| 79 | }; |
-
diff --git a/binaries/data/mods/public/gui/common/functions_utility.js b/binaries/data/mods/public/gui/common/functions_utility.js
index 12380bb..9f99b2c 100644
a
|
b
|
function notifyUser(userName, msgText)
|
245 | 245 | |
246 | 246 | g_LastNickNotification = timeNow; |
247 | 247 | } |
| 248 | |
| 249 | /** |
| 250 | * Horizontally spaces objects within a parent |
| 251 | * |
| 252 | * @param margin The gap, in px, between the objects |
| 253 | */ |
| 254 | function horizontallySpaceObjects(parentName, margin=0) |
| 255 | { |
| 256 | let objects = Engine.GetGUIObjectByName(parentName).children; |
| 257 | for (let i = 0; i < objects.length; ++i) |
| 258 | { |
| 259 | let size = objects[i].size; |
| 260 | let width = size.right - size.left; |
| 261 | size.left = i * (width + margin) + margin; |
| 262 | size.right = (i + 1) * (width + margin); |
| 263 | objects[i].size = size; |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | /** |
| 268 | * Hide all children after a certain index |
| 269 | * |
| 270 | * @param idx - The index from which to start |
| 271 | */ |
| 272 | function hideRemaining(parentName, start = 0) |
| 273 | { |
| 274 | let objects = Engine.GetGUIObjectByName(parentName).children; |
| 275 | |
| 276 | for (let i = start; i < objects.length; ++i) |
| 277 | objects[i].hidden = true; |
| 278 | } |
-
diff --git a/binaries/data/mods/public/gui/common/l10n.js b/binaries/data/mods/public/gui/common/l10n.js
index 53c16c2..3fd4570 100644
a
|
b
|
|
1 | | const localisedResourceNames = { |
2 | | "firstWord": { |
3 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
4 | | "food": translateWithContext("firstWord", "Food"), |
5 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
6 | | "meat": translateWithContext("firstWord", "Meat"), |
7 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
8 | | "metal": translateWithContext("firstWord", "Metal"), |
9 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
10 | | "ore": translateWithContext("firstWord", "Ore"), |
11 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
12 | | "rock": translateWithContext("firstWord", "Rock"), |
13 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
14 | | "ruins": translateWithContext("firstWord", "Ruins"), |
15 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
16 | | "stone": translateWithContext("firstWord", "Stone"), |
17 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
18 | | "treasure": translateWithContext("firstWord", "Treasure"), |
19 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
20 | | "tree": translateWithContext("firstWord", "Tree"), |
21 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
22 | | "wood": translateWithContext("firstWord", "Wood"), |
23 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
24 | | "fruit": translateWithContext("firstWord", "Fruit"), |
25 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
26 | | "grain": translateWithContext("firstWord", "Grain"), |
27 | | // Translation: Word as used at the beginning of a sentence or as a single-word sentence. |
28 | | "fish": translateWithContext("firstWord", "Fish"), |
29 | | }, |
30 | | "withinSentence": { |
31 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
32 | | "food": translateWithContext("withinSentence", "Food"), |
33 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
34 | | "meat": translateWithContext("withinSentence", "Meat"), |
35 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
36 | | "metal": translateWithContext("withinSentence", "Metal"), |
37 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
38 | | "ore": translateWithContext("withinSentence", "Ore"), |
39 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
40 | | "rock": translateWithContext("withinSentence", "Rock"), |
41 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
42 | | "ruins": translateWithContext("withinSentence", "Ruins"), |
43 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
44 | | "stone": translateWithContext("withinSentence", "Stone"), |
45 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
46 | | "treasure": translateWithContext("withinSentence", "Treasure"), |
47 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
48 | | "tree": translateWithContext("withinSentence", "Tree"), |
49 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
50 | | "wood": translateWithContext("withinSentence", "Wood"), |
51 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
52 | | "fruit": translateWithContext("withinSentence", "Fruit"), |
53 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
54 | | "grain": translateWithContext("withinSentence", "Grain"), |
55 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
56 | | "fish": translateWithContext("withinSentence", "Fish"), |
57 | | } |
58 | | }; |
59 | 1 | |
60 | | function getLocalizedResourceName(resourceCode, context) |
| 2 | function getLocalizedResourceName(resourceName, context) |
61 | 3 | { |
62 | | if (!localisedResourceNames[context]) |
63 | | { |
64 | | warn("Internationalization: Unexpected context for resource type localization found: ‘" + context + "’. This context is not supported."); |
65 | | return resourceCode; |
66 | | } |
67 | | if (!localisedResourceNames[context][resourceCode]) |
68 | | { |
69 | | warn("Internationalization: Unexpected resource type found with code ‘" + resourceCode + ". This resource type must be internationalized."); |
70 | | return resourceCode; |
71 | | } |
72 | | return localisedResourceNames[context][resourceCode]; |
| 4 | return translateWithContext(context, resourceName); |
73 | 5 | } |
74 | 6 | |
75 | 7 | /** |
… |
… |
function getLocalizedResourceAmounts(resources)
|
81 | 13 | .filter(type => resources[type] > 0) |
82 | 14 | .map(type => sprintf(translate("%(amount)s %(resourceType)s"), { |
83 | 15 | "amount": resources[type], |
84 | | "resourceType": getLocalizedResourceName(type, "withinSentence") |
| 16 | "resourceType": translateWithContext("withinSentence", type) |
85 | 17 | })); |
86 | 18 | |
87 | 19 | if (amounts.length > 1) |
-
diff --git a/binaries/data/mods/public/gui/common/tooltips.js b/binaries/data/mods/public/gui/common/tooltips.js
index 6cebc6d..dcca7b6 100644
a
|
b
|
function getEntityCostComponentsTooltipString(template, trainNum, entity)
|
339 | 339 | |
340 | 340 | return costs; |
341 | 341 | } |
| 342 | |
342 | 343 | function getGatherTooltip(template) |
343 | 344 | { |
344 | 345 | if (!template.gather) |
-
diff --git a/binaries/data/mods/public/gui/session/diplomacy_window.xml b/binaries/data/mods/public/gui/session/diplomacy_window.xml
index 1890513..b03a570 100644
a
|
b
|
|
1 | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | 2 | |
3 | 3 | <object name="diplomacyDialogPanel" |
4 | | size="50%-300 50%-200 50%+300 50%+150" |
| 4 | size="50%-260 50%-200 50%+260 50%+150" |
5 | 5 | type="image" |
6 | 6 | hidden="true" |
7 | 7 | sprite="ModernDialog" |
… |
… |
|
11 | 11 | </object> |
12 | 12 | |
13 | 13 | <object name="diplomacyHeader" size="32 32 100%-32 64"> |
14 | | <object name="diplomacyHeaderName" size="0 0 150 100%" type="text" style="chatPanel" ghost="true"> |
| 14 | <object name="diplomacyHeaderName" size="0 0 140 100%" type="text" style="chatPanel" ghost="true" text_align="center"> |
15 | 15 | <translatableAttribute id="caption">Name</translatableAttribute> |
16 | 16 | </object> |
17 | 17 | <object name="diplomacyHeaderCiv" size="150 0 250 100%" type="text" style="chatPanel" ghost="true"> |
… |
… |
|
23 | 23 | <object name="diplomacyHeaderTheirs" size="300 0 360 100%" type="text" style="chatPanel" ghost="true"> |
24 | 24 | <translatableAttribute id="caption">Theirs</translatableAttribute> |
25 | 25 | </object> |
26 | | <object name="diplomacyHeaderAlly" size="100%-180 0 100%-160 100%" type="text" style="chatPanel" tooltip_style="sessionToolTipBold"> |
| 26 | <object name="diplomacyHeaderAlly" size="360 0 380 100%" type="text" style="chatPanel" tooltip_style="sessionToolTipBold"> |
27 | 27 | <translatableAttribute id="caption">A</translatableAttribute> |
28 | 28 | <translatableAttribute id="tooltip">Ally</translatableAttribute> |
29 | 29 | </object> |
30 | | <object name="diplomacyHeaderNeutral" size="100%-160 0 100%-140 100%" type="text" style="chatPanel" tooltip_style="sessionToolTipBold"> |
| 30 | <object name="diplomacyHeaderNeutral" size="380 0 400 100%" type="text" style="chatPanel" tooltip_style="sessionToolTipBold"> |
31 | 31 | <translatableAttribute id="caption">N</translatableAttribute> |
32 | 32 | <translatableAttribute id="tooltip">Neutral</translatableAttribute> |
33 | 33 | </object> |
34 | | <object name="diplomacyHeaderEnemy" size="100%-140 0 100%-120 100%" type="text" style="chatPanel" tooltip_style="sessionToolTipBold"> |
| 34 | <object name="diplomacyHeaderEnemy" size="400 0 420 100%" type="text" style="chatPanel" tooltip_style="sessionToolTipBold"> |
35 | 35 | <translatableAttribute id="caption">E</translatableAttribute> |
36 | 36 | <translatableAttribute id="tooltip">Enemy</translatableAttribute> |
37 | 37 | </object> |
38 | | <object name="diplomacyHeaderTribute" size="100%-110 0 100% 100%" type="text" style="chatPanel"> |
| 38 | <object name="diplomacyHeaderTribute" size="430 0 100%-30 100%" type="text" style="chatPanel" text_align="center"> |
39 | 39 | <translatableAttribute id="caption">Tribute</translatableAttribute> |
40 | 40 | </object> |
41 | 41 | </object> |
… |
… |
|
48 | 48 | <object name="diplomacyPlayerTheirs[n]" size="300 0 360 100%" type="text" style="chatPanel" ghost="true"/> |
49 | 49 | |
50 | 50 | <!-- Diplomatic stance - selection --> |
51 | | <object name="diplomacyPlayerAlly[n]" size="100%-180 0 100%-160 100%" type="button" style="StoneButton" hidden="true"/> |
52 | | <object name="diplomacyPlayerNeutral[n]" size="100%-160 0 100%-140 100%" type="button" style="StoneButton" hidden="true"/> |
53 | | <object name="diplomacyPlayerEnemy[n]" size="100%-140 0 100%-120 100%" type="button" style="StoneButton" hidden="true"/> |
| 51 | <object name="diplomacyPlayerAlly[n]" size="360 0 380 100%" type="button" style="StoneButton" hidden="true"/> |
| 52 | <object name="diplomacyPlayerNeutral[n]" size="380 0 400 100%" type="button" style="StoneButton" hidden="true"/> |
| 53 | <object name="diplomacyPlayerEnemy[n]" size="400 0 420 100%" type="button" style="StoneButton" hidden="true"/> |
54 | 54 | |
55 | 55 | <!-- Tribute --> |
56 | | <object name="diplomacyPlayerTributeFood[n]" size="100%-110 0 100%-90 100%" type="button" style="iconButton" tooltip_style="sessionToolTipBold" hidden="true"> |
57 | | <object name="diplomacyPlayerTributeFoodImage[n]" type="image" size="0 0 100% 100%" sprite="stretched:session/icons/resources/food.png" ghost="true"/> |
58 | | </object> |
59 | | <object name="diplomacyPlayerTributeWood[n]" size="100%-90 0 100%-70 100%" type="button" style="iconButton" tooltip_style="sessionToolTipBold" hidden="true"> |
60 | | <object name="diplomacyPlayerTributeWoodImage[n]" type="image" size="0 0 100% 100%" sprite="stretched:session/icons/resources/wood.png" ghost="true"/> |
61 | | </object> |
62 | | <object name="diplomacyPlayerTributeStone[n]" size="100%-70 0 100%-50 100%" type="button" style="iconButton" tooltip_style="sessionToolTipBold" hidden="true"> |
63 | | <object name="diplomacyPlayerTributeStoneImage[n]" type="image" size="0 0 100% 100%" sprite="stretched:session/icons/resources/stone.png" ghost="true"/> |
64 | | </object> |
65 | | <object name="diplomacyPlayerTributeMetal[n]" size="100%-50 0 100%-30 100%" type="button" style="iconButton" tooltip_style="sessionToolTipBold" hidden="true"> |
66 | | <object name="diplomacyPlayerTributeMetalImage[n]" type="image" size="0 0 100% 100%" sprite="stretched:session/icons/resources/metal.png" ghost="true"/> |
| 56 | <object size="430 0 100%-40 100%"> |
| 57 | <repeat count="8" var="r"> |
| 58 | <object name="diplomacyPlayer[n]_tribute[r]" size="0 0 20 100%" type="button" style="iconButton" tooltip_style="sessionToolTipBold" hidden="true"> |
| 59 | <object name="diplomacyPlayer[n]_tribute[r]_image" type="image" size="0 0 100% 100%" ghost="true"/> |
| 60 | </object> |
| 61 | </repeat> |
67 | 62 | </object> |
68 | 63 | |
69 | 64 | <object name="diplomacyAttackRequest[n]" size="100%-20 0 100% 100%" type="button" style="iconButton" tooltip_style="sessionToolTipBold" hidden="true"> |
-
diff --git a/binaries/data/mods/public/gui/session/menu.js b/binaries/data/mods/public/gui/session/menu.js
index 83abfc8..c42d1bb 100644
a
|
b
|
const INITIAL_MENU_POSITION = "100%-164 " + MENU_TOP + " 100% " + MENU_BOTTOM;
|
22 | 22 | // Number of pixels per millisecond to move |
23 | 23 | const MENU_SPEED = 1.2; |
24 | 24 | |
25 | | // Available resources in trade and tribute menu |
26 | | const RESOURCES = ["food", "wood", "stone", "metal"]; |
27 | | |
28 | 25 | // Trade menu: step for probability changes |
29 | 26 | const STEP = 5; |
30 | 27 | |
31 | 28 | // Shown in the trade dialog. |
32 | 29 | const g_IdleTraderTextColor = "orange"; |
33 | 30 | |
| 31 | const BARTER_RESOURCE_AMOUNT_TO_SELL = 100; |
| 32 | const BARTER_BUNCH_MULTIPLIER = 5; |
| 33 | const BARTER_ACTIONS = ["Sell", "Buy"]; |
| 34 | var g_BarterSell; |
| 35 | |
34 | 36 | var g_IsMenuOpen = false; |
35 | 37 | |
36 | 38 | var g_IsDiplomacyOpen = false; |
… |
… |
function closeChat()
|
228 | 230 | Engine.GetGUIObjectByName("chatDialogPanel").hidden = true; |
229 | 231 | } |
230 | 232 | |
| 233 | function resizeDiplomacyDialog() |
| 234 | { |
| 235 | let dialog = Engine.GetGUIObjectByName("diplomacyDialogPanel"); |
| 236 | let size = dialog.size; |
| 237 | let width = size.right - size.left; |
| 238 | |
| 239 | let tribSize = Engine.GetGUIObjectByName("diplomacyPlayer[0]_tribute[0]").size; |
| 240 | width += g_ResourceData.GetCodes().length * (tribSize.right - tribSize.left); |
| 241 | |
| 242 | size.left = -width / 2; |
| 243 | size.right = width / 2; |
| 244 | dialog.size = size; |
| 245 | } |
| 246 | |
231 | 247 | function openDiplomacy() |
232 | 248 | { |
233 | 249 | closeOpenDialogs(); |
… |
… |
function openDiplomacy()
|
255 | 271 | diplomacyFormatTributeButtons(i, myself || playerInactive); |
256 | 272 | diplomacyFormatAttackRequestButton(i, myself || playerInactive || isCeasefireActive || !hasAllies || !g_Players[i].isEnemy[g_ViewedPlayer]); |
257 | 273 | } |
258 | | |
259 | 274 | Engine.GetGUIObjectByName("diplomacyDialogPanel").hidden = false; |
260 | 275 | } |
261 | 276 | |
… |
… |
function diplomacyFormatStanceButtons(i, hidden)
|
306 | 321 | |
307 | 322 | function diplomacyFormatTributeButtons(i, hidden) |
308 | 323 | { |
309 | | for (let resource of RESOURCES) |
| 324 | let resNames = g_ResourceData.GetNames(); |
| 325 | let resCodes = g_ResourceData.GetCodes(); |
| 326 | let r = 0; |
| 327 | for (let resCode of resCodes) |
310 | 328 | { |
311 | | let button = Engine.GetGUIObjectByName("diplomacyPlayerTribute"+resource[0].toUpperCase()+resource.substring(1)+"["+(i-1)+"]"); |
| 329 | let button = Engine.GetGUIObjectByName("diplomacyPlayer["+(i-1)+"]_tribute["+r+"]"); |
| 330 | if (!button) |
| 331 | break; |
| 332 | Engine.GetGUIObjectByName("diplomacyPlayer["+(i-1)+"]_tribute["+r+"]_image").sprite = "stretched:session/icons/resources/"+resCode+".png"; |
312 | 333 | button.hidden = hidden; |
| 334 | setPanelObjectPosition(button, r, 8, 0); |
| 335 | ++r; |
313 | 336 | if (hidden) |
314 | 337 | continue; |
315 | 338 | |
316 | 339 | button.enabled = controlsPlayer(g_ViewedPlayer); |
317 | | button.tooltip = formatTributeTooltip(i, resource, 100); |
318 | | button.onpress = (function(i, resource, button) { |
| 340 | button.tooltip = formatTributeTooltip(i, resNames[resCode], 100); |
| 341 | button.onPress = (function(i, resCode, button) { |
319 | 342 | // Shift+click to send 500, shift+click+click to send 1000, etc. |
320 | 343 | // See INPUT_MASSTRIBUTING in input.js |
321 | 344 | let multiplier = 1; |
… |
… |
function diplomacyFormatTributeButtons(i, hidden)
|
328 | 351 | } |
329 | 352 | |
330 | 353 | let amounts = {}; |
331 | | for (let type of RESOURCES) |
332 | | amounts[type] = 0; |
333 | | amounts[resource] = 100 * multiplier; |
| 354 | for (let res of resCodes) |
| 355 | amounts[res] = 0; |
| 356 | amounts[resCode] = 100 * multiplier; |
334 | 357 | |
335 | | button.tooltip = formatTributeTooltip(i, resource, amounts[resource]); |
| 358 | button.tooltip = formatTributeTooltip(i, resNames[resCode], amounts[resCode]); |
336 | 359 | |
337 | 360 | // This is in a closure so that we have access to `player`, `amounts`, and `multiplier` without some |
338 | 361 | // evil global variable hackery. |
339 | 362 | g_FlushTributing = function() { |
340 | 363 | Engine.PostNetworkCommand({ "type": "tribute", "player": i, "amounts": amounts }); |
341 | 364 | multiplier = 1; |
342 | | button.tooltip = formatTributeTooltip(i, resource, 100); |
| 365 | button.tooltip = formatTributeTooltip(i, resNames[resCode], 100); |
343 | 366 | }; |
344 | 367 | |
345 | 368 | if (!isBatchTrainPressed) |
346 | 369 | g_FlushTributing(); |
347 | 370 | }; |
348 | | })(i, resource, button); |
| 371 | })(i, resCode, button); |
349 | 372 | } |
350 | 373 | } |
351 | 374 | |
… |
… |
function toggleDiplomacy()
|
378 | 401 | openDiplomacy(); |
379 | 402 | } |
380 | 403 | |
| 404 | function resizeTradeDialog() |
| 405 | { |
| 406 | let dialog = Engine.GetGUIObjectByName("tradeDialogPanel"); |
| 407 | let size = dialog.size; |
| 408 | let width = size.right - size.left; |
| 409 | |
| 410 | let tradeSize = Engine.GetGUIObjectByName("tradeResource[0]").size; |
| 411 | width += g_ResourceData.GetCodes().length * (tradeSize.right - tradeSize.left); |
| 412 | |
| 413 | size.left = -width / 2; |
| 414 | size.right = width / 2; |
| 415 | dialog.size = size; |
| 416 | } |
| 417 | |
381 | 418 | function openTrade() |
382 | 419 | { |
383 | 420 | closeOpenDialogs(); |
… |
… |
function openTrade()
|
387 | 424 | |
388 | 425 | g_IsTradeOpen = true; |
389 | 426 | |
390 | | var updateButtons = function() |
| 427 | let updateTradeButtons = function() |
391 | 428 | { |
392 | | for (var res in button) |
| 429 | for (let res in button) |
393 | 430 | { |
394 | 431 | button[res].label.caption = proba[res] + "%"; |
395 | 432 | |
… |
… |
function openTrade()
|
399 | 436 | } |
400 | 437 | }; |
401 | 438 | |
402 | | var proba = Engine.GuiInterfaceCall("GetTradingGoods", g_ViewedPlayer); |
403 | | var button = {}; |
404 | | var selec = RESOURCES[0]; |
405 | | for (var i = 0; i < RESOURCES.length; ++i) |
| 439 | let proba = Engine.GuiInterfaceCall("GetTradingGoods", g_ViewedPlayer); |
| 440 | let button = {}; |
| 441 | let resCodes = g_ResourceData.GetCodes(); |
| 442 | let selec = resCodes[0]; |
| 443 | hideRemaining("tradeResources", resCodes.length); |
| 444 | Engine.GetGUIObjectByName("tradeHelp").hidden = false; |
| 445 | |
| 446 | let maxTradeResources = Engine.GetGUIObjectByName("tradeResources").children.length; |
| 447 | let maxBarterResources = Engine.GetGUIObjectByName("barterResources").children.length; |
| 448 | |
| 449 | for (let i = 0; i < resCodes.length; ++i) |
406 | 450 | { |
407 | | var buttonResource = Engine.GetGUIObjectByName("tradeResource["+i+"]"); |
408 | | if (i > 0) |
409 | | { |
410 | | var size = Engine.GetGUIObjectByName("tradeResource["+(i-1)+"]").size; |
411 | | var width = size.right - size.left; |
412 | | size.left += width; |
413 | | size.right += width; |
414 | | Engine.GetGUIObjectByName("tradeResource["+i+"]").size = size; |
415 | | } |
416 | | var resource = RESOURCES[i]; |
417 | | proba[resource] = (proba[resource] ? proba[resource] : 0); |
418 | | var buttonResource = Engine.GetGUIObjectByName("tradeResourceButton["+i+"]"); |
419 | | var icon = Engine.GetGUIObjectByName("tradeResourceIcon["+i+"]"); |
420 | | icon.sprite = "stretched:session/icons/resources/" + resource + ".png"; |
421 | | var label = Engine.GetGUIObjectByName("tradeResourceText["+i+"]"); |
422 | | var buttonUp = Engine.GetGUIObjectByName("tradeArrowUp["+i+"]"); |
423 | | var buttonDn = Engine.GetGUIObjectByName("tradeArrowDn["+i+"]"); |
424 | | var iconSel = Engine.GetGUIObjectByName("tradeResourceSelection["+i+"]"); |
425 | | button[resource] = { "up": buttonUp, "dn": buttonDn, "label": label, "sel": iconSel }; |
| 451 | let resCode = resCodes[i]; |
| 452 | |
| 453 | // Barter |
| 454 | let barterButton = {}; |
| 455 | for (let a of ["Buy", "Sell"]) |
| 456 | barterButton[a] = Engine.GetGUIObjectByName("barter" + a + "Button[" + i + "]"); |
| 457 | |
| 458 | if (!g_BarterSell) |
| 459 | g_BarterSell = g_ResourceData.GetCodes()[0]; |
| 460 | |
| 461 | let resource = getLocalizedResourceName(g_ResourceData.GetNames()[resCode], "withinSentence"); |
| 462 | barterButton.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource }); |
| 463 | barterButton.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource }); |
| 464 | |
| 465 | barterButton.Sell.onPress = function() { |
| 466 | g_BarterSell = resCode; |
| 467 | }; |
| 468 | |
| 469 | setPanelObjectPosition(Engine.GetGUIObjectByName("barterResource[" + i + "]"), i, maxBarterResources); |
| 470 | |
| 471 | // Trade |
| 472 | let tradeResource = Engine.GetGUIObjectByName("tradeResource["+i+"]"); |
| 473 | if (!tradeResource) |
| 474 | break; |
| 475 | |
| 476 | setPanelObjectPosition(tradeResource, i, maxTradeResources); |
| 477 | |
| 478 | let icon = Engine.GetGUIObjectByName("tradeResourceIcon["+i+"]"); |
| 479 | icon.sprite = "stretched:session/icons/resources/" + resCode + ".png"; |
426 | 480 | |
| 481 | let buttonUp = Engine.GetGUIObjectByName("tradeArrowUp["+i+"]"); |
| 482 | let buttonDn = Engine.GetGUIObjectByName("tradeArrowDn["+i+"]"); |
| 483 | |
| 484 | button[resCode] = { |
| 485 | "up": buttonUp, |
| 486 | "dn": buttonDn, |
| 487 | "label": Engine.GetGUIObjectByName("tradeResourceText["+i+"]"), |
| 488 | "sel": Engine.GetGUIObjectByName("tradeResourceSelection["+i+"]") |
| 489 | }; |
| 490 | |
| 491 | proba[resCode] = proba[resCode] || 0; |
| 492 | |
| 493 | let buttonResource = Engine.GetGUIObjectByName("tradeResourceButton["+i+"]"); |
427 | 494 | buttonResource.enabled = controlsPlayer(g_ViewedPlayer); |
428 | 495 | buttonResource.onpress = (function(resource){ |
429 | 496 | return function() { |
430 | 497 | if (Engine.HotkeyIsPressed("session.fulltradeswap")) |
431 | 498 | { |
432 | | for (var ress of RESOURCES) |
433 | | proba[ress] = 0; |
| 499 | for (let res of resCodes) |
| 500 | proba[res] = 0; |
434 | 501 | proba[resource] = 100; |
435 | 502 | Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba}); |
436 | 503 | } |
437 | 504 | selec = resource; |
438 | | updateButtons(); |
| 505 | updateTradeButtons(); |
439 | 506 | }; |
440 | | })(resource); |
| 507 | })(resCode); |
441 | 508 | |
442 | 509 | buttonUp.enabled = controlsPlayer(g_ViewedPlayer); |
443 | 510 | buttonUp.onpress = (function(resource){ |
… |
… |
function openTrade()
|
445 | 512 | proba[resource] += Math.min(STEP, proba[selec]); |
446 | 513 | proba[selec] -= Math.min(STEP, proba[selec]); |
447 | 514 | Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba}); |
448 | | updateButtons(); |
| 515 | updateTradeButtons(); |
449 | 516 | }; |
450 | | })(resource); |
| 517 | })(resCode); |
451 | 518 | |
452 | 519 | buttonDn.enabled = controlsPlayer(g_ViewedPlayer); |
453 | 520 | buttonDn.onpress = (function(resource){ |
… |
… |
function openTrade()
|
455 | 522 | proba[selec] += Math.min(STEP, proba[resource]); |
456 | 523 | proba[resource] -= Math.min(STEP, proba[resource]); |
457 | 524 | Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba}); |
458 | | updateButtons(); |
| 525 | updateTradeButtons(); |
459 | 526 | }; |
460 | | })(resource); |
| 527 | })(resCode); |
461 | 528 | } |
462 | | updateButtons(); |
| 529 | updateTradeButtons(); |
463 | 530 | |
464 | 531 | let traderNumber = Engine.GuiInterfaceCall("GetTraderNumber", g_ViewedPlayer); |
465 | | Engine.GetGUIObjectByName("landTraders").caption = getIdleLandTradersText(traderNumber); |
466 | | Engine.GetGUIObjectByName("shipTraders").caption = getIdleShipTradersText(traderNumber); |
467 | | |
| 532 | Engine.GetGUIObjectByName("traders").caption = getIdleLandTradersText(traderNumber) + "\n\n" + getIdleShipTradersText(traderNumber); |
468 | 533 | Engine.GetGUIObjectByName("tradeDialogPanel").hidden = false; |
469 | 534 | } |
470 | 535 | |
| 536 | function updateBarterButtons() |
| 537 | { |
| 538 | let resCodes = g_ResourceData.GetCodes(); |
| 539 | if (!g_BarterSell) |
| 540 | g_BarterSell = resCodes[0]; |
| 541 | |
| 542 | let canBarter = Engine.GuiInterfaceCall("PlayerCanBarter", g_ViewedPlayer); |
| 543 | Engine.GetGUIObjectByName("barterNoMarketsMessage").hidden = canBarter; |
| 544 | Engine.GetGUIObjectByName("barterResources").hidden = !canBarter; |
| 545 | if (!canBarter) |
| 546 | return; |
| 547 | |
| 548 | for (let i = 0; i < resCodes.length; ++i) |
| 549 | { |
| 550 | let resCode = resCodes[i]; |
| 551 | let barterButton = {}; |
| 552 | let barterIcon = {}; |
| 553 | let barterAmount = {}; |
| 554 | for (let a of ["Buy", "Sell"]) |
| 555 | { |
| 556 | barterButton[a] = Engine.GetGUIObjectByName("barter" + a + "Button[" + i + "]"); |
| 557 | barterIcon[a] = Engine.GetGUIObjectByName("barter" + a + "Icon[" + i + "]"); |
| 558 | barterAmount[a] = Engine.GetGUIObjectByName("barter" + a + "Amount[" + i + "]"); |
| 559 | } |
| 560 | let selectionIcon = Engine.GetGUIObjectByName("barterSellSelection[" + i + "]"); |
| 561 | |
| 562 | |
| 563 | let amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL; |
| 564 | if (Engine.HotkeyIsPressed("session.massbarter")) |
| 565 | amountToSell *= BARTER_BUNCH_MULTIPLIER; |
| 566 | |
| 567 | let isSelected = resCode == g_BarterSell; |
| 568 | let grayscale = isSelected ? "color: 0 0 0 100:grayscale:" : ""; |
| 569 | |
| 570 | // do we have enough of this resource to sell? |
| 571 | let neededRes = {}; |
| 572 | neededRes[resCode] = amountToSell; |
| 573 | let canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", { |
| 574 | "cost": neededRes, |
| 575 | "player": g_ViewedPlayer, |
| 576 | }) ? "color:255 0 0 80:" : ""; |
| 577 | |
| 578 | // Let's see if we have enough resources to barter. |
| 579 | neededRes = {}; |
| 580 | neededRes[g_BarterSell] = amountToSell; |
| 581 | let canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", { |
| 582 | "cost": neededRes, |
| 583 | "player": g_ViewedPlayer, |
| 584 | }) ? "color:255 0 0 80:" : ""; |
| 585 | |
| 586 | barterIcon.Sell.sprite = canSellCurrent + "stretched:" + grayscale + "session/icons/resources/" + resCode + ".png"; |
| 587 | barterIcon.Buy.sprite = canBuyAny + "stretched:" + grayscale + "session/icons/resources/" + resCode + ".png"; |
| 588 | |
| 589 | barterAmount.Sell.caption = "-" + amountToSell; |
| 590 | let prices = Engine.GuiInterfaceCall("GetBarterPrices"); |
| 591 | barterAmount.Buy.caption = "+" + Math.round(prices.sell[g_BarterSell] / prices.buy[resCode] * amountToSell); |
| 592 | |
| 593 | barterButton.Buy.onPress = function() { |
| 594 | Engine.PostNetworkCommand({ |
| 595 | "type": "barter", |
| 596 | "sell": g_BarterSell, |
| 597 | "buy": resCode, |
| 598 | "amount": amountToSell |
| 599 | }); |
| 600 | }; |
| 601 | |
| 602 | barterButton.Buy.hidden = isSelected; |
| 603 | barterButton.Buy.enabled = controlsPlayer(g_ViewedPlayer); |
| 604 | barterButton.Sell.hidden = false; |
| 605 | selectionIcon.hidden = !isSelected; |
| 606 | } |
| 607 | }; |
| 608 | |
471 | 609 | function getIdleLandTradersText(traderNumber) |
472 | 610 | { |
473 | 611 | let active = traderNumber.landTrader.trading; |
-
diff --git a/binaries/data/mods/public/gui/session/selection_details.js b/binaries/data/mods/public/gui/session/selection_details.js
index a288fe6..9915232 100644
a
|
b
|
function layoutSelectionMultiple()
|
12 | 12 | |
13 | 13 | function getResourceTypeDisplayName(resourceType) |
14 | 14 | { |
15 | | let resourceCode = resourceType.generic; |
16 | | if (resourceCode == "treasure") |
17 | | return getLocalizedResourceName(resourceType.specific, "firstWord"); |
18 | | else |
19 | | return getLocalizedResourceName(resourceCode, "firstWord"); |
| 15 | return getLocalizedResourceName( |
| 16 | g_ResourceData.GetNames()[ |
| 17 | resourceType.generic == "treasure" ? |
| 18 | resourceType.specific : |
| 19 | resourceType.generic |
| 20 | ], |
| 21 | "firstWord"); |
20 | 22 | } |
21 | 23 | |
22 | 24 | // Updates the health bar of garrisoned units |
-
diff --git a/binaries/data/mods/public/gui/session/selection_panels.js b/binaries/data/mods/public/gui/session/selection_panels.js
index fb12b00..487f5b9 100644
a
|
b
|
g_SelectionPanels.Alert = {
|
84 | 84 | } |
85 | 85 | }; |
86 | 86 | |
87 | | g_SelectionPanels.Barter = { |
88 | | "getMaxNumberOfItems": function() |
89 | | { |
90 | | return 4; |
91 | | }, |
92 | | "rowLength": 4, |
93 | | "getItems": function(unitEntState, selection) |
94 | | { |
95 | | if (!unitEntState.barterMarket) |
96 | | return []; |
97 | | // ["food", "wood", "stone", "metal"] |
98 | | return BARTER_RESOURCES; |
99 | | }, |
100 | | "setupButton": function(data) |
101 | | { |
102 | | // data.item is the resource name in this case |
103 | | let button = {}; |
104 | | let icon = {}; |
105 | | let amount = {}; |
106 | | for (let a of BARTER_ACTIONS) |
107 | | { |
108 | | button[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Button[" + data.i + "]"); |
109 | | icon[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Icon[" + data.i + "]"); |
110 | | amount[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Amount[" + data.i + "]"); |
111 | | } |
112 | | let selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection[" + data.i + "]"); |
113 | | |
114 | | let amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL; |
115 | | if (Engine.HotkeyIsPressed("session.massbarter")) |
116 | | amountToSell *= BARTER_BUNCH_MULTIPLIER; |
117 | | |
118 | | amount.Sell.caption = "-" + amountToSell; |
119 | | let prices = data.unitEntState.barterMarket.prices; |
120 | | amount.Buy.caption = "+" + Math.round(prices.sell[g_BarterSell] / prices.buy[data.item] * amountToSell); |
121 | | |
122 | | let resource = getLocalizedResourceName(data.item, "withinSentence"); |
123 | | button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource }); |
124 | | button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource }); |
125 | | |
126 | | button.Sell.onPress = function() { |
127 | | g_BarterSell = data.item; |
128 | | updateSelectionDetails(); |
129 | | }; |
130 | | |
131 | | button.Buy.onPress = function() { |
132 | | Engine.PostNetworkCommand({ |
133 | | "type": "barter", |
134 | | "sell": g_BarterSell, |
135 | | "buy": data.item, |
136 | | "amount": amountToSell |
137 | | }); |
138 | | }; |
139 | | |
140 | | let isSelected = data.item == g_BarterSell; |
141 | | let grayscale = isSelected ? "color: 0 0 0 100:grayscale:" : ""; |
142 | | |
143 | | // do we have enough of this resource to sell? |
144 | | let neededRes = {}; |
145 | | neededRes[data.item] = amountToSell; |
146 | | let canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", { |
147 | | "cost": neededRes, |
148 | | "player": data.unitEntState.player |
149 | | }) ? "color:255 0 0 80:" : ""; |
150 | | |
151 | | // Let's see if we have enough resources to barter. |
152 | | neededRes = {}; |
153 | | neededRes[g_BarterSell] = amountToSell; |
154 | | let canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", { |
155 | | "cost": neededRes, |
156 | | "player": data.unitEntState.player |
157 | | }) ? "color:255 0 0 80:" : ""; |
158 | | |
159 | | icon.Sell.sprite = canSellCurrent + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png"; |
160 | | icon.Buy.sprite = canBuyAny + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png"; |
161 | | |
162 | | button.Buy.hidden = isSelected; |
163 | | button.Buy.enabled = controlsPlayer(data.unitEntState.player); |
164 | | button.Sell.hidden = false; |
165 | | selectionIcon.hidden = !isSelected; |
166 | | |
167 | | setPanelObjectPosition(button.Sell, data.i, data.rowLength); |
168 | | setPanelObjectPosition(button.Buy, data.i + data.rowLength, data.rowLength); |
169 | | return true; |
170 | | } |
171 | | }; |
172 | | |
173 | 87 | g_SelectionPanels.Command = { |
174 | 88 | "getMaxNumberOfItems": function() |
175 | 89 | { |
… |
… |
g_SelectionPanels.Upgrade = {
|
1144 | 1058 | */ |
1145 | 1059 | let g_PanelsOrder = [ |
1146 | 1060 | // LEFT PANE |
1147 | | "Barter", // Must always be visible on markets |
1148 | 1061 | "Garrison", // More important than Formation, as you want to see the garrisoned units in ships |
1149 | 1062 | "Alert", |
1150 | 1063 | "Formation", |
-
diff --git a/binaries/data/mods/public/gui/session/selection_panels_helpers.js b/binaries/data/mods/public/gui/session/selection_panels_helpers.js
index c224597..e5b1f03 100644
a
|
b
|
|
1 | | const BARTER_RESOURCE_AMOUNT_TO_SELL = 100; |
2 | | const BARTER_BUNCH_MULTIPLIER = 5; |
3 | | const BARTER_RESOURCES = ["food", "wood", "stone", "metal"]; |
4 | | const BARTER_ACTIONS = ["Sell", "Buy"]; |
5 | 1 | const GATE_ACTIONS = ["lock", "unlock"]; |
6 | 2 | |
7 | | // upgrade constants |
8 | 3 | const UPGRADING_NOT_STARTED = -2; |
9 | 4 | const UPGRADING_CHOSEN_OTHER = -1; |
10 | 5 | |
11 | | // ============================================== |
12 | | // BARTER HELPERS |
13 | | // Resources to sell on barter panel |
14 | | var g_BarterSell = "food"; |
15 | | |
16 | 6 | function canMoveSelectionIntoFormation(formationTemplate) |
17 | 7 | { |
18 | 8 | if (!(formationTemplate in g_canMoveIntoFormation)) |
-
diff --git a/binaries/data/mods/public/gui/session/selection_panels_left/barter_panel.xml b/binaries/data/mods/public/gui/session/selection_panels_left/barter_panel.xml
deleted file mode 100644
index 46b8b7b..0000000
+
|
-
|
|
1 | | <?xml version="1.0" encoding="utf-8"?> |
2 | | <object name="unitBarterPanel" |
3 | | size="6 36 100% 100%" |
4 | | hidden="true" |
5 | | > |
6 | | <object ghost="true" style="resourceText" type="text" size="0 0 100% 20"> |
7 | | <translatableAttribute id="tooltip">Exchange resources:</translatableAttribute> |
8 | | </object> |
9 | | <object size="0 32 100% 124"> |
10 | | <repeat count="4"> |
11 | | <!-- sell --> |
12 | | <object name="unitBarterSellButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottomBold"> |
13 | | <object name="unitBarterSellIcon[n]" type="image" ghost="true" size="3 3 43 43"/> |
14 | | <object name="unitBarterSellAmount[n]" ghost="true" style="resourceText" type="text" size="0 0 100% 50%"/> |
15 | | <object name="unitBarterSellSelection[n]" hidden="true" type="image" ghost="true" size="3 3 43 43" sprite="stretched:session/icons/corners.png"/> |
16 | | </object> |
17 | | <!-- buy --> |
18 | | <object name="unitBarterBuyButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottomBold"> |
19 | | <object name="unitBarterBuyIcon[n]" type="image" ghost="true" size="3 3 43 43"/> |
20 | | <object name="unitBarterBuyAmount[n]" ghost="true" style="resourceText" type="text" size="0 0 100% 50%"/> |
21 | | </object> |
22 | | </repeat> |
23 | | </object> |
24 | | </object> |
-
diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js
index d82312e..5548b2d 100644
a
|
b
|
var g_EntityStates = {};
|
130 | 130 | var g_TemplateData = {}; |
131 | 131 | var g_TemplateDataWithoutLocalization = {}; |
132 | 132 | var g_TechnologyData = {}; |
| 133 | var g_ResourceData = new Resources(); |
133 | 134 | |
134 | 135 | /** |
135 | 136 | * Top coordinate of the research list. |
… |
… |
function init(initData, hotloadData)
|
268 | 269 | let gameSpeedIdx = g_GameSpeeds.Speed.indexOf(Engine.GetSimRate()); |
269 | 270 | gameSpeed.selected = gameSpeedIdx != -1 ? gameSpeedIdx : g_GameSpeeds.Default; |
270 | 271 | gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); }; |
| 272 | |
271 | 273 | initMenuPosition(); |
| 274 | resizeDiplomacyDialog(); |
| 275 | resizeTradeDialog(); |
272 | 276 | |
273 | 277 | for (let slot in Engine.GetGUIObjectByName("unitHeroPanel").children) |
274 | 278 | initGUIHeroes(slot); |
… |
… |
function updateTopPanel()
|
474 | 478 | let viewPlayer = Engine.GetGUIObjectByName("viewPlayer"); |
475 | 479 | viewPlayer.hidden = !g_IsObserver && !g_DevSettings.changePerspective; |
476 | 480 | |
477 | | Engine.GetGUIObjectByName("food").hidden = !isPlayer; |
478 | | Engine.GetGUIObjectByName("wood").hidden = !isPlayer; |
479 | | Engine.GetGUIObjectByName("stone").hidden = !isPlayer; |
480 | | Engine.GetGUIObjectByName("metal").hidden = !isPlayer; |
| 481 | let resCodes = g_ResourceData.GetCodes(); |
| 482 | let r = 0; |
| 483 | for (let res of resCodes) |
| 484 | { |
| 485 | if (!Engine.GetGUIObjectByName("resource["+r+"]")) |
| 486 | { |
| 487 | warn("Current GUI limits prevent displaying more than " + r + " resources at the top of the screen"); |
| 488 | break; |
| 489 | } |
| 490 | Engine.GetGUIObjectByName("resource["+r+"]_icon").sprite = "stretched:session/icons/resources/" + res + ".png"; |
| 491 | Engine.GetGUIObjectByName("resource["+r+"]").hidden = !isPlayer; |
| 492 | ++r; |
| 493 | } |
| 494 | horizontallySpaceObjects("resourceCounts", 0); |
| 495 | hideRemaining("resourceCounts", r); |
| 496 | |
| 497 | let resPop = Engine.GetGUIObjectByName("population"); |
| 498 | let resPopSize = resPop.size; |
| 499 | resPopSize.left = Engine.GetGUIObjectByName("resource["+ (r-1) +"]").size.right; |
| 500 | resPop.size = resPopSize; |
| 501 | |
481 | 502 | Engine.GetGUIObjectByName("population").hidden = !isPlayer; |
482 | 503 | Engine.GetGUIObjectByName("diplomacyButton1").hidden = !isPlayer; |
483 | 504 | Engine.GetGUIObjectByName("tradeButton1").hidden = !isPlayer; |
… |
… |
function leaveGame(willRejoin)
|
547 | 568 | "disconnected": g_Disconnected, |
548 | 569 | "isReplay": g_IsReplay, |
549 | 570 | "replayDirectory": !g_HasRejoined && replayDirectory, |
550 | | "replaySelectionData": g_ReplaySelectionData |
| 571 | "replaySelectionData": g_ReplaySelectionData, |
551 | 572 | } |
552 | 573 | }); |
553 | 574 | } |
… |
… |
function updateGUIObjects()
|
714 | 735 | updateBuildingPlacementPreview(); |
715 | 736 | updateTimeNotifications(); |
716 | 737 | updateIdleWorkerButton(); |
| 738 | updateBarterButtons(); |
717 | 739 | |
718 | 740 | if (g_ViewedPlayer > 0) |
719 | 741 | { |
… |
… |
function updatePlayerDisplay()
|
948 | 970 | if (!playerState) |
949 | 971 | return; |
950 | 972 | |
951 | | for (let res of RESOURCES) |
| 973 | let resCodes = g_ResourceData.GetCodes(); |
| 974 | let resNames = g_ResourceData.GetNames(); |
| 975 | for (let r = 0; r < resCodes.length; ++r) |
952 | 976 | { |
953 | | Engine.GetGUIObjectByName("resource_" + res).caption = Math.floor(playerState.resourceCounts[res]); |
954 | | Engine.GetGUIObjectByName(res).tooltip = getLocalizedResourceName(res, "firstWord") + getAllyStatTooltip(res); |
| 977 | if (!Engine.GetGUIObjectByName("resource["+r+"]")) |
| 978 | break; |
| 979 | let res = resCodes[r]; |
| 980 | Engine.GetGUIObjectByName("resource["+r+"]").tooltip = getLocalizedResourceName(resNames[res], "firstWord") + getAllyStatTooltip(res); |
| 981 | Engine.GetGUIObjectByName("resource["+r+"]_count").caption = Math.floor(playerState.resourceCounts[res]); |
955 | 982 | } |
956 | 983 | |
957 | 984 | Engine.GetGUIObjectByName("resourcePop").caption = sprintf(translate("%(popCount)s/%(popLimit)s"), playerState); |
-
diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml
index 7355a6a..9b1ce7a 100644
a
|
b
|
|
111 | 111 | |
112 | 112 | <!-- Supplemental Details Panel (Left of Selection Details) --> |
113 | 113 | <object |
114 | | size="50%-304 100%-170 50%-110 100%" |
| 114 | size="50%-304 100%-172 50%-110 100%" |
115 | 115 | name="supplementalSelectionDetails" |
116 | 116 | type="image" |
117 | 117 | sprite="supplementalDetailsPanel" |
-
diff --git a/binaries/data/mods/public/gui/session/top_panel/button_trade.xml b/binaries/data/mods/public/gui/session/top_panel/button_trade.xml
index 787c1f7..6f59e92 100644
a
|
b
|
|
7 | 7 | > |
8 | 8 | <!-- TODO make the button less ugly --> |
9 | 9 | <object size="0 0 100% 100%" name="tradeButtonImage" type="image" sprite="stretched:session/icons/economics.png" ghost="true"/> |
10 | | <translatableAttribute id="tooltip">Trade</translatableAttribute> |
| 10 | <translatableAttribute id="tooltip">Barter & Trade</translatableAttribute> |
11 | 11 | <action on="Press"> |
12 | 12 | toggleTrade(); |
13 | 13 | </action> |
-
diff --git a/binaries/data/mods/public/gui/session/top_panel/resource_food.xml b/binaries/data/mods/public/gui/session/top_panel/resource_food.xml
deleted file mode 100644
index 4d84ca7..0000000
+
|
-
|
|
1 | | <?xml version="1.0" encoding="utf-8"?> |
2 | | <object name="food" size="10 0 100 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold"> |
3 | | <object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/food.png" ghost="true"/> |
4 | | <object size="32 0 100% 100%-2" type="text" style="resourceText" name="resource_food"/> |
5 | | </object> |
-
diff --git a/binaries/data/mods/public/gui/session/top_panel/resource_metal.xml b/binaries/data/mods/public/gui/session/top_panel/resource_metal.xml
deleted file mode 100644
index 4edba79..0000000
+
|
-
|
|
1 | | <?xml version="1.0" encoding="utf-8"?> |
2 | | <object name="metal" size="280 0 370 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold"> |
3 | | <object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/metal.png" ghost="true"/> |
4 | | <object size="32 0 100% 100%-2" type="text" style="resourceText" name="resource_metal"/> |
5 | | </object> |
-
diff --git a/binaries/data/mods/public/gui/session/top_panel/resource_population.xml b/binaries/data/mods/public/gui/session/top_panel/resource_population.xml
index 9c9dcc2..ffffe6b 100644
a
|
b
|
|
1 | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | | <object name="population" size="370 0 460 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold"> |
3 | | <object size="0 -4 40 34" type="image" sprite="stretched:session/icons/resources/population.png" ghost="true"/> |
| 2 | |
| 3 | <object name="population" size="0 0 50%-52 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold"> |
| 4 | <object size="0 -2 40 38" type="image" sprite="stretched:session/icons/resources/population.png" ghost="true"/> |
4 | 5 | <object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourcePop"/> |
5 | 6 | </object> |
-
diff --git a/binaries/data/mods/public/gui/session/top_panel/resource_stone.xml b/binaries/data/mods/public/gui/session/top_panel/resource_stone.xml
deleted file mode 100644
index 6133acc..0000000
+
|
-
|
|
1 | | <?xml version="1.0" encoding="utf-8"?> |
2 | | <object name="stone" size="190 0 280 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold"> |
3 | | <object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/stone.png" ghost="true"/> |
4 | | <object size="32 0 100% 100%-2" type="text" style="resourceText" name="resource_stone"/> |
5 | | </object> |
-
diff --git a/binaries/data/mods/public/gui/session/top_panel/resource_wood.xml b/binaries/data/mods/public/gui/session/top_panel/resource_wood.xml
deleted file mode 100644
index f020979..0000000
+
|
-
|
|
1 | | <?xml version="1.0" encoding="utf-8"?> |
2 | | <object name="wood" size="100 0 190 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold"> |
3 | | <object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/wood.png" ghost="true"/> |
4 | | <object size="32 0 100% 100%-2" type="text" style="resourceText" name="resource_wood"/> |
5 | | </object> |
-
diff --git a/binaries/data/mods/public/gui/session/top_panel/resources.xml b/binaries/data/mods/public/gui/session/top_panel/resources.xml
new file mode 100644
index 0000000..190c20e
-
|
+
|
|
| 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | |
| 3 | <object size="0 0 50%-90-52 100%" name="resourceCounts"> |
| 4 | <repeat count="5"> |
| 5 | <object name="resource[n]" size="0 0 75.6 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold"> |
| 6 | <object size="0 -2 40 38" type="image" name="resource[n]_icon" ghost="true"/> |
| 7 | <object size="28 0 100%-2 100%-2" type="text" style="resourceText" name="resource[n]_count"/> |
| 8 | </object> |
| 9 | </repeat> |
| 10 | </object> |
-
diff --git a/binaries/data/mods/public/gui/session/trade_window.xml b/binaries/data/mods/public/gui/session/trade_window.xml
index ec9ed10..14f6905 100644
a
|
b
|
|
1 | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | 2 | <object name="tradeDialogPanel" |
3 | | size="50%-250 50%-130 50%+250 50%+100" |
| 3 | size="50%-80 50%-280 50%+80 50%+136" |
4 | 4 | type="image" |
5 | 5 | hidden="true" |
6 | 6 | sprite="ModernDialog" |
| 7 | z="100" |
7 | 8 | > |
8 | 9 | <object type="text" style="TitleText" size="50%-96 -16 50%+96 16"> |
9 | | <translatableAttribute id="caption">Trade</translatableAttribute> |
| 10 | <translatableAttribute id="caption">Barter & Trade Goods</translatableAttribute> |
10 | 11 | </object> |
11 | 12 | |
12 | | <!-- Trading goods --> |
13 | | <object name="tradeGoods" size="20 50 100%-20 82"> |
14 | | <object name="tradeHeader" size="0 0 180 100%" type="text" style="ModernLabelText" text_align="left" ghost="true"> |
15 | | <translatableAttribute id="caption">Trading goods selection:</translatableAttribute> |
| 13 | |
| 14 | <!-- Barter Goods --> |
| 15 | <object size="24 24 100%-24 33%"> |
| 16 | |
| 17 | <object name="barterHeader" size="8 0 100% 32" type="text" style="ModernLeftLabelText"> |
| 18 | <translatableAttribute id="caption">Barter</translatableAttribute> |
16 | 19 | </object> |
| 20 | <object size="0 28 100% 29" type="image" sprite="ModernGoldLine"/> |
| 21 | |
| 22 | <object size="0 38 100% 38+84"> |
| 23 | |
| 24 | <object size="0 0 60 41" type="text" style="ModernRightLabelText"> |
| 25 | <translatableAttribute id="caption">Sell:</translatableAttribute> |
| 26 | </object> |
| 27 | |
| 28 | <object size="0 100%-41 60 100%" type="text" style="ModernRightLabelText"> |
| 29 | <translatableAttribute id="caption">Buy:</translatableAttribute> |
| 30 | </object> |
| 31 | |
| 32 | <object size="72 0 100% 100%" type="text" style="ModernLabelText" name="barterNoMarketsMessage"> |
| 33 | <translatableAttribute id="caption">No Markets Available</translatableAttribute> |
| 34 | </object> |
| 35 | |
| 36 | <object name="barterResources" size="72 0 100% 100%"> |
| 37 | <repeat count="8"> |
| 38 | <object name="barterResource[n]" size="0 0 58 100%"> |
| 39 | |
| 40 | <!-- Sell --> |
| 41 | <object name="barterSellButton[n]" style="iconButton" type="button" size="0 0 41 41" tooltip_style="sessionToolTipBottomBold" hidden="true"> |
| 42 | <object name="barterSellIcon[n]" type="image" ghost="true" size="3 3 100%-3 100%-3"/> |
| 43 | <object name="barterSellAmount[n]" ghost="true" style="resourceText" type="text" size="0 0 100% 50%"/> |
| 44 | <object name="barterSellSelection[n]" hidden="true" type="image" ghost="true" size="3 3 100%-3 100%-3" sprite="stretched:session/icons/corners.png"/> |
| 45 | </object> |
| 46 | |
| 47 | <!-- Buy --> |
| 48 | <object name="barterBuyButton[n]" style="iconButton" type="button" size="0 100%-41 41 100%" tooltip_style="sessionToolTipBottomBold" hidden="true"> |
| 49 | <object name="barterBuyIcon[n]" type="image" ghost="true" size="3 3 100%-3 100%-3"/> |
| 50 | <object name="barterBuyAmount[n]" ghost="true" style="resourceText" type="text" size="0 0 100% 50%"/> |
| 51 | </object> |
17 | 52 | |
18 | | <object size="180 0 100% 100%"> |
19 | | <repeat count="4"> |
20 | | <object name="tradeResource[n]" size="0 0 58 32"> |
21 | | <object name="tradeResourceButton[n]" size="4 0 36 100%" type="button" style="StoneButton"> |
22 | | <object name="tradeResourceIcon[n]" type="image" ghost="true"/> |
23 | | <object name="tradeResourceSelection[n]" type="image" sprite="stretched:session/icons/corners.png" ghost="true"/> |
24 | | <object name="tradeResourceText[n]" type="text" style="ModernLabelText" ghost="true"/> |
25 | | </object> |
26 | | <object name="tradeArrowUp[n]" size="36 0 52 50%" type="button" style="iconButton"> |
27 | | <object type="image" ghost="true" sprite="StoneArrowUp"/> |
28 | 53 | </object> |
29 | | <object name="tradeArrowDn[n]" size="36 50% 52 100%" type="button" style="iconButton"> |
30 | | <object type="image" ghost="true" sprite="StoneArrowDn"/> |
| 54 | </repeat> |
| 55 | </object> |
| 56 | |
| 57 | </object> |
| 58 | </object> |
| 59 | |
| 60 | <!-- Trading goods --> |
| 61 | <object size="24 33%+32 100%-24 100%-64"> |
| 62 | |
| 63 | <object name="tradeHeader" size="8 0 100% 32" type="text" style="ModernLeftLabelText"> |
| 64 | <translatableAttribute id="caption">Trade</translatableAttribute> |
| 65 | </object> |
| 66 | <object size="0 28 100% 29" type="image" sprite="ModernGoldLine"/> |
| 67 | |
| 68 | <object name="tradeGoods" size="0 38 100% 38+32"> |
| 69 | |
| 70 | <object size="0 0 60 100%" type="text" style="ModernRightLabelText"> |
| 71 | <translatableAttribute id="caption">Goods:</translatableAttribute> |
| 72 | </object> |
| 73 | |
| 74 | <object size="72 0 100% 100%" name="tradeResources"> |
| 75 | <repeat count="8"> |
| 76 | <object name="tradeResource[n]" size="0 0 58 32"> |
| 77 | |
| 78 | <object name="tradeResourceButton[n]" size="4 0 36 100%" type="button" style="StoneButton"> |
| 79 | <object name="tradeResourceIcon[n]" type="image" ghost="true"/> |
| 80 | <object name="tradeResourceSelection[n]" type="image" sprite="stretched:session/icons/corners.png" ghost="true"/> |
| 81 | <object name="tradeResourceText[n]" type="text" style="ModernLabelText" ghost="true"/> |
| 82 | </object> |
| 83 | <object name="tradeArrowUp[n]" size="36 0 52 50%" type="button" style="iconButton"> |
| 84 | <object type="image" ghost="true" sprite="StoneArrowUp"/> |
| 85 | </object> |
| 86 | <object name="tradeArrowDn[n]" size="36 50% 52 100%" type="button" style="iconButton"> |
| 87 | <object type="image" ghost="true" sprite="StoneArrowDn"/> |
| 88 | </object> |
| 89 | |
31 | 90 | </object> |
32 | | </object> |
33 | | </repeat> |
| 91 | </repeat> |
| 92 | </object> |
| 93 | |
34 | 94 | <object name="tradeHelp" size="100%-24 4 100% 28" enabled="false" type="button" style="StoneButton" tooltip_style="sessionToolTipBold"> |
35 | 95 | <object size="20% 15% 80% 75%" type="image" ghost="true" sprite="iconInfoWhite"/> |
36 | 96 | </object> |
| 97 | |
37 | 98 | </object> |
38 | | </object> |
39 | 99 | |
40 | | <object name="tradeStatistics" size="20 90 100%-20 168"> |
41 | | <object name="landTraders" size="0 0 100% 50%" type="text" style="ModernLabelText" text_align="left" ghost="true" /> |
42 | | <object name="shipTraders" size="0 50% 100% 100%" type="text" style="ModernLabelText" text_align="left" ghost="true" /> |
| 100 | <object name="traders" size="8 88 100% 100%" type="text" style="ModernLeftTabLabelText"/> |
| 101 | |
43 | 102 | </object> |
44 | 103 | |
45 | 104 | <object size="50%-64 100%-50 50%+64 100%-22" type="button" style="StoneButton"> |
-
diff --git a/binaries/data/mods/public/gui/session/unit_actions.js b/binaries/data/mods/public/gui/session/unit_actions.js
index 03e9765..13948c6 100644
a
|
b
|
var g_EntityCommands =
|
1208 | 1208 | return false; |
1209 | 1209 | |
1210 | 1210 | return { |
1211 | | "tooltip": translate("Select trading goods"), |
| 1211 | "tooltip": translate("Barter & Trade"), |
1212 | 1212 | "icon": "economics.png" |
1213 | 1213 | }; |
1214 | 1214 | }, |
-
diff --git a/binaries/data/mods/public/gui/structree/draw.js b/binaries/data/mods/public/gui/structree/draw.js
index 06f1e39..7d3fc8b 100644
a
|
b
|
function draw()
|
112 | 112 | if (p>c) |
113 | 113 | c = p; |
114 | 114 | |
115 | | hideRemaining("phase["+i+"]_struct["+s+"]_row["+r+"]_prod[", p, "]"); |
| 115 | hideRemaining("phase["+i+"]_struct["+s+"]_row["+r+"]", p); |
116 | 116 | } |
117 | 117 | |
118 | 118 | let size = thisEle.size; |
… |
… |
function draw()
|
132 | 132 | phaEle.size = size; |
133 | 133 | } |
134 | 134 | ++r; |
135 | | hideRemaining("phase["+i+"]_struct["+s+"]_row[", r, "]"); |
| 135 | hideRemaining("phase["+i+"]_struct["+s+"]_rows", r); |
136 | 136 | ++s; |
137 | 137 | } |
138 | | hideRemaining("phase["+i+"]_struct[", s, "]"); |
| 138 | hideRemaining("phase["+i+"]", s); |
139 | 139 | ++i; |
140 | 140 | } |
141 | 141 | |
… |
… |
function draw()
|
179 | 179 | ++p; |
180 | 180 | } |
181 | 181 | } |
182 | | hideRemaining("trainer["+t+"]_prod[", p, "]"); |
| 182 | hideRemaining("trainer["+t+"]_row", p); |
183 | 183 | |
184 | 184 | let size = thisEle.size; |
185 | 185 | size.right = size.left + Math.max(p*24, defWidth) + 4; |
… |
… |
function draw()
|
193 | 193 | phaEle.size = size; |
194 | 194 | ++t; |
195 | 195 | } |
196 | | hideRemaining("trainer[", t, "]"); |
| 196 | hideRemaining("trainers", t); |
197 | 197 | |
198 | 198 | let size = Engine.GetGUIObjectByName("display_tree").size; |
199 | 199 | size.right = t > 0 ? -124 : -4; |
… |
… |
function getPositionOffset(idx)
|
235 | 235 | return size; |
236 | 236 | } |
237 | 237 | |
238 | | function hideRemaining(prefix, idx, suffix) |
239 | | { |
240 | | let obj = Engine.GetGUIObjectByName(prefix + idx + suffix); |
241 | | while (obj) |
242 | | { |
243 | | obj.hidden = true; |
244 | | ++idx; |
245 | | obj = Engine.GetGUIObjectByName(prefix + idx + suffix); |
246 | | } |
247 | | } |
248 | | |
249 | | |
250 | 238 | /** |
251 | 239 | * Positions certain elements that only need to be positioned once |
252 | 240 | * (as <repeat> does not reposition automatically). |
… |
… |
function predraw()
|
283 | 271 | prodBarIcon.sprite = "stretched:session/portraits/"+g_ParsedData.phases[phaseList[i+j]].icon; |
284 | 272 | } |
285 | 273 | // Hide remaining prod bars |
286 | | hideRemaining("phase["+i+"]_bar[", j-1, "]"); |
| 274 | hideRemaining("phase["+i+"]_bars", j-1); |
287 | 275 | |
288 | 276 | let s = 0; |
289 | 277 | let ele = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]"); |
… |
… |
function predraw()
|
334 | 322 | g_DrawLimits[pha].structQuant = s; |
335 | 323 | ++i; |
336 | 324 | } |
337 | | hideRemaining("phase[", i, "]"); |
338 | | hideRemaining("phase[", i, "]_bar"); |
| 325 | hideRemaining("phase_rows", i); |
| 326 | hideRemaining("phase_ident", i); |
339 | 327 | |
340 | 328 | let t = 0; |
341 | 329 | let ele = Engine.GetGUIObjectByName("trainer["+t+"]"); |
-
diff --git a/binaries/data/mods/public/gui/structree/load.js b/binaries/data/mods/public/gui/structree/load.js
index 1dfd332..6b52fdf 100644
a
|
b
|
|
5 | 5 | */ |
6 | 6 | function getGatherRates(templateName) |
7 | 7 | { |
8 | | // TODO: It would be nice to use the gather rates present in the templates |
9 | | // instead of hard-coding the possible rates here. |
10 | | |
11 | | // We ignore ruins here, as those are not that common and would skew the results |
12 | | var types = { |
13 | | "food": ["food", "food.fish", "food.fruit", "food.grain", "food.meat", "food.milk"], |
14 | | "wood": ["wood", "wood.tree"], |
15 | | "stone": ["stone", "stone.rock"], |
16 | | "metal": ["metal", "metal.ore"] |
17 | | }; |
18 | | var rates = {}; |
| 8 | let rates = {}; |
19 | 9 | |
20 | | for (let type in types) |
| 10 | for (let resource of g_ResourceData.GetData()) |
21 | 11 | { |
| 12 | let types = [resource.code]; |
| 13 | for (let subtype in resource.subtypes) |
| 14 | // We ignore ruins as those are not that common and skew the results |
| 15 | if (subtype !== "ruins") |
| 16 | types.push(resource.code + "." + subtype); |
| 17 | |
22 | 18 | let count, rate; |
23 | | [rate, count] = types[type].reduce(function(sum, t) { |
| 19 | [rate, count] = types.reduce((sum, t) => { |
24 | 20 | let r = +fetchValue(templateName, "ResourceGatherer/Rates/"+t); |
25 | 21 | return [sum[0] + (r > 0 ? r : 0), sum[1] + (r > 0 ? 1 : 0)]; |
26 | 22 | }, [0, 0]); |
27 | 23 | |
28 | 24 | if (rate > 0) |
29 | | rates[type] = Math.round(rate / count * 100) / 100; |
| 25 | rates[resource.code] = +(rate / count).toFixed(1); |
30 | 26 | } |
31 | 27 | |
32 | 28 | if (!Object.keys(rates).length) |
-
diff --git a/binaries/data/mods/public/gui/structree/rows.xml b/binaries/data/mods/public/gui/structree/rows.xml
index 4e53293..fdffe3c 100644
a
|
b
|
|
9 | 9 | <object type="image" style="StructIcon" name="phase[k]_struct[s]_icon" |
10 | 10 | sprite="stretched:pregame/shell/logo/wfg_logo_white.png" |
11 | 11 | /> |
12 | | <repeat count="4" var="r"> |
13 | | <object name="phase[k]_struct[s]_row[r]"> |
14 | | <repeat count="24" var="p"> |
15 | | <object type="image" style="ProdBox" name="phase[k]_struct[s]_row[r]_prod[p]"/> |
16 | | </repeat> |
17 | | </object> |
18 | | </repeat> |
| 12 | <object name="phase[k]_struct[s]_rows"> |
| 13 | <repeat count="4" var="r"> |
| 14 | <object name="phase[k]_struct[s]_row[r]"> |
| 15 | <repeat count="24" var="p"> |
| 16 | <object type="image" style="ProdBox" name="phase[k]_struct[s]_row[r]_prod[p]"/> |
| 17 | </repeat> |
| 18 | </object> |
| 19 | </repeat> |
| 20 | </object> |
19 | 21 | </object> |
20 | 22 | </repeat> |
21 | 23 | </object> |
-
diff --git a/binaries/data/mods/public/gui/structree/structree.js b/binaries/data/mods/public/gui/structree/structree.js
index 65bfd9e..fcb6a9c 100644
a
|
b
|
var g_Lists = {};
|
9 | 9 | var g_CivData = {}; |
10 | 10 | var g_SelectedCiv = ""; |
11 | 11 | var g_CallbackSet = false; |
| 12 | var g_ResourceData = new Resources(); |
12 | 13 | |
13 | 14 | /** |
14 | 15 | * Initialize the dropdown containing all the available civs |
-
diff --git a/binaries/data/mods/public/gui/structree/structree.xml b/binaries/data/mods/public/gui/structree/structree.xml
index b53bd93..f33d12e 100644
a
|
b
|
|
64 | 64 | |
65 | 65 | <!-- Structure Tree display --> |
66 | 66 | <object size="0 54+64 100%-124 100%-54" name="display_tree"> |
67 | | <repeat count="4" var="n"> |
68 | | <object name="phase[n]_phase" type="image"/> |
69 | | <object name="phase[n]_bar"> |
70 | | <repeat count="4" var="k"> |
71 | | <object name="phase[n]_bar[k]" type="image" sprite="ProdBar"> |
72 | | <object name="phase[n]_bar[k]_icon" type="image" size="2 2 20 20"/> |
| 67 | <object name="phase_ident"> |
| 68 | <repeat count="4" var="n"> |
| 69 | <object> |
| 70 | <object name="phase[n]_phase" type="image"/> |
| 71 | <object name="phase[n]_bars"> |
| 72 | <repeat count="4" var="k"> |
| 73 | <object name="phase[n]_bar[k]" type="image" sprite="ProdBar"> |
| 74 | <object name="phase[n]_bar[k]_icon" type="image" size="2 2 20 20"/> |
| 75 | </object> |
| 76 | </repeat> |
73 | 77 | </object> |
74 | | </repeat> |
75 | | </object> |
76 | | </repeat> |
| 78 | </object> |
| 79 | </repeat> |
| 80 | </object> |
77 | 81 | |
78 | 82 | <object type="image" style="TreeDisplay" size="48+16+8 0 100%-12 100%"> |
79 | 83 | <include file="gui/structree/rows.xml"/> |
… |
… |
|
93 | 97 | <translatableAttribute id="caption">Trainer Units</translatableAttribute> |
94 | 98 | </object> |
95 | 99 | |
96 | | <object type="image" style="TreeDisplay" size="0 24 100% 100%"> |
| 100 | <object type="image" style="TreeDisplay" size="0 24 100% 100%" name="trainers"> |
97 | 101 | <repeat count="3" var="t"> |
98 | 102 | <object type="image" style="StructBox" name="trainer[t]"> |
99 | 103 | <object type="text" style="StructNameSpecific" name="trainer[t]_name"/> |
-
diff --git a/binaries/data/mods/public/gui/summary/counters.js b/binaries/data/mods/public/gui/summary/counters.js
index 562e3bb..36b1812 100644
a
|
b
|
function updateCountersPlayer(playerState, counters, idGUI)
|
63 | 63 | { |
64 | 64 | for (let w in counters) |
65 | 65 | { |
| 66 | if (!Engine.GetGUIObjectByName(idGUI + "[" + w + "]")) |
| 67 | break; |
66 | 68 | let fn = counters[w].fn; |
67 | 69 | Engine.GetGUIObjectByName(idGUI + "[" + w + "]").caption = fn && fn(playerState, w); |
68 | 70 | } |
… |
… |
function calculateUnits(playerState, position)
|
250 | 252 | |
251 | 253 | function calculateResources(playerState, position) |
252 | 254 | { |
253 | | let type = g_ResourcesTypes[position]; |
| 255 | let type = g_ResourceData.GetCodes()[position]; |
254 | 256 | |
255 | 257 | return formatIncome( |
256 | 258 | playerState.statistics.resourcesGathered[type], |
… |
… |
function calculateTotalResources(playerState)
|
262 | 264 | let totalGathered = 0; |
263 | 265 | let totalUsed = 0; |
264 | 266 | |
265 | | for (let type of g_ResourcesTypes) |
| 267 | for (let type of g_ResourceData.GetCodes()) |
266 | 268 | { |
267 | 269 | totalGathered += playerState.statistics.resourcesGathered[type]; |
268 | 270 | totalUsed += playerState.statistics.resourcesUsed[type] - playerState.statistics.resourcesSold[type]; |
… |
… |
function calculateResourcesTeam(counters)
|
330 | 332 | |
331 | 333 | function calculateResourceExchanged(playerState, position) |
332 | 334 | { |
333 | | let type = g_ResourcesTypes[position]; |
| 335 | let type = g_ResourceData.GetCodes()[position]; |
334 | 336 | |
335 | 337 | return formatIncome( |
336 | 338 | playerState.statistics.resourcesBought[type], |
-
diff --git a/binaries/data/mods/public/gui/summary/layout.js b/binaries/data/mods/public/gui/summary/layout.js
index 7feeb45..efb20e9 100644
a
|
b
|
var g_ScorePanelsData = {
|
92 | 92 | "resources": { |
93 | 93 | "headings": [ |
94 | 94 | { "caption": translate("Player name"), "yStart": 26, "width": 200 }, |
95 | | { "caption": translate("Food"), "yStart": 34, "width": 100 }, |
96 | | { "caption": translate("Wood"), "yStart": 34, "width": 100 }, |
97 | | { "caption": translate("Stone"), "yStart": 34, "width": 100 }, |
98 | | { "caption": translate("Metal"), "yStart": 34, "width": 100 }, |
99 | 95 | { "caption": translate("Total"), "yStart": 34, "width": 110 }, |
100 | 96 | { |
101 | 97 | "caption": sprintf(translate("Tributes \n(%(sent)s / %(received)s)"), |
… |
… |
var g_ScorePanelsData = {
|
121 | 117 | }, // width = 510 |
122 | 118 | ], |
123 | 119 | "counters": [ |
124 | | { "width": 100, "fn": calculateResources, "verticalOffset": 12 }, |
125 | | { "width": 100, "fn": calculateResources, "verticalOffset": 12 }, |
126 | | { "width": 100, "fn": calculateResources, "verticalOffset": 12 }, |
127 | | { "width": 100, "fn": calculateResources, "verticalOffset": 12 }, |
128 | 120 | { "width": 110, "fn": calculateTotalResources, "verticalOffset": 12 }, |
129 | 121 | { "width": 121, "fn": calculateTributeSent, "verticalOffset": 12 }, |
130 | 122 | { "width": 100, "fn": calculateTreasureCollected, "verticalOffset": 12 }, |
… |
… |
var g_ScorePanelsData = {
|
135 | 127 | "market": { |
136 | 128 | "headings": [ |
137 | 129 | { "caption": translate("Player name"), "yStart": 26, "width": 200 }, |
138 | | { "caption": translate("Food exchanged"), "yStart": 16, "width": 100 }, |
139 | | { "caption": translate("Wood exchanged"), "yStart": 16, "width": 100 }, |
140 | | { "caption": translate("Stone exchanged"), "yStart": 16, "width": 100 }, |
141 | | { "caption": translate("Metal exchanged"), "yStart": 16, "width": 100 }, |
142 | 130 | { "caption": translate("Barter efficiency"), "yStart": 16, "width": 100 }, |
143 | 131 | { "caption": translate("Trade income"), "yStart": 16, "width": 100 } |
144 | 132 | ], |
145 | 133 | "titleHeadings": [], |
146 | 134 | "counters": [ |
147 | | { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 }, |
148 | | { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 }, |
149 | | { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 }, |
150 | | { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 }, |
151 | 135 | { "width": 100, "fn": calculateBarterEfficiency, "verticalOffset": 12 }, |
152 | 136 | { "width": 100, "fn": calculateTradeIncome, "verticalOffset": 12 } |
153 | 137 | ], |
… |
… |
function updateGeneralPanelHeadings(headings)
|
206 | 190 | headerGUIName = "Heading[" + (h - 1) + "]"; |
207 | 191 | |
208 | 192 | let headerGUI = Engine.GetGUIObjectByName(headerGUIName); |
| 193 | if (!headerGUI) |
| 194 | break; |
209 | 195 | headerGUI.caption = headings[h].caption; |
210 | 196 | headerGUI.size = left + " " + headings[h].yStart + " " + (left + headings[h].width) + " 100%"; |
211 | 197 | headerGUI.hidden = false; |
… |
… |
function updateGeneralPanelCounter(counters)
|
249 | 235 | for (let w in counters) |
250 | 236 | { |
251 | 237 | counterObject = Engine.GetGUIObjectByName("valueData[" + p + "][" + w + "]"); |
| 238 | if (!counterObject) |
| 239 | break; |
252 | 240 | counterObject.size = left + " " + counters[w].verticalOffset + " " + (left + counters[w].width) + " 100%"; |
253 | 241 | counterObject.hidden = false; |
254 | 242 | left += counters[w].width; |
… |
… |
function updateGeneralPanelCounter(counters)
|
264 | 252 | for (let w in counters) |
265 | 253 | { |
266 | 254 | counterObject = Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + p + "][" + w + "]"); |
| 255 | if (!counterObject) |
| 256 | break; |
267 | 257 | counterObject.size = left + " " + counters[w].verticalOffset + " " + (left + counters[w].width) + " 100%"; |
268 | 258 | counterObject.hidden = false; |
269 | 259 | |
-
diff --git a/binaries/data/mods/public/gui/summary/summary.js b/binaries/data/mods/public/gui/summary/summary.js
index 51e5577..7849b1f 100644
a
|
b
|
|
1 | | const g_MaxHeadingTitle= 8; |
| 1 | const g_MaxHeadingTitle= 12; |
2 | 2 | |
3 | 3 | // const for filtering long collective headings |
4 | 4 | const g_LongHeadingWidth = 250; |
… |
… |
const g_CapturedColor = '[color="255 255 157"]';
|
17 | 17 | |
18 | 18 | const g_BuildingsTypes = [ "total", "House", "Economic", "Outpost", "Military", "Fortress", "CivCentre", "Wonder" ]; |
19 | 19 | const g_UnitsTypes = [ "total", "Infantry", "Worker", "Cavalry", "Champion", "Hero", "Ship", "Trader" ]; |
20 | | const g_ResourcesTypes = [ "food", "wood", "stone", "metal" ]; |
21 | 20 | |
22 | 21 | // Colors used for gathered and traded resources |
23 | 22 | const g_IncomeColor = '[color="201 255 200"]'; |
… |
… |
var g_PlayerCount = 0;
|
34 | 33 | // Count players without team (or all if teams are not displayed) |
35 | 34 | var g_WithoutTeam = 0; |
36 | 35 | var g_GameData; |
| 36 | var g_ResourceData = new Resources(); |
37 | 37 | |
38 | 38 | function selectPanel(panel) |
39 | 39 | { |
… |
… |
function init(data)
|
242 | 242 | else |
243 | 243 | g_Teams = false; |
244 | 244 | |
| 245 | // Resource names and counters |
| 246 | let resHeads = []; |
| 247 | let tradeHeads = []; |
| 248 | let resPanel = g_ScorePanelsData.resources; |
| 249 | let tradePanel = g_ScorePanelsData.market; |
| 250 | let resNames = g_ResourceData.GetNames(); |
| 251 | let resCodes = g_ResourceData.GetCodes(); |
| 252 | for (let code of resCodes) |
| 253 | { |
| 254 | resHeads.push({ |
| 255 | "caption": translateWithContext("firstWord", resNames[code]), |
| 256 | "yStart": 34, |
| 257 | "width": 100 |
| 258 | }); |
| 259 | |
| 260 | resPanel.counters.unshift({ |
| 261 | "width": 100, |
| 262 | "fn": calculateResources, |
| 263 | "verticalOffset": 12 |
| 264 | }); |
| 265 | |
| 266 | tradeHeads.push({ |
| 267 | "caption": sprintf( |
| 268 | translate("%(resource)s exchanged"), { |
| 269 | "resource": translateWithContext("withinSentence", resNames[code]) |
| 270 | }), |
| 271 | "yStart": 16, |
| 272 | "width": 100 |
| 273 | }); |
| 274 | |
| 275 | tradePanel.counters.unshift({ |
| 276 | "width": 100, |
| 277 | "fn": calculateResourceExchanged, |
| 278 | "verticalOffset": 12 |
| 279 | }); |
| 280 | } |
| 281 | resPanel.headings.splice.apply(resPanel.headings, [1, 0].concat(resHeads)); |
| 282 | resPanel.titleHeadings[0].width = 100 * resCodes.length + 110; |
| 283 | tradePanel.headings.splice.apply(tradePanel.headings, [1, 0].concat(tradeHeads)); |
| 284 | |
245 | 285 | // Erase teams data if teams are not displayed |
246 | 286 | if (!g_Teams) |
247 | 287 | { |
-
diff --git a/binaries/data/mods/public/gui/summary/summary.xml b/binaries/data/mods/public/gui/summary/summary.xml
index 7d27a47..47bdb4d 100644
a
|
b
|
|
103 | 103 | <object name="playerNameHeading" type="text" style="ModernLeftTabLabelText"> |
104 | 104 | <translatableAttribute id="caption">Player name</translatableAttribute> |
105 | 105 | </object> |
106 | | <repeat var="x" count="8"> |
| 106 | <repeat var="x" count="12"> |
107 | 107 | <object name="titleHeading[x]" type="text" style="ModernTabLabelText"> |
108 | 108 | </object> |
109 | 109 | </repeat> |
110 | | <repeat var="x" count="8"> |
| 110 | <repeat var="x" count="12"> |
111 | 111 | <object name="Heading[x]" type="text" style="ModernTabLabelText"> |
112 | 112 | </object> |
113 | 113 | </repeat> |
… |
… |
|
124 | 124 | </object> |
125 | 125 | <object name="playerNamet[i][n]" type="text" size="40 2 208 100%" style="ModernLeftLabelText"/> |
126 | 126 | <object name="civIcont[i][n]" type="image" size="208 5 240 37" /> |
127 | | <repeat var="x" count="8"> |
| 127 | <repeat var="x" count="12"> |
128 | 128 | <object name="valueDataTeam[i][n][x]" type="text" style="ModernTabLabelText"> |
129 | 129 | </object> |
130 | 130 | </repeat> |
… |
… |
|
132 | 132 | </repeat> |
133 | 133 | </object> |
134 | 134 | <object name="teamHeadingt[i]" type="text" style="ModernLeftTabLabelText"/> |
135 | | <repeat var="x" count="8"> |
| 135 | <repeat var="x" count="12"> |
136 | 136 | <object name="valueDataTeam[i][x]" type="text" style="ModernTabLabelText"> |
137 | 137 | </object> |
138 | 138 | </repeat> |
… |
… |
|
147 | 147 | </object> |
148 | 148 | <object name="playerName[n]" type="text" size="40 2 208 100%" style="ModernLeftLabelText"/> |
149 | 149 | <object name="civIcon[n]" type="image" size="208 5 240 37"/> |
150 | | <repeat var="x" count="8"> |
| 150 | <repeat var="x" count="12"> |
151 | 151 | <object name="valueData[n][x]" type="text" style="ModernTabLabelText"> |
152 | 152 | </object> |
153 | 153 | </repeat> |
-
diff --git a/binaries/data/mods/public/l10n/messages.json b/binaries/data/mods/public/l10n/messages.json
index 4c98928..431065c 100644
a
|
b
|
|
289 | 289 | } |
290 | 290 | } |
291 | 291 | }, |
292 | | { |
| 292 | { |
293 | 293 | "extractor": "json", |
294 | 294 | "filemasks": [ |
295 | 295 | "gui/credits/texts/**.json" |
… |
… |
|
559 | 559 | "description" |
560 | 560 | ] |
561 | 561 | } |
| 562 | }, |
| 563 | { |
| 564 | "extractor": "json", |
| 565 | "filemasks": [ |
| 566 | "simulation/data/resources/**.json" |
| 567 | ], |
| 568 | "options": { |
| 569 | "keywords": [ |
| 570 | "name", |
| 571 | "subtypes" |
| 572 | ], |
| 573 | "context": "firstWord" |
| 574 | } |
| 575 | }, |
| 576 | { |
| 577 | "extractor": "json", |
| 578 | "filemasks": [ |
| 579 | "simulation/data/resources/**.json" |
| 580 | ], |
| 581 | "options": { |
| 582 | "keywords": [ |
| 583 | "name", |
| 584 | "subtypes" |
| 585 | ], |
| 586 | "context": "withinSentence" |
| 587 | } |
562 | 588 | } |
563 | 589 | ] |
564 | 590 | }, |
-
diff --git a/binaries/data/mods/public/simulation/ai/common-api/resources.js b/binaries/data/mods/public/simulation/ai/common-api/resources.js
index 8130676..b6fe84b 100644
a
|
b
|
m.Resources = function(amounts = {}, population = 0)
|
9 | 9 | this.population = population > 0 ? population : 0; |
10 | 10 | }; |
11 | 11 | |
12 | | m.Resources.prototype.types = []; // This array will be filled in SharedScript.init |
| 12 | // This array will be filled in SharedScript.init |
| 13 | m.Resources.prototype.types = []; |
13 | 14 | |
14 | 15 | m.Resources.prototype.reset = function() |
15 | 16 | { |
-
diff --git a/binaries/data/mods/public/simulation/ai/common-api/shared.js b/binaries/data/mods/public/simulation/ai/common-api/shared.js
index 68ab45d..0e7b6c5 100644
a
|
b
|
m.SharedScript.prototype.init = function(state, deserialization)
|
180 | 180 | this.accessibility.init(state, this.terrainAnalyzer); |
181 | 181 | |
182 | 182 | // Setup resources |
183 | | this.resourceTypes = { "food": 0, "wood": 1, "stone": 2, "metal": 2 }; |
184 | | this.resourceList = []; |
185 | | for (let res in this.resourceTypes) |
186 | | this.resourceList.push(res); |
187 | | m.Resources.prototype.types = this.resourceList; |
| 183 | this.resourceInfo = state.resources; |
| 184 | m.Resources.prototype.types = state.resources.codes; |
188 | 185 | // Resource types: 0 = not used for resource maps |
189 | | // 1 = abondant resource with small amount each |
| 186 | // 1 = abundant resource with small amount each |
190 | 187 | // 2 = spare resource, but huge amount each |
191 | 188 | // The following maps are defined in TerrainAnalysis.js and are used for some building placement (cc, dropsites) |
192 | 189 | // They are updated by checking for create and destroy events for all resources |
… |
… |
m.SharedScript.prototype.init = function(state, deserialization)
|
197 | 194 | this.ccResourceMaps = {}; // Contains maps showing the density of resources, optimized for CC placement. |
198 | 195 | this.createResourceMaps(); |
199 | 196 | |
200 | | /** Keep in sync with gui/common/l10n.js */ |
201 | | this.resourceNames = { |
202 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
203 | | "food": markForTranslationWithContext("withinSentence", "Food"), |
204 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
205 | | "wood": markForTranslationWithContext("withinSentence", "Wood"), |
206 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
207 | | "metal": markForTranslationWithContext("withinSentence", "Metal"), |
208 | | // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). |
209 | | "stone": markForTranslationWithContext("withinSentence", "Stone"), |
210 | | }; |
211 | | |
212 | 197 | this.gameState = {}; |
213 | 198 | for (let i in this._players) |
214 | 199 | { |
-
diff --git a/binaries/data/mods/public/simulation/ai/common-api/terrain-analysis.js b/binaries/data/mods/public/simulation/ai/common-api/terrain-analysis.js
index 04633dc..0639911 100644
a
|
b
|
m.Accessibility.prototype.floodFill = function(startIndex, value, onWater)
|
383 | 383 | /** creates a map of resource density */ |
384 | 384 | m.SharedScript.prototype.createResourceMaps = function() |
385 | 385 | { |
386 | | for (let resource of this.resourceList) |
| 386 | for (let resource of this.resourceInfo.codes) |
387 | 387 | { |
388 | | if (this.resourceTypes[resource] !== 1 && this.resourceTypes[resource] !== 2) |
| 388 | if (this.resourceInfo.aiInfluenceGroups[resource] === 0) |
389 | 389 | continue; |
390 | 390 | // if there is no resourceMap create one with an influence for everything with that resource |
391 | 391 | if (this.resourceMaps[resource]) |
… |
… |
m.SharedScript.prototype.createResourceMaps = function()
|
405 | 405 | let cellSize = this.resourceMaps[resource].cellSize; |
406 | 406 | let x = Math.floor(ent.position()[0] / cellSize); |
407 | 407 | let z = Math.floor(ent.position()[1] / cellSize); |
408 | | let type = this.resourceTypes[resource]; |
409 | | let strength = Math.floor(ent.resourceSupplyMax()/this.normalizationFactor[type]); |
410 | | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2, "constant"); |
411 | | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2); |
412 | | this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[type]/cellSize, strength, "constant"); |
| 408 | let grp = this.resourceInfo.aiInfluenceGroups[resource]; |
| 409 | let strength = Math.floor(ent.resourceSupplyMax() / this.normalizationFactor[grp]); |
| 410 | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp] / cellSize, strength/2, "constant"); |
| 411 | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp] / cellSize, strength/2); |
| 412 | this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[grp] / cellSize, strength, "constant"); |
413 | 413 | } |
414 | 414 | }; |
415 | 415 | |
… |
… |
m.SharedScript.prototype.createResourceMaps = function()
|
420 | 420 | */ |
421 | 421 | m.SharedScript.prototype.updateResourceMaps = function(events) |
422 | 422 | { |
423 | | for (let resource of this.resourceList) |
| 423 | for (let resource of this.resourceInfo.codes) |
424 | 424 | { |
425 | | if (this.resourceTypes[resource] !== 1 && this.resourceTypes[resource] !== 2) |
| 425 | if (this.resourceInfo.aiInfluenceGroups[resource] === 0) |
426 | 426 | continue; |
427 | 427 | // if there is no resourceMap create one with an influence for everything with that resource |
428 | 428 | if (this.resourceMaps[resource]) |
… |
… |
m.SharedScript.prototype.updateResourceMaps = function(events)
|
447 | 447 | let cellSize = this.resourceMaps[resource].cellSize; |
448 | 448 | let x = Math.floor(ent.position()[0] / cellSize); |
449 | 449 | let z = Math.floor(ent.position()[1] / cellSize); |
450 | | let type = this.resourceTypes[resource]; |
451 | | let strength = -Math.floor(ent.resourceSupplyMax()/this.normalizationFactor[type]); |
452 | | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2, "constant"); |
453 | | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2); |
454 | | this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[type]/cellSize, strength, "constant"); |
| 450 | let grp = this.resourceInfo.aiInfluenceGroups[resource]; |
| 451 | let strength = -Math.floor(ent.resourceSupplyMax() / this.normalizationFactor[grp]); |
| 452 | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp] / cellSize, strength/2, "constant"); |
| 453 | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp] / cellSize, strength/2); |
| 454 | this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[grp] / cellSize, strength, "constant"); |
455 | 455 | } |
456 | 456 | for (let e of events.Create) |
457 | 457 | { |
… |
… |
m.SharedScript.prototype.updateResourceMaps = function(events)
|
466 | 466 | let cellSize = this.resourceMaps[resource].cellSize; |
467 | 467 | let x = Math.floor(ent.position()[0] / cellSize); |
468 | 468 | let z = Math.floor(ent.position()[1] / cellSize); |
469 | | let type = this.resourceTypes[resource]; |
470 | | let strength = Math.floor(ent.resourceSupplyMax()/this.normalizationFactor[type]); |
471 | | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2, "constant"); |
472 | | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2); |
473 | | this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[type]/cellSize, strength, "constant"); |
| 469 | let grp = this.resourceInfo.aiInfluenceGroups[resource]; |
| 470 | let strength = Math.floor(ent.resourceSupplyMax() / this.normalizationFactor[grp]); |
| 471 | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp] / cellSize, strength/2, "constant"); |
| 472 | this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp] / cellSize, strength/2); |
| 473 | this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[grp] / cellSize, strength, "constant"); |
474 | 474 | } |
475 | 475 | }; |
476 | 476 | |
-
diff --git a/binaries/data/mods/public/simulation/ai/petra/baseManager.js b/binaries/data/mods/public/simulation/ai/petra/baseManager.js
index f1b338f..1c823c7 100644
a
|
b
|
m.BaseManager.prototype.init = function(gameState, state)
|
55 | 55 | this.dropsites = {}; |
56 | 56 | this.dropsiteSupplies = {}; |
57 | 57 | this.gatherers = {}; |
58 | | for (let res of gameState.sharedScript.resourceList) |
| 58 | for (let res of gameState.sharedScript.resourceInfo.codes) |
59 | 59 | { |
60 | 60 | this.dropsiteSupplies[res] = { "nearby": [], "medium": [], "faraway": [] }; |
61 | 61 | this.gatherers[res] = { "nextCheck": 0, "used": 0, "lost": 0 }; |
… |
… |
m.BaseManager.prototype.getResourceLevel = function (gameState, type, nearbyOnly
|
434 | 434 | /** check our resource levels and react accordingly */ |
435 | 435 | m.BaseManager.prototype.checkResourceLevels = function (gameState, queues) |
436 | 436 | { |
437 | | for (let type of gameState.sharedScript.resourceList) |
| 437 | for (let type of gameState.sharedScript.resourceInfo.codes) |
438 | 438 | { |
439 | 439 | if (type === "food") |
440 | 440 | { |
-
diff --git a/binaries/data/mods/public/simulation/ai/petra/chatHelper.js b/binaries/data/mods/public/simulation/ai/petra/chatHelper.js
index 60999e8..1ef89be 100644
a
|
b
|
m.chatLaunchAttack = function(gameState, player, type)
|
17 | 17 | "message": message, |
18 | 18 | "translateMessage": true, |
19 | 19 | "translateParameters": ["_player_"], |
20 | | "parameters": {"_player_": player} |
| 20 | "parameters": { "_player_": player } |
21 | 21 | }); |
22 | 22 | }; |
23 | 23 | |
… |
… |
m.chatAnswerRequestAttack = function(gameState, player, answer, other)
|
45 | 45 | "message": message, |
46 | 46 | "translateMessage": true, |
47 | 47 | "translateParameters": ["_player_"], |
48 | | "parameters": {"_player_": player} |
| 48 | "parameters": { "_player_": player } |
49 | 49 | }; |
50 | 50 | if (other !== undefined) |
51 | 51 | { |
… |
… |
m.chatSentTribute = function(gameState, player)
|
71 | 71 | "message": message, |
72 | 72 | "translateMessage": true, |
73 | 73 | "translateParameters": ["_player_"], |
74 | | "parameters": {"_player_": player} |
| 74 | "parameters": { "_player_": player } |
75 | 75 | }); |
76 | 76 | }; |
77 | 77 | |
… |
… |
m.chatRequestTribute = function(gameState, resource)
|
90 | 90 | "type": "aichat", |
91 | 91 | "message": message, |
92 | 92 | "translateMessage": true, |
93 | | "translateParameters": {"resource": "withinSentence"}, |
94 | | "parameters": {"resource": gameState.sharedScript.resourceNames[resource]} |
| 93 | "translateParameters": { "resource": "withinSentence" }, |
| 94 | "parameters": { "resource": gameState.sharedScript.resourceInfo.names[resource] } |
95 | 95 | }); |
96 | 96 | }; |
97 | 97 | |
… |
… |
m.chatNewTradeRoute = function(gameState, player)
|
109 | 109 | "message": message, |
110 | 110 | "translateMessage": true, |
111 | 111 | "translateParameters": ["_player_"], |
112 | | "parameters": {"_player_": player} |
| 112 | "parameters": { "_player_": player } |
113 | 113 | }); |
114 | 114 | }; |
115 | 115 | |
-
diff --git a/binaries/data/mods/public/simulation/ai/petra/config.js b/binaries/data/mods/public/simulation/ai/petra/config.js
index d5dc6b3..b0f5e7f 100644
a
|
b
|
m.Config = function(difficulty)
|
103 | 103 | "defensive": 0.5 |
104 | 104 | }; |
105 | 105 | |
106 | | this.resources = ["food", "wood", "stone", "metal"]; |
| 106 | // See m.QueueManager.prototype.wantedGatherRates() |
| 107 | this.queues = |
| 108 | { |
| 109 | "firstTurn": { |
| 110 | "food": 10, |
| 111 | "wood": 10, |
| 112 | "default": 0 |
| 113 | }, |
| 114 | "short": { |
| 115 | "food": 200, |
| 116 | "wood": 200, |
| 117 | "default": 100 |
| 118 | }, |
| 119 | "medium": { |
| 120 | "default": 0 |
| 121 | }, |
| 122 | "long": { |
| 123 | "default": 0 |
| 124 | } |
| 125 | }; |
107 | 126 | }; |
108 | 127 | |
109 | 128 | m.Config.prototype.setConfig = function(gameState) |
-
diff --git a/binaries/data/mods/public/simulation/ai/petra/headquarters.js b/binaries/data/mods/public/simulation/ai/petra/headquarters.js
index 3e67da9..871035f 100644
a
|
b
|
m.HQ.prototype.init = function(gameState, queues)
|
69 | 69 | this.navalMap = false; |
70 | 70 | this.navalRegions = {}; |
71 | 71 | |
72 | | for (let res of gameState.sharedScript.resourceList) |
| 72 | for (let res of gameState.sharedScript.resourceInfo.codes) |
73 | 73 | { |
74 | 74 | this.wantedRates[res] = 0; |
75 | 75 | this.currentRates[res] = 0; |
… |
… |
m.HQ.prototype.bulkPickWorkers = function(gameState, baseRef, number)
|
653 | 653 | m.HQ.prototype.getTotalResourceLevel = function(gameState) |
654 | 654 | { |
655 | 655 | let total = {}; |
656 | | for (let res of gameState.sharedScript.resourceList) |
| 656 | for (let res of gameState.sharedScript.resourceInfo.codes) |
657 | 657 | total[res] = 0; |
658 | 658 | for (let base of this.baseManagers) |
659 | 659 | for (let res in total) |
-
diff --git a/binaries/data/mods/public/simulation/ai/petra/queueManager.js b/binaries/data/mods/public/simulation/ai/petra/queueManager.js
index 4691d11..671131d 100644
a
|
b
|
m.QueueManager.prototype.wantedGatherRates = function(gameState)
|
84 | 84 | if (gameState.ai.playedTurn === 0) |
85 | 85 | { |
86 | 86 | let ret = {}; |
87 | | for (let res of gameState.sharedScript.resourceList) |
88 | | ret[res] = (res === "food" || res === "wood" ) ? 10 : 0; |
| 87 | for (let res of gameState.sharedScript.resourceInfo.codes) |
| 88 | ret[res] = this.Config.queues.firstTurn[res] || this.Config.queues.firstTurn.default; |
89 | 89 | return ret; |
90 | 90 | } |
91 | 91 | |
… |
… |
m.QueueManager.prototype.wantedGatherRates = function(gameState)
|
97 | 97 | let totalShort = {}; |
98 | 98 | let totalMedium = {}; |
99 | 99 | let totalLong = {}; |
100 | | for (let res of gameState.sharedScript.resourceList) |
| 100 | for (let res of gameState.sharedScript.resourceInfo.codes) |
101 | 101 | { |
102 | | totalShort[res] = (res === "food" || res === "wood" ) ? 200 : 100; |
103 | | totalMedium[res] = 0; |
104 | | totalLong[res] = 0; |
| 102 | totalShort[res] = this.Config.queues.short[res] || this.Config.queues.short.default; |
| 103 | totalMedium[res] = this.Config.queues.medium[res] || this.Config.queues.medium.default; |
| 104 | totalLong[res] = this.Config.queues.long[res] || this.Config.queues.long.default; |
105 | 105 | } |
106 | 106 | let total; |
107 | 107 | //queueArrays because it's faster. |
… |
… |
m.QueueManager.prototype.wantedGatherRates = function(gameState)
|
133 | 133 | // global rates |
134 | 134 | let rates = {}; |
135 | 135 | let diff; |
136 | | for (let res of gameState.sharedScript.resourceList) |
| 136 | for (let res of gameState.sharedScript.resourceInfo.codes) |
137 | 137 | { |
138 | 138 | if (current[res] > 0) |
139 | 139 | { |
-
diff --git a/binaries/data/mods/public/simulation/ai/petra/researchManager.js b/binaries/data/mods/public/simulation/ai/petra/researchManager.js
index f169b39..8e409a1 100644
a
|
b
|
m.ResearchManager.prototype.researchWantedTechs = function(gameState, techs)
|
100 | 100 | let cost = template.cost; |
101 | 101 | let costMax = 0; |
102 | 102 | for (let res in cost) |
103 | | costMax = Math.max(costMax, Math.max(cost[res]-available[res], 0)); |
| 103 | if (gameState.sharedScript.resourceInfo.codes.indexOf(res)) |
| 104 | costMax = Math.max(costMax, Math.max(cost[res]-available[res], 0)); |
104 | 105 | if (10*numWorkers < costMax) |
105 | 106 | continue; |
106 | 107 | } |
-
diff --git a/binaries/data/mods/public/simulation/components/Barter.js b/binaries/data/mods/public/simulation/components/Barter.js
index 24c39a4..106d1e3 100644
a
|
b
|
|
1 | | // True price of 100 units of resource (for case if some resource is more worth). |
| 1 | // The "true price" is a base price of 100 units of resource (for the case of some resources being of more worth than others). |
2 | 2 | // With current bartering system only relative values makes sense |
3 | 3 | // so if for example stone is two times more expensive than wood, |
4 | 4 | // there will 2:1 exchange rate. |
5 | | const TRUE_PRICES = { "food": 100, "wood": 100, "stone": 100, "metal": 100 }; |
6 | | |
| 5 | // |
7 | 6 | // Constant part of price difference between true price and buy/sell price. |
8 | 7 | // In percents. |
9 | 8 | // Buy price equal to true price plus constant difference. |
… |
… |
const DIFFERENCE_RESTORE = 0.5;
|
21 | 20 | // Interval of timer which slowly restore prices after deals |
22 | 21 | const RESTORE_TIMER_INTERVAL = 5000; |
23 | 22 | |
24 | | // Array of resource names |
25 | | const RESOURCES = ["food", "wood", "stone", "metal"]; |
26 | | |
27 | 23 | function Barter() {} |
28 | 24 | |
29 | 25 | Barter.prototype.Schema = |
… |
… |
Barter.prototype.Schema =
|
32 | 28 | Barter.prototype.Init = function() |
33 | 29 | { |
34 | 30 | this.priceDifferences = {}; |
35 | | for (var resource of RESOURCES) |
| 31 | for (let resource of Resources.GetCodes()) |
36 | 32 | this.priceDifferences[resource] = 0; |
37 | 33 | this.restoreTimer = undefined; |
38 | 34 | }; |
… |
… |
Barter.prototype.Init = function()
|
40 | 36 | Barter.prototype.GetPrices = function() |
41 | 37 | { |
42 | 38 | var prices = { "buy": {}, "sell": {} }; |
43 | | for (var resource of RESOURCES) |
| 39 | for (let resource of Resources.GetCodes()) |
44 | 40 | { |
45 | | prices["buy"][resource] = TRUE_PRICES[resource] * (100 + CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100; |
46 | | prices["sell"][resource] = TRUE_PRICES[resource] * (100 - CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100; |
| 41 | let truePrice = Resources.GetResource(resource).truePrice; |
| 42 | prices.buy[resource] = truePrice * (100 + CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100; |
| 43 | prices.sell[resource] = truePrice * (100 - CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100; |
47 | 44 | } |
48 | 45 | return prices; |
49 | 46 | }; |
… |
… |
Barter.prototype.ExchangeResources = function(playerEntity, resourceToSell, reso
|
71 | 68 | warn("ExchangeResources: incorrect amount: " + uneval(amount)); |
72 | 69 | return; |
73 | 70 | } |
74 | | if (RESOURCES.indexOf(resourceToSell) == -1) |
| 71 | let availResources = Resources.GetCodes(); |
| 72 | if (availResources.indexOf(resourceToSell) == -1) |
75 | 73 | { |
76 | 74 | warn("ExchangeResources: incorrect resource to sell: " + uneval(resourceToSell)); |
77 | 75 | return; |
78 | 76 | } |
79 | | if (RESOURCES.indexOf(resourceToBuy) == -1) |
| 77 | if (availResources.indexOf(resourceToBuy) == -1) |
80 | 78 | { |
81 | 79 | warn("ExchangeResources: incorrect resource to buy: " + uneval(resourceToBuy)); |
82 | 80 | return; |
… |
… |
Barter.prototype.ExchangeResources = function(playerEntity, resourceToSell, reso
|
123 | 121 | Barter.prototype.ProgressTimeout = function(data) |
124 | 122 | { |
125 | 123 | var needRestore = false; |
126 | | for (var resource of RESOURCES) |
| 124 | for (let resource of Resources.GetCodes()) |
127 | 125 | { |
128 | 126 | // Calculate value to restore, it should be limited to [-DIFFERENCE_RESTORE; DIFFERENCE_RESTORE] interval |
129 | 127 | var differenceRestore = Math.min(DIFFERENCE_RESTORE, Math.max(-DIFFERENCE_RESTORE, this.priceDifferences[resource])); |
-
diff --git a/binaries/data/mods/public/simulation/components/Cost.js b/binaries/data/mods/public/simulation/components/Cost.js
index 6a70192..0673536 100644
a
|
b
|
Cost.prototype.Schema =
|
19 | 19 | "<element name='PopulationBonus' a:help='Population cap increase while this entity exists'>" + |
20 | 20 | "<data type='nonNegativeInteger'/>" + |
21 | 21 | "</element>" + |
22 | | "<element name='BuildTime' a:help='Time taken to construct/train this unit (in seconds)'>" + |
| 22 | "<element name='BuildTime' a:help='Time taken to construct/train this entity (in seconds)'>" + |
23 | 23 | "<ref name='nonNegativeDecimal'/>" + |
24 | 24 | "</element>" + |
25 | | "<element name='Resources' a:help='Resource costs to construct/train this unit'>" + |
26 | | "<interleave>" + |
27 | | "<element name='food'><data type='nonNegativeInteger'/></element>" + |
28 | | "<element name='wood'><data type='nonNegativeInteger'/></element>" + |
29 | | "<element name='stone'><data type='nonNegativeInteger'/></element>" + |
30 | | "<element name='metal'><data type='nonNegativeInteger'/></element>" + |
31 | | "</interleave>" + |
| 25 | "<element name='Resources' a:help='Resource costs to construct/train this entity'>" + |
| 26 | Resources.BuildSchema("nonNegativeDecimal") + |
32 | 27 | "</element>"; |
33 | 28 | |
34 | 29 | Cost.prototype.Init = function() |
… |
… |
Cost.prototype.GetResourceCosts = function(owner)
|
70 | 65 | let entityTemplate = cmpTemplateManager.GetTemplate(entityTemplateName); |
71 | 66 | |
72 | 67 | let costs = {}; |
73 | | for (let r in this.template.Resources) |
74 | | costs[r] = ApplyValueModificationsToTemplate("Cost/Resources/"+r, +this.template.Resources[r], owner, entityTemplate); |
| 68 | let resCodes = Resources.GetCodes(); |
| 69 | |
| 70 | for (let res in this.template.Resources) |
| 71 | { |
| 72 | if (resCodes.indexOf(res) < 0) |
| 73 | continue; |
| 74 | costs[res] = ApplyValueModificationsToTemplate("Cost/Resources/"+res, +this.template.Resources[res], owner, entityTemplate); |
| 75 | } |
| 76 | |
75 | 77 | return costs; |
76 | 78 | }; |
77 | 79 | |
-
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
index 533c891..a0923b1 100644
a
|
b
|
GuiInterface.prototype.GetSimulationState = function()
|
152 | 152 | let cmpBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter); |
153 | 153 | ret.barterPrices = cmpBarter.GetPrices(); |
154 | 154 | |
| 155 | // Add Resource Codes, untranslated names and AI Analysis |
| 156 | ret.resources = { |
| 157 | "codes": Resources.GetCodes(), |
| 158 | "names": Resources.GetNames(), |
| 159 | "aiInfluenceGroups": {} |
| 160 | }; |
| 161 | for (let res of ret.resources.codes) |
| 162 | ret.resources.aiInfluenceGroups[res] = Resources.GetResource(res).aiAnalysisInfluenceGroup || 0; |
| 163 | |
155 | 164 | // Add basic statistics to each player |
156 | 165 | for (let i = 0; i < numPlayers; ++i) |
157 | 166 | { |
… |
… |
GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
|
1269 | 1278 | |
1270 | 1279 | let result = { |
1271 | 1280 | "pieces": [], |
1272 | | "cost": { "food": 0, "wood": 0, "stone": 0, "metal": 0, "population": 0, "populationBonus": 0, "time": 0 }, |
| 1281 | "cost": { "population": 0, "populationBonus": 0, "time": 0 }, |
1273 | 1282 | }; |
| 1283 | for (let res of Resources.GetCodes()) |
| 1284 | result.cost[res] = 0; |
1274 | 1285 | |
1275 | 1286 | let previewEntities = []; |
1276 | 1287 | if (end.pos) |
… |
… |
GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
|
1545 | 1556 | // copied over, so we need to fetch it from the template instead). |
1546 | 1557 | // TODO: we should really use a Cost object or at least some utility functions for this, this is mindless |
1547 | 1558 | // boilerplate that's probably duplicated in tons of places. |
1548 | | result.cost.food += tplData.cost.food; |
1549 | | result.cost.wood += tplData.cost.wood; |
1550 | | result.cost.stone += tplData.cost.stone; |
1551 | | result.cost.metal += tplData.cost.metal; |
1552 | | result.cost.population += tplData.cost.population; |
1553 | | result.cost.populationBonus += tplData.cost.populationBonus; |
1554 | | result.cost.time += tplData.cost.time; |
| 1559 | for (let res of Resources.GetCodes().concat("population", "populationBonus", "time")) |
| 1560 | result.cost[res] = tplData.cost[res]; |
1555 | 1561 | } |
1556 | 1562 | |
1557 | 1563 | let canAfford = true; |
… |
… |
GuiInterface.prototype.GetTradingGoods = function(player)
|
1932 | 1938 | return QueryPlayerIDInterface(player).GetTradingGoods(); |
1933 | 1939 | }; |
1934 | 1940 | |
| 1941 | GuiInterface.prototype.GetBarterPrices = function() |
| 1942 | { |
| 1943 | return Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter).GetPrices(); |
| 1944 | }; |
| 1945 | |
| 1946 | GuiInterface.prototype.PlayerCanBarter = function(player) |
| 1947 | { |
| 1948 | let playerEnt = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(player); |
| 1949 | return Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter).PlayerHasMarket(playerEnt); |
| 1950 | }; |
| 1951 | |
1935 | 1952 | GuiInterface.prototype.OnGlobalEntityRenamed = function(msg) |
1936 | 1953 | { |
1937 | 1954 | this.renamedEntities.push(msg); |
… |
… |
let exposedFunctions = {
|
1996 | 2013 | |
1997 | 2014 | "GetTraderNumber": 1, |
1998 | 2015 | "GetTradingGoods": 1, |
| 2016 | "GetBarterPrices": 1, |
| 2017 | "PlayerCanBarter": 1, |
1999 | 2018 | }; |
2000 | 2019 | |
2001 | 2020 | GuiInterface.prototype.ScriptCall = function(player, name, args) |
-
diff --git a/binaries/data/mods/public/simulation/components/Loot.js b/binaries/data/mods/public/simulation/components/Loot.js
index 3161340..8ce865e 100644
a
|
b
|
|
1 | 1 | function Loot() {} |
2 | 2 | |
3 | 3 | Loot.prototype.Schema = |
4 | | "<optional>" + |
5 | | "<element name='xp'><data type='nonNegativeInteger'/></element>" + |
6 | | "</optional>" + |
7 | | "<optional>" + |
8 | | "<element name='food'><data type='nonNegativeInteger'/></element>" + |
9 | | "</optional>" + |
10 | | "<optional>" + |
11 | | "<element name='wood'><data type='nonNegativeInteger'/></element>" + |
12 | | "</optional>" + |
13 | | "<optional>" + |
14 | | "<element name='stone'><data type='nonNegativeInteger'/></element>" + |
15 | | "</optional>" + |
16 | | "<optional>" + |
17 | | "<element name='metal'><data type='nonNegativeInteger'/></element>" + |
18 | | "</optional>"; |
| 4 | "<a:help>Specifies the loot credited when this entity is killed.</a:help>" + |
| 5 | "<a:example>" + |
| 6 | "<xp>35</xp>" + |
| 7 | "<metal>10</metal>" + |
| 8 | "</a:example>" + |
| 9 | Resources.BuildSchema("nonNegativeInteger", ["xp"]); |
19 | 10 | |
20 | 11 | Loot.prototype.Serialize = null; // we have no dynamic state to save |
21 | 12 | |
… |
… |
Loot.prototype.GetXp = function()
|
26 | 17 | |
27 | 18 | Loot.prototype.GetResources = function() |
28 | 19 | { |
29 | | return { |
30 | | "food": +(this.template.food || 0), |
31 | | "wood": +(this.template.wood || 0), |
32 | | "metal": +(this.template.metal || 0), |
33 | | "stone": +(this.template.stone || 0) |
34 | | }; |
| 20 | let ret = {}; |
| 21 | for (let res of Resources.GetCodes()) |
| 22 | ret[res] = +(this.template[res] || 0); |
| 23 | |
| 24 | return ret; |
35 | 25 | }; |
36 | 26 | |
37 | 27 | Engine.RegisterComponentType(IID_Loot, "Loot", Loot); |
-
diff --git a/binaries/data/mods/public/simulation/components/Player.js b/binaries/data/mods/public/simulation/components/Player.js
index 89986af..796a420 100644
a
|
b
|
Player.prototype.Init = function()
|
18 | 18 | this.popBonuses = 0; // sum of population bonuses of player's entities |
19 | 19 | this.maxPop = 300; // maximum population |
20 | 20 | this.trainingBlocked = false; // indicates whether any training queue is currently blocked |
21 | | this.resourceCount = { |
22 | | "food": 300, |
23 | | "wood": 300, |
24 | | "metal": 300, |
25 | | "stone": 300 |
26 | | }; |
27 | | // goods for next trade-route and its proba in % (the sum of probas must be 100) |
28 | | this.tradingGoods = [ |
29 | | { "goods": "wood", "proba": 30 }, |
30 | | { "goods": "stone", "proba": 35 }, |
31 | | { "goods": "metal", "proba": 35 }, |
32 | | ]; |
| 21 | this.resourceCount = {}; |
| 22 | this.tradingGoods = []; // goods for next trade-route and its proba in % (the sum of probas must be 100) |
33 | 23 | this.team = -1; // team number of the player, players on the same team will always have ally diplomatic status - also this is useful for team emblems, scoring, etc. |
34 | 24 | this.teamsLocked = false; |
35 | 25 | this.state = "active"; // game state - one of "active", "defeated", "won" |
… |
… |
Player.prototype.Init = function()
|
44 | 34 | this.cheatsEnabled = false; |
45 | 35 | this.cheatTimeMultiplier = 1; |
46 | 36 | this.heroes = []; |
47 | | this.resourceNames = { |
48 | | "food": markForTranslation("Food"), |
49 | | "wood": markForTranslation("Wood"), |
50 | | "metal": markForTranslation("Metal"), |
51 | | "stone": markForTranslation("Stone"), |
52 | | }; |
| 37 | this.resourceNames = {}; |
53 | 38 | this.disabledTemplates = {}; |
54 | 39 | this.disabledTechnologies = {}; |
55 | 40 | this.startingTechnologies = []; |
| 41 | |
| 42 | // Initial resources and trading goods probability in steps of 5 |
| 43 | let resCodes = Resources.GetCodes(); |
| 44 | let quotient = Math.floor(20 / resCodes.length); |
| 45 | let remainder = 20 % resCodes.length; |
| 46 | for (let i in resCodes) |
| 47 | { |
| 48 | let res = resCodes[i]; |
| 49 | this.resourceCount[res] = 300; |
| 50 | this.resourceNames[res] = Resources.GetResource(res).name; |
| 51 | this.tradingGoods.push({ |
| 52 | "goods": res, |
| 53 | "proba": 5 * (quotient + (+i < remainder ? 1 : 0)) |
| 54 | }); |
| 55 | } |
56 | 56 | }; |
57 | 57 | |
58 | 58 | Player.prototype.SetPlayerID = function(id) |
… |
… |
Player.prototype.UnBlockTraining = function()
|
197 | 197 | |
198 | 198 | Player.prototype.SetResourceCounts = function(resources) |
199 | 199 | { |
200 | | if (resources.food !== undefined) |
201 | | this.resourceCount.food = resources.food; |
202 | | if (resources.wood !== undefined) |
203 | | this.resourceCount.wood = resources.wood; |
204 | | if (resources.stone !== undefined) |
205 | | this.resourceCount.stone = resources.stone; |
206 | | if (resources.metal !== undefined) |
207 | | this.resourceCount.metal = resources.metal; |
| 200 | for (let res in resources) |
| 201 | if (this.resourceCount[res]) |
| 202 | this.resourceCount[res] = resources[res]; |
208 | 203 | }; |
209 | 204 | |
210 | 205 | Player.prototype.GetResourceCounts = function() |
… |
… |
Player.prototype.SubtractResourcesOrNotify = function(amounts)
|
297 | 292 | |
298 | 293 | // Subtract the resources |
299 | 294 | for (var type in amounts) |
300 | | this.resourceCount[type] -= amounts[type]; |
| 295 | if (this.resourceCount[type]) |
| 296 | this.resourceCount[type] -= amounts[type]; |
301 | 297 | |
302 | 298 | return true; |
303 | 299 | }; |
… |
… |
Player.prototype.GetTradingGoods = function()
|
340 | 336 | |
341 | 337 | Player.prototype.SetTradingGoods = function(tradingGoods) |
342 | 338 | { |
343 | | var sumProba = 0; |
344 | | for (var resource in tradingGoods) |
| 339 | let resCodes = Resources.GetCodes(); |
| 340 | let sumProba = 0; |
| 341 | for (let resource in tradingGoods) |
| 342 | { |
| 343 | if (resCodes.indexOf(resource) == -1) |
| 344 | { |
| 345 | error("Invalid trading goods: " + uneval(tradingGoods)); |
| 346 | return; |
| 347 | } |
345 | 348 | sumProba += tradingGoods[resource]; |
346 | | if (sumProba != 100) // consistency check |
| 349 | } |
| 350 | |
| 351 | if (sumProba != 100) |
347 | 352 | { |
348 | | error("Player.js SetTradingGoods: " + uneval(tradingGoods)); |
349 | | tradingGoods = { "food": 20, "wood":20, "stone":30, "metal":30 }; |
| 353 | error("Invalid trading goods: " + uneval(tradingGoods)); |
| 354 | return; |
350 | 355 | } |
351 | 356 | |
352 | 357 | this.tradingGoods = []; |
353 | | for (var resource in tradingGoods) |
354 | | this.tradingGoods.push( {"goods": resource, "proba": tradingGoods[resource]} ); |
| 358 | for (let resource in tradingGoods) |
| 359 | this.tradingGoods.push({ |
| 360 | "goods": resource, |
| 361 | "proba": tradingGoods[resource] |
| 362 | }); |
355 | 363 | }; |
356 | 364 | |
357 | 365 | Player.prototype.GetState = function() |
-
diff --git a/binaries/data/mods/public/simulation/components/ProductionQueue.js b/binaries/data/mods/public/simulation/components/ProductionQueue.js
index 6c72202..e110ab1 100644
a
|
b
|
ProductionQueue.prototype.Schema =
|
31 | 31 | "</element>" + |
32 | 32 | "</optional>" + |
33 | 33 | "<element name='TechCostMultiplier' a:help='Multiplier to modify ressources cost and research time of technologies searched in this building.'>" + |
34 | | "<interleave>" + |
35 | | "<element name='food'><ref name='nonNegativeDecimal'/></element>" + |
36 | | "<element name='wood'><ref name='nonNegativeDecimal'/></element>" + |
37 | | "<element name='stone'><ref name='nonNegativeDecimal'/></element>" + |
38 | | "<element name='metal'><ref name='nonNegativeDecimal'/></element>" + |
39 | | "<element name='time'><ref name='nonNegativeDecimal'/></element>" + |
40 | | "</interleave>" + |
| 34 | Resources.BuildSchema("nonNegativeDecimal", ["time"]) + |
41 | 35 | "</element>"; |
42 | 36 | |
43 | 37 | ProductionQueue.prototype.Init = function() |
… |
… |
ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadat
|
260 | 254 | // TODO: there should probably be a limit on the number of queued batches |
261 | 255 | // TODO: there should be a way for the GUI to determine whether it's going |
262 | 256 | // to be possible to add a batch (based on resource costs and length limits) |
263 | | var cmpPlayer = QueryOwnerInterface(this.entity); |
| 257 | let cmpPlayer = QueryOwnerInterface(this.entity); |
| 258 | let resCodes = Resources.GetCodes(); |
264 | 259 | |
265 | 260 | if (this.queue.length < MAX_QUEUE_SIZE) |
266 | 261 | { |
… |
… |
ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadat
|
293 | 288 | var buildTime = ApplyValueModificationsToTemplate("Cost/BuildTime", +template.Cost.BuildTime, cmpPlayer.GetPlayerID(), template); |
294 | 289 | var time = timeMult * buildTime; |
295 | 290 | |
296 | | for (var r in template.Cost.Resources) |
| 291 | for (let res in template.Cost.Resources) |
297 | 292 | { |
298 | | costs[r] = ApplyValueModificationsToTemplate("Cost/Resources/"+r, +template.Cost.Resources[r], cmpPlayer.GetPlayerID(), template); |
299 | | totalCosts[r] = Math.floor(count * costs[r]); |
| 293 | if (resCodes.indexOf(res) < 0) |
| 294 | continue; |
| 295 | costs[res] = ApplyValueModificationsToTemplate("Cost/Resources/"+res, +template.Cost.Resources[res], cmpPlayer.GetPlayerID(), template); |
| 296 | totalCosts[res] = Math.floor(count * costs[res]); |
300 | 297 | } |
301 | 298 | |
302 | 299 | var population = ApplyValueModificationsToTemplate("Cost/Population", +template.Cost.Population, cmpPlayer.GetPlayerID(), template); |
… |
… |
ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadat
|
337 | 334 | var template = cmpDataTemplateManager.GetTechnologyTemplate(templateName); |
338 | 335 | if (!template) |
339 | 336 | return; |
340 | | var cmpPlayer = QueryOwnerInterface(this.entity); |
341 | 337 | let techCostMultiplier = this.GetTechCostMultiplier(); |
342 | 338 | let time = techCostMultiplier.time * template.researchTime * cmpPlayer.GetCheatTimeMultiplier(); |
343 | 339 | |
344 | | var cost = {}; |
| 340 | let cost = {}; |
345 | 341 | for (let res in template.cost) |
346 | | cost[res] = Math.floor(techCostMultiplier[res] * template.cost[res]); |
| 342 | { |
| 343 | if (resCodes.indexOf(res) < 0) |
| 344 | continue; |
| 345 | cost[res] = Math.floor((techCostMultiplier[res] || 1) * template.cost[res]); |
| 346 | } |
347 | 347 | |
348 | 348 | // TrySubtractResources should report error to player (they ran out of resources) |
349 | 349 | if (!cmpPlayer.TrySubtractResources(cost)) |
… |
… |
ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadat
|
361 | 361 | "player": cmpPlayer.GetPlayerID(), |
362 | 362 | "count": 1, |
363 | 363 | "technologyTemplate": templateName, |
364 | | "resources": deepcopy(template.cost), // need to copy to avoid serialization problems |
| 364 | "resources": cost, |
365 | 365 | "productionStarted": false, |
366 | 366 | "timeTotal": time*1000, |
367 | 367 | "timeRemaining": time*1000, |
… |
… |
ProductionQueue.prototype.RemoveBatch = function(id)
|
433 | 433 | // Refund the resource cost for this batch |
434 | 434 | var totalCosts = {}; |
435 | 435 | var cmpStatisticsTracker = QueryPlayerIDInterface(item.player, IID_StatisticsTracker); |
436 | | for each (var r in ["food", "wood", "stone", "metal"]) |
| 436 | for (let r of Resources.GetCodes()) |
437 | 437 | { |
| 438 | if (!item.resources[r]) |
| 439 | continue; |
438 | 440 | totalCosts[r] = Math.floor(item.count * item.resources[r]); |
439 | 441 | if (cmpStatisticsTracker) |
440 | 442 | cmpStatisticsTracker.IncreaseResourceUsedCounter(r, -totalCosts[r]); |
-
diff --git a/binaries/data/mods/public/simulation/components/ResourceDropsite.js b/binaries/data/mods/public/simulation/components/ResourceDropsite.js
index 819807c..a8c70f7 100644
a
|
b
|
ResourceDropsite.prototype.Schema =
|
4 | 4 | "<element name='Types'>" + |
5 | 5 | "<list>" + |
6 | 6 | "<zeroOrMore>" + |
7 | | "<choice>" + |
8 | | "<value>food</value>" + |
9 | | "<value>wood</value>" + |
10 | | "<value>stone</value>" + |
11 | | "<value>metal</value>" + |
12 | | "</choice>" + |
| 7 | Resources.BuildChoicesSchema() + |
13 | 8 | "</zeroOrMore>" + |
14 | 9 | "</list>" + |
15 | 10 | "</element>" + |
… |
… |
ResourceDropsite.prototype.Init = function()
|
24 | 19 | }; |
25 | 20 | |
26 | 21 | /** |
27 | | * Returns the list of resource types accepted by this dropsite. |
| 22 | * Returns the list of resource types accepted by this dropsite, |
| 23 | * as defined by it being referred to in the template and the resource being enabled. |
28 | 24 | */ |
29 | 25 | ResourceDropsite.prototype.GetTypes = function() |
30 | 26 | { |
31 | 27 | let types = ApplyValueModificationsToEntity("ResourceDropsite/Types", this.template.Types, this.entity); |
32 | | return types ? types.split(/\s+/) : []; |
| 28 | let resources = Resources.GetCodes(); |
| 29 | return types.split(/\s+/).filter(type => resources.indexOf(type.toLowerCase()) != -1); |
33 | 30 | }; |
34 | 31 | |
35 | 32 | /** |
-
diff --git a/binaries/data/mods/public/simulation/components/ResourceGatherer.js b/binaries/data/mods/public/simulation/components/ResourceGatherer.js
index acd5fbd..3a40376 100644
a
|
b
|
ResourceGatherer.prototype.Schema =
|
25 | 25 | "<ref name='positiveDecimal'/>" + |
26 | 26 | "</element>" + |
27 | 27 | "<element name='Rates' a:help='Per-resource-type gather rate multipliers. If a resource type is not specified then it cannot be gathered by this unit'>" + |
28 | | "<interleave>" + |
29 | | "<optional><element name='food' a:help='Food gather rate (may be overridden by \"food.*\" subtypes)'><ref name='positiveDecimal'/></element></optional>" + |
30 | | "<optional><element name='wood' a:help='Wood gather rate'><ref name='positiveDecimal'/></element></optional>" + |
31 | | "<optional><element name='stone' a:help='Stone gather rate'><ref name='positiveDecimal'/></element></optional>" + |
32 | | "<optional><element name='metal' a:help='Metal gather rate'><ref name='positiveDecimal'/></element></optional>" + |
33 | | "<optional><element name='treasure' a:help='Treasure gather rate (only presense on value makes sense, size is only used to determine the delay before gathering, so it should be set to 1)'><ref name='positiveDecimal'/></element></optional>" + |
34 | | "<optional><element name='food.fish' a:help='Fish gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" + |
35 | | "<optional><element name='food.fruit' a:help='Fruit gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" + |
36 | | "<optional><element name='food.grain' a:help='Grain gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" + |
37 | | "<optional><element name='food.meat' a:help='Meat gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" + |
38 | | "<optional><element name='food.milk' a:help='Milk gather rate (overrides \"food\")'><ref name='positiveDecimal'/></element></optional>" + |
39 | | "<optional><element name='wood.tree' a:help='Tree gather rate (overrides \"wood\")'><ref name='positiveDecimal'/></element></optional>" + |
40 | | "<optional><element name='wood.ruins' a:help='Tree gather rate (overrides \"wood\")'><ref name='positiveDecimal'/></element></optional>" + |
41 | | "<optional><element name='stone.rock' a:help='Rock gather rate (overrides \"stone\")'><ref name='positiveDecimal'/></element></optional>" + |
42 | | "<optional><element name='stone.ruins' a:help='Rock gather rate (overrides \"stone\")'><ref name='positiveDecimal'/></element></optional>" + |
43 | | "<optional><element name='metal.ore' a:help='Ore gather rate (overrides \"metal\")'><ref name='positiveDecimal'/></element></optional>" + |
44 | | "<optional><element name='treasure.food' a:help='Food treasure gather rate (overrides \"treasure\")'><ref name='positiveDecimal'/></element></optional>" + |
45 | | "<optional><element name='treasure.wood' a:help='Wood treasure gather rate (overrides \"treasure\")'><ref name='positiveDecimal'/></element></optional>" + |
46 | | "<optional><element name='treasure.stone' a:help='Stone treasure gather rate (overrides \"treasure\")'><ref name='positiveDecimal'/></element></optional>" + |
47 | | "<optional><element name='treasure.metal' a:help='Metal treasure gather rate (overrides \"treasure\")'><ref name='positiveDecimal'/></element></optional>" + |
48 | | "</interleave>" + |
| 28 | Resources.BuildSchema("positiveDecimal", ["treasure"], true) + |
49 | 29 | "</element>" + |
50 | 30 | "<element name='Capacities' a:help='Per-resource-type maximum carrying capacity'>" + |
51 | | "<interleave>" + |
52 | | "<element name='food' a:help='Food capacity'><ref name='positiveDecimal'/></element>" + |
53 | | "<element name='wood' a:help='Wood capacity'><ref name='positiveDecimal'/></element>" + |
54 | | "<element name='stone' a:help='Stone capacity'><ref name='positiveDecimal'/></element>" + |
55 | | "<element name='metal' a:help='Metal capacity'><ref name='positiveDecimal'/></element>" + |
56 | | "</interleave>" + |
| 31 | Resources.BuildSchema("positiveDecimal") + |
57 | 32 | "</element>"; |
58 | 33 | |
59 | 34 | ResourceGatherer.prototype.Init = function() |
… |
… |
ResourceGatherer.prototype.RecalculateGatherRatesAndCapacities = function()
|
137 | 112 | this.rates = {}; |
138 | 113 | for (let r in this.template.Rates) |
139 | 114 | { |
| 115 | let type = r.split("."); |
| 116 | let res = Resources.GetResource(type[0]); |
| 117 | |
| 118 | if (!res && type[0] !== "treasure" || type.length > 1 && !res.subtypes[type[1]]) |
| 119 | continue; |
| 120 | |
140 | 121 | let rate = ApplyValueModificationsToEntity("ResourceGatherer/Rates/" + r, +this.template.Rates[r], this.entity); |
141 | 122 | this.rates[r] = rate * this.baseSpeed; |
142 | 123 | } |
… |
… |
ResourceGatherer.prototype.GetRange = function()
|
174 | 155 | |
175 | 156 | /** |
176 | 157 | * Try to gather treasure |
177 | | * @return 'true' if treasure is successfully gathered and 'false' in the other case |
| 158 | * @return 'true' if treasure is successfully gathered, otherwise 'false' |
178 | 159 | */ |
179 | 160 | ResourceGatherer.prototype.TryInstantGather = function(target) |
180 | 161 | { |
-
diff --git a/binaries/data/mods/public/simulation/components/ResourceSupply.js b/binaries/data/mods/public/simulation/components/ResourceSupply.js
index 04e95da..367810d 100644
a
|
b
|
ResourceSupply.prototype.Schema =
|
12 | 12 | "<element name='Amount' a:help='Amount of resources available from this entity'>" + |
13 | 13 | "<choice><data type='nonNegativeInteger'/><value>Infinity</value></choice>" + |
14 | 14 | "</element>" + |
15 | | "<element name='Type' a:help='Type of resources'>" + |
16 | | "<choice>" + |
17 | | "<value>wood.tree</value>" + |
18 | | "<value>wood.ruins</value>" + |
19 | | "<value>stone.rock</value>" + |
20 | | "<value>stone.ruins</value>" + |
21 | | "<value>metal.ore</value>" + |
22 | | "<value>food.fish</value>" + |
23 | | "<value>food.fruit</value>" + |
24 | | "<value>food.grain</value>" + |
25 | | "<value>food.meat</value>" + |
26 | | "<value>food.milk</value>" + |
27 | | "<value>treasure.wood</value>" + |
28 | | "<value>treasure.stone</value>" + |
29 | | "<value>treasure.metal</value>" + |
30 | | "<value>treasure.food</value>" + |
31 | | "</choice>" + |
| 15 | "<element name='Type' a:help='Type and Subtype of resource available from this entity'>" + |
| 16 | Resources.BuildChoicesSchema(true, true) + |
32 | 17 | "</element>" + |
33 | 18 | "<element name='MaxGatherers' a:help='Amount of gatherers who can gather resources from this entity at the same time'>" + |
34 | 19 | "<data type='nonNegativeInteger'/>" + |
… |
… |
ResourceSupply.prototype.Init = function()
|
45 | 30 | this.amount = this.GetMaxAmount(); |
46 | 31 | |
47 | 32 | this.gatherers = []; // list of IDs for each players |
48 | | var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); // system component so that's safe. |
49 | | var numPlayers = cmpPlayerManager.GetNumPlayers(); |
50 | | for (var i = 0; i <= numPlayers; ++i) // use "<=" because we want Gaia too. |
| 33 | let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); // system component so that's safe. |
| 34 | let numPlayers = cmpPlayerManager.GetNumPlayers(); |
| 35 | for (let i = 0; i <= numPlayers; ++i) // use "<=" because we want Gaia too. |
51 | 36 | this.gatherers.push([]); |
52 | 37 | |
53 | 38 | this.infinite = !isFinite(+this.template.Amount); |
54 | 39 | |
55 | | [this.type,this.subType] = this.template.Type.split('.'); |
56 | | this.cachedType = { "generic" : this.type, "specific" : this.subType }; |
| 40 | [this.type, this.subtype] = this.template.Type.split('.'); |
| 41 | let resData = Resources.GetResource(this.type); |
| 42 | if (this.type === "treasure") |
| 43 | resData = { "subtypes": Resources.GetNames() }; |
57 | 44 | |
| 45 | // Remove entity from gameworld if the resource supplied by this entity is disabled or not valid. |
| 46 | if (!resData || !resData.subtypes[this.subtype]) |
| 47 | Engine.DestroyEntity(this.entity); |
| 48 | |
| 49 | this.cachedType = { "generic": this.type, "specific": this.subtype }; |
58 | 50 | }; |
59 | 51 | |
60 | 52 | ResourceSupply.prototype.IsInfinite = function() |
-
diff --git a/binaries/data/mods/public/simulation/components/ResourceTrickle.js b/binaries/data/mods/public/simulation/components/ResourceTrickle.js
index 5c554e7..f194a1f 100644
a
|
b
|
function ResourceTrickle() {}
|
3 | 3 | ResourceTrickle.prototype.Schema = |
4 | 4 | "<a:help>Controls the resource trickle ability of the unit.</a:help>" + |
5 | 5 | "<element name='Rates' a:help='Trickle Rates'>" + |
6 | | "<interleave>" + |
7 | | "<optional>" + |
8 | | "<element name='food' a:help='Food given to the player every interval'>" + |
9 | | "<ref name='nonNegativeDecimal'/>" + |
10 | | "</element>" + |
11 | | "</optional>" + |
12 | | "<optional>" + |
13 | | "<element name='wood' a:help='Wood given to the player every interval'>" + |
14 | | "<ref name='nonNegativeDecimal'/>" + |
15 | | "</element>" + |
16 | | "</optional>" + |
17 | | "<optional>" + |
18 | | "<element name='stone' a:help='Stone given to the player every interval'>" + |
19 | | "<ref name='nonNegativeDecimal'/>" + |
20 | | "</element>" + |
21 | | "</optional>" + |
22 | | "<optional>" + |
23 | | "<element name='metal' a:help='Metal given to the player every interval'>" + |
24 | | "<ref name='nonNegativeDecimal'/>" + |
25 | | "</element>" + |
26 | | "</optional>" + |
27 | | "</interleave>" + |
| 6 | Resources.BuildSchema("nonNegativeDecimal") + |
28 | 7 | "</element>" + |
29 | 8 | "<element name='Interval' a:help='Number of miliseconds must pass for the player to gain the next trickle.'>" + |
30 | 9 | "<ref name='nonNegativeDecimal'/>" + |
… |
… |
ResourceTrickle.prototype.GetTimer = function()
|
45 | 24 | |
46 | 25 | ResourceTrickle.prototype.GetRates = function() |
47 | 26 | { |
48 | | var rates = {}; |
49 | | for (var resource in this.template.Rates) |
| 27 | let rates = {}; |
| 28 | let resCodes = Resources.GetCodes(); |
| 29 | for (let resource in this.template.Rates) |
| 30 | { |
| 31 | if (resCodes.indexOf(resource) < 0) |
| 32 | continue; |
50 | 33 | rates[resource] = ApplyValueModificationsToEntity("ResourceTrickle/Rates/"+resource, +this.template.Rates[resource], this.entity); |
| 34 | } |
51 | 35 | |
52 | 36 | return rates; |
53 | 37 | }; |
… |
… |
ResourceTrickle.prototype.GetRates = function()
|
55 | 39 | // Do the actual work here |
56 | 40 | ResourceTrickle.prototype.Trickle = function(data, lateness) |
57 | 41 | { |
58 | | var cmpPlayer = QueryOwnerInterface(this.entity); |
59 | | if (!cmpPlayer) |
60 | | return; |
61 | | |
62 | | var rates = this.GetRates(); |
63 | | for (var resource in rates) |
64 | | cmpPlayer.AddResource(resource, rates[resource]); |
| 42 | let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); |
| 43 | if (cmpPlayer) |
| 44 | cmpPlayer.AddResources(this.GetRates()); |
65 | 45 | }; |
66 | 46 | |
67 | 47 | Engine.RegisterComponentType(IID_ResourceTrickle, "ResourceTrickle", ResourceTrickle); |
-
diff --git a/binaries/data/mods/public/simulation/components/StatisticsTracker.js b/binaries/data/mods/public/simulation/components/StatisticsTracker.js
index 3bc1f79..482502d 100644
a
|
b
|
StatisticsTracker.prototype.Init = function()
|
105 | 105 | this.buildingsCapturedValue = 0; |
106 | 106 | |
107 | 107 | this.resourcesGathered = { |
108 | | "food": 0, |
109 | | "wood": 0, |
110 | | "metal": 0, |
111 | | "stone": 0, |
112 | 108 | "vegetarianFood": 0 |
113 | 109 | }; |
114 | | this.resourcesUsed = { |
115 | | "food": 0, |
116 | | "wood": 0, |
117 | | "metal": 0, |
118 | | "stone": 0 |
119 | | }; |
120 | | this.resourcesSold = { |
121 | | "food": 0, |
122 | | "wood": 0, |
123 | | "metal": 0, |
124 | | "stone": 0 |
125 | | }; |
126 | | this.resourcesBought = { |
127 | | "food": 0, |
128 | | "wood": 0, |
129 | | "metal": 0, |
130 | | "stone": 0 |
131 | | }; |
| 110 | this.resourcesUsed = {}; |
| 111 | this.resourcesSold = {}; |
| 112 | this.resourcesBought = {}; |
| 113 | for (let res of Resources.GetCodes()) |
| 114 | { |
| 115 | this.resourcesGathered[res] = 0; |
| 116 | this.resourcesUsed[res] = 0; |
| 117 | this.resourcesSold[res] = 0; |
| 118 | this.resourcesBought[res] = 0; |
| 119 | } |
132 | 120 | |
133 | 121 | this.tributesSent = 0; |
134 | 122 | this.tributesReceived = 0; |
… |
… |
StatisticsTracker.prototype.IncreaseResourceGatheredCounter = function(type, amo
|
347 | 335 | */ |
348 | 336 | StatisticsTracker.prototype.IncreaseResourceUsedCounter = function(type, amount) |
349 | 337 | { |
350 | | this.resourcesUsed[type] += amount; |
| 338 | if (typeof this.resourcesUsed[type] === "number") |
| 339 | this.resourcesUsed[type] += amount; |
351 | 340 | }; |
352 | 341 | |
353 | 342 | StatisticsTracker.prototype.IncreaseTreasuresCollectedCounter = function() |
-
diff --git a/binaries/data/mods/public/simulation/components/Trader.js b/binaries/data/mods/public/simulation/components/Trader.js
index 735778d..771a814 100644
a
|
b
|
|
4 | 4 | // Additional gain for ships for each garrisoned trader, in percents |
5 | 5 | const GARRISONED_TRADER_ADDITION = 20; |
6 | 6 | |
7 | | // Array of resource names |
8 | | const RESOURCES = ["food", "wood", "stone", "metal"]; |
9 | | |
10 | 7 | function Trader() {} |
11 | 8 | |
12 | 9 | Trader.prototype.Schema = |
-
diff --git a/binaries/data/mods/public/simulation/components/Upgrade.js b/binaries/data/mods/public/simulation/components/Upgrade.js
index 6179076..f185bf0 100644
a
|
b
|
Upgrade.prototype.Schema =
|
29 | 29 | "<element name='Cost' a:help='Resource cost to upgrade this unit'>" + |
30 | 30 | "<oneOrMore>" + |
31 | 31 | "<choice>" + |
32 | | "<element name='food'><data type='nonNegativeInteger'/></element>" + |
33 | | "<element name='wood'><data type='nonNegativeInteger'/></element>" + |
34 | | "<element name='stone'><data type='nonNegativeInteger'/></element>" + |
35 | | "<element name='metal'><data type='nonNegativeInteger'/></element>" + |
| 32 | Resources.BuildSchema("nonNegativeInteger") + |
36 | 33 | "</choice>" + |
37 | 34 | "</oneOrMore>" + |
38 | 35 | "</element>" + |
-
diff --git a/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js b/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
index 6ae026e..e90d4b6 100644
a
|
b
|
Engine.LoadComponentScript("interfaces/Upgrade.js");
|
33 | 33 | Engine.LoadComponentScript("interfaces/BuildingAI.js"); |
34 | 34 | Engine.LoadComponentScript("GuiInterface.js"); |
35 | 35 | |
| 36 | Resources = { |
| 37 | "GetCodes": () => ["food", "metal", "stone", "wood"], |
| 38 | "GetNames": () => ({ |
| 39 | "food": "Food", |
| 40 | "metal": "Metal", |
| 41 | "stone": "Stone", |
| 42 | "wood": "Wood" |
| 43 | }), |
| 44 | "GetResource": () => ({}), |
| 45 | }; |
| 46 | |
36 | 47 | var cmp = ConstructComponent(SYSTEM_ENTITY, "GuiInterface"); |
37 | 48 | |
38 | 49 | |
… |
… |
TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
|
330 | 341 | circularMap: false, |
331 | 342 | timeElapsed: 0, |
332 | 343 | gameType: "conquest", |
333 | | barterPrices: {buy: {food: 150}, sell: {food: 25}} |
| 344 | "barterPrices": { |
| 345 | "buy": { "food": 150 }, |
| 346 | "sell": { "food": 25 } |
| 347 | }, |
| 348 | "resources": { |
| 349 | "codes": ["food", "metal", "stone", "wood"], |
| 350 | "names": { |
| 351 | "food": "Food", |
| 352 | "metal": "Metal", |
| 353 | "stone": "Stone", |
| 354 | "wood": "Wood", |
| 355 | }, |
| 356 | "aiInfluenceGroups": { |
| 357 | "food": 0, |
| 358 | "metal": 0, |
| 359 | "stone": 0, |
| 360 | "wood": 0, |
| 361 | } |
| 362 | }, |
334 | 363 | }); |
335 | 364 | |
336 | 365 | TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), { |
… |
… |
TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
|
449 | 478 | circularMap: false, |
450 | 479 | timeElapsed: 0, |
451 | 480 | gameType: "conquest", |
452 | | barterPrices: {buy: {food: 150}, sell: {food: 25}} |
| 481 | "barterPrices": { |
| 482 | "buy": { "food": 150 }, |
| 483 | "sell": { "food": 25 } |
| 484 | }, |
| 485 | resources: { |
| 486 | codes: ["food", "metal", "stone", "wood"], |
| 487 | names: { |
| 488 | "food": "Food", |
| 489 | "metal": "Metal", |
| 490 | "stone": "Stone", |
| 491 | "wood": "Wood", |
| 492 | }, |
| 493 | aiInfluenceGroups: { |
| 494 | "food": 0, |
| 495 | "metal": 0, |
| 496 | "stone": 0, |
| 497 | "wood": 0, |
| 498 | } |
| 499 | }, |
453 | 500 | }); |
454 | 501 | |
455 | 502 | |
-
diff --git a/binaries/data/mods/public/simulation/components/tests/test_Player.js b/binaries/data/mods/public/simulation/components/tests/test_Player.js
index abb2d73..3d8136e 100644
a
|
b
|
Engine.LoadComponentScript("interfaces/Player.js");
|
3 | 3 | Engine.LoadComponentScript("interfaces/TechnologyManager.js"); |
4 | 4 | Engine.LoadComponentScript("Player.js"); |
5 | 5 | |
| 6 | Resources = { |
| 7 | "GetCodes": () => ["food", "metal", "stone", "wood"], |
| 8 | "GetResource": () => ({}), |
| 9 | }; |
| 10 | |
6 | 11 | var cmpPlayer = ConstructComponent(10, "Player"); |
7 | 12 | |
8 | 13 | TS_ASSERT_EQUALS(cmpPlayer.GetPopulationCount(), 0); |
-
diff --git a/binaries/data/mods/public/simulation/data/resources/food.json b/binaries/data/mods/public/simulation/data/resources/food.json
new file mode 100644
index 0000000..5d4461b
-
|
+
|
|
| 1 | { |
| 2 | "code": "food", |
| 3 | "name": "Food", |
| 4 | "order": 1, |
| 5 | "subtypes": { |
| 6 | "fish": "Fish", |
| 7 | "fruit": "Fruit", |
| 8 | "grain": "Grain", |
| 9 | "meat": "Meat", |
| 10 | "milk": "Milk" |
| 11 | }, |
| 12 | "truePrice": 100, |
| 13 | "aiAnalysisInfluenceGroup": 0, |
| 14 | "enabled": true |
| 15 | } |
-
diff --git a/binaries/data/mods/public/simulation/data/resources/metal.json b/binaries/data/mods/public/simulation/data/resources/metal.json
new file mode 100644
index 0000000..286fda4
-
|
+
|
|
| 1 | { |
| 2 | "code": "metal", |
| 3 | "name": "Metal", |
| 4 | "order": 4, |
| 5 | "subtypes": { |
| 6 | "ore": "Ore" |
| 7 | }, |
| 8 | "truePrice": 100, |
| 9 | "aiAnalysisInfluenceGroup": 2, |
| 10 | "enabled": true |
| 11 | } |
-
diff --git a/binaries/data/mods/public/simulation/data/resources/stone.json b/binaries/data/mods/public/simulation/data/resources/stone.json
new file mode 100644
index 0000000..e78272e
-
|
+
|
|
| 1 | { |
| 2 | "code": "stone", |
| 3 | "name": "Stone", |
| 4 | "order": 3, |
| 5 | "subtypes": { |
| 6 | "rock": "Rock", |
| 7 | "ruins": "Ruins" |
| 8 | }, |
| 9 | "truePrice": 100, |
| 10 | "aiAnalysisInfluenceGroup": 2, |
| 11 | "enabled": true |
| 12 | } |
-
diff --git a/binaries/data/mods/public/simulation/data/resources/wood.json b/binaries/data/mods/public/simulation/data/resources/wood.json
new file mode 100644
index 0000000..25f90a1
-
|
+
|
|
| 1 | { |
| 2 | "code": "wood", |
| 3 | "name": "Wood", |
| 4 | "order": 2, |
| 5 | "subtypes": { |
| 6 | "tree": "Tree", |
| 7 | "ruins": "Ruins" |
| 8 | }, |
| 9 | "truePrice": 100, |
| 10 | "aiAnalysisInfluenceGroup": 1, |
| 11 | "enabled": true |
| 12 | } |
-
diff --git a/binaries/data/mods/public/simulation/helpers/Resources.js b/binaries/data/mods/public/simulation/helpers/Resources.js
new file mode 100644
index 0000000..fa5cac0
-
|
+
|
|
| 1 | /** |
| 2 | * Builds a RelaxRNG schema based on currently valid elements. |
| 3 | * |
| 4 | * To prevent validation errors, disabled resources are included in the schema. |
| 5 | * |
| 6 | * @param datatype - The datatype of the element |
| 7 | * @param additional - Array of additional data elements. Time, xp, treasure, etc. |
| 8 | * @param subtypes - If true, resource subtypes will be included as well. |
| 9 | * @return RelaxNG schema string |
| 10 | */ |
| 11 | Resources.prototype.BuildSchema = function(datatype, additional = [], subtypes = false) |
| 12 | { |
| 13 | if (!datatype) |
| 14 | return ""; |
| 15 | |
| 16 | switch (datatype) |
| 17 | { |
| 18 | case "decimal": |
| 19 | case "nonNegativeDecimal": |
| 20 | case "positiveDecimal": |
| 21 | datatype = "<ref name='" + datatype + "'/>"; |
| 22 | break; |
| 23 | |
| 24 | default: |
| 25 | datatype = "<data type='" + datatype + "'/>"; |
| 26 | } |
| 27 | |
| 28 | let resCodes = this.resourceData.map(resource => resource.code); |
| 29 | let schema = ""; |
| 30 | for (let res of resCodes.concat(additional)) |
| 31 | schema += |
| 32 | "<optional>" + |
| 33 | "<element name='" + res + "'>" + |
| 34 | datatype + |
| 35 | "</element>" + |
| 36 | "</optional>"; |
| 37 | |
| 38 | if (!subtypes) |
| 39 | return "<interleave>" + schema + "</interleave>"; |
| 40 | |
| 41 | for (let res of this.resourceData) |
| 42 | for (let subtype in res.subtypes) |
| 43 | schema += |
| 44 | "<optional>" + |
| 45 | "<element name='" + res.code + "." + subtype + "'>" + |
| 46 | datatype + |
| 47 | "</element>" + |
| 48 | "</optional>"; |
| 49 | |
| 50 | if (additional.indexOf("treasure") !== -1) |
| 51 | for (let res of resCodes) |
| 52 | schema += |
| 53 | "<optional>" + |
| 54 | "<element name='" + "treasure." + res + "'>" + |
| 55 | datatype + |
| 56 | "</element>" + |
| 57 | "</optional>"; |
| 58 | |
| 59 | return "<interleave>" + schema + "</interleave>"; |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * Builds the value choices for a RelaxNG `<choice></choice>` object, based on currently valid resources. |
| 64 | * |
| 65 | * @oaram subtypes - If set to true, the choices returned will be resource subtypes, rather than main types |
| 66 | * @param treasure - If set to true, the pseudo resource 'treasure' (or its subtypes) will be included |
| 67 | * @return String of RelaxNG Schema `<choice/>` values. |
| 68 | */ |
| 69 | Resources.prototype.BuildChoicesSchema = function(subtypes = false, treasure = false) |
| 70 | { |
| 71 | let schema = ""; |
| 72 | |
| 73 | if (!subtypes) |
| 74 | { |
| 75 | let resCodes = this.resourceData.map(resource => resource.code); |
| 76 | if (treasure) |
| 77 | resCodes.push("treasure"); |
| 78 | for (let res of resCodes) |
| 79 | schema += "<value>" + res + "</value>"; |
| 80 | } |
| 81 | else |
| 82 | for (let res of this.resourceData) |
| 83 | { |
| 84 | for (let subtype in res.subtypes) |
| 85 | schema += "<value>" + res.code + "." + subtype + "</value>"; |
| 86 | if (treasure) |
| 87 | schema += "<value>" + "treasure." + res.code + "</value>"; |
| 88 | } |
| 89 | |
| 90 | return "<choice>" + schema + "</choice>"; |
| 91 | } |
| 92 | |
| 93 | Resources = new Resources(); |