diff --git a/binaries/data/mods/public/globalscripts/Resources.js b/binaries/data/mods/public/globalscripts/Resources.js
new file mode 100644
index 0000000..04c3056
--- /dev/null
+++ b/binaries/data/mods/public/globalscripts/Resources.js
@@ -0,0 +1,164 @@
+
+/**
+ * Resources Global
+ *
+ * Engine.FindJSONFiles only exists within the session context
+ * Engine.BuildDirEntList only exists within the gui context
+ * The AI and test contexts have no access to any JSON file access functions
+ */
+function Resources()
+{
+ let jsonFiles = [];
+ if (Engine.FindJSONFiles)
+ {
+ jsonFiles = Engine.FindJSONFiles("resources", false);
+ for (let file in jsonFiles)
+ jsonFiles[file] = "resources/" + jsonFiles[file] + ".json";
+ }
+ else if (Engine.BuildDirEntList)
+ jsonFiles = Engine.BuildDirEntList("simulation/data/resources/", "*.json", false);
+ else
+ {
+ warn("Resources: No JSON access functions are unavailable");
+ return;
+ }
+
+ this.resourceData = [];
+ this.resourceCodes = [];
+
+ for (let filename of jsonFiles)
+ {
+ let data = Engine.ReadJSONFile(filename);
+ if (!data)
+ continue;
+
+ data.subtypeNames = data.subtypes;
+ data.subtypes = Object.keys(data.subtypes);
+
+ this.resourceData.push(data);
+ if (data.enabled)
+ this.resourceCodes.push(data.code);
+ }
+};
+
+Resources.prototype.GetData = function()
+{
+ return this.resourceData.filter(resource => resource.enabled);
+};
+
+Resources.prototype.GetResource = function(type)
+{
+ let lType = type.toLowerCase();
+ return this.GetData().find(resource => resource.code == lType);
+};
+
+Resources.prototype.GetCodes = function()
+{
+ return this.resourceCodes;
+};
+
+/**
+ * Returns an object containing untranslated resource names mapped to
+ * resource codes. Includes subtypes.
+ */
+Resources.prototype.GetNames = function()
+{
+ let names = {};
+ for (let res of this.GetData())
+ {
+ names[res.code] = res.name;
+ for (let subres of res.subtypes)
+ names[subres] = res.subtypeNames[subres]
+ }
+ return names;
+};
+
+/**
+ * Builds a RelaxRNG schema based on currently valid elements.
+ *
+ * To prevent validation errors, disabled resources are included in the schema.
+ *
+ * @param datatype The datatype of the element
+ * @param additional Array of additional data elements. Time, xp, treasure, etc.
+ * @param subtypes If true, resource subtypes will be included as well.
+ * @return RelaxNG schema string
+ */
+Resources.prototype.BuildSchema = function(datatype, additional = [], subtypes = false)
+{
+ if (!datatype)
+ return "";
+
+ switch (datatype)
+ {
+ case "decimal":
+ case "nonNegativeDecimal":
+ case "positiveDecimal":
+ datatype = "";
+ break;
+
+ default:
+ datatype = " ";
+ }
+
+ let resCodes = this.resourceData.map(resource => resource.code);
+ let schema = "";
+ for (let res of resCodes.concat(additional))
+ schema +=
+ "" +
+ "" +
+ datatype +
+ " " +
+ " ";
+
+ if (!subtypes)
+ return schema + " ";
+
+ for (let res of this.resourceData)
+ for (let subtype of res.subtypes)
+ schema +=
+ "" +
+ "" +
+ datatype +
+ " " +
+ " ";
+
+ if (additional.indexOf("treasure") !== -1)
+ for (let res of resCodes)
+ schema +=
+ "" +
+ "" +
+ datatype +
+ " " +
+ " ";
+
+ return schema + "";
+}
+
+/**
+ * Builds the value choices for a RelaxNG ` ` object, based on currently valid resources.
+ *
+ * @oaram subtypes If set to true, the choices returned will be resource subtypes, rather than main types
+ * @param treasure If set to true, the pseudo resource 'treasure' (or its subtypes) will be included
+ * @return String of RelaxNG Schema ` ` values.
+ */
+Resources.prototype.BuildChoicesSchema = function(subtypes = false, treasure = false)
+{
+ let schema = "";
+
+ if (!subtypes)
+ {
+ let resCodes = this.resourceData.map(resource => resource.code);
+ for (let res of resCodes.concat(treasure ? [ "treasure" ] : []))
+ schema += "" + res + " ";
+ }
+ else
+ for (let res of this.resourceData)
+ {
+ for (let subtype of res.subtypes)
+ schema += "" + res.code + "." + subtype + " ";
+ if (treasure)
+ schema += "" + "treasure." + res.code + " ";
+ }
+
+ return schema + " ";
+}
diff --git a/binaries/data/mods/public/gui/common/functions_utility.js b/binaries/data/mods/public/gui/common/functions_utility.js
index 9984cb0..9e89efd 100644
--- a/binaries/data/mods/public/gui/common/functions_utility.js
+++ b/binaries/data/mods/public/gui/common/functions_utility.js
@@ -416,3 +416,45 @@ function formatPlayerInfo(playerDataArray, playerStates)
return teamDescription.join("\n\n");
}
+
+/**
+ * Horizontally fit objects within a parent.
+ *
+ * @param margin - The gap, in px, between the repeated objects
+ * @param limit - The number of elements to fit
+ */
+function horizontallyDistributeObjects(parentName, margin = 0, limit = undefined)
+{
+ let objects = Engine.GetGUIObjectByName(parentName).children;
+ if (limit !== undefined)
+ objects = objects.splice(0, limit);
+
+ for (let i in objects)
+ {
+ i = +i;
+ let size = objects[i].size;
+ size.rleft = 100 / objects.length * i;
+ size.rright = 100 / objects.length * (i + 1);
+ size.right = -margin;
+ objects[i].size = size;
+ }
+}
+
+/**
+ * Hide all children after a certain index
+ *
+ * @param prefix - The part of the element name preceeding the index
+ * @param idx - The index from which to start
+ * @param prefix - The part of the element name after the index
+ */
+function hideRemaining(prefix, idx, suffix)
+{
+ while (true)
+ {
+ let obj = Engine.GetGUIObjectByName(prefix + idx + suffix);
+ if (!obj)
+ return;
+ obj.hidden = true;
+ ++idx;
+ }
+}
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/binaries/data/mods/public/gui/common/l10n.js
+++ b/binaries/data/mods/public/gui/common/l10n.js
@@ -1,75 +1,7 @@
-const localisedResourceNames = {
- "firstWord": {
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "food": translateWithContext("firstWord", "Food"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "meat": translateWithContext("firstWord", "Meat"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "metal": translateWithContext("firstWord", "Metal"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "ore": translateWithContext("firstWord", "Ore"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "rock": translateWithContext("firstWord", "Rock"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "ruins": translateWithContext("firstWord", "Ruins"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "stone": translateWithContext("firstWord", "Stone"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "treasure": translateWithContext("firstWord", "Treasure"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "tree": translateWithContext("firstWord", "Tree"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "wood": translateWithContext("firstWord", "Wood"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "fruit": translateWithContext("firstWord", "Fruit"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "grain": translateWithContext("firstWord", "Grain"),
- // Translation: Word as used at the beginning of a sentence or as a single-word sentence.
- "fish": translateWithContext("firstWord", "Fish"),
- },
- "withinSentence": {
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "food": translateWithContext("withinSentence", "Food"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "meat": translateWithContext("withinSentence", "Meat"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "metal": translateWithContext("withinSentence", "Metal"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "ore": translateWithContext("withinSentence", "Ore"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "rock": translateWithContext("withinSentence", "Rock"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "ruins": translateWithContext("withinSentence", "Ruins"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "stone": translateWithContext("withinSentence", "Stone"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "treasure": translateWithContext("withinSentence", "Treasure"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "tree": translateWithContext("withinSentence", "Tree"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "wood": translateWithContext("withinSentence", "Wood"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "fruit": translateWithContext("withinSentence", "Fruit"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "grain": translateWithContext("withinSentence", "Grain"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "fish": translateWithContext("withinSentence", "Fish"),
- }
-};
-function getLocalizedResourceName(resourceCode, context)
+function getLocalizedResourceName(resourceName, context)
{
- if (!localisedResourceNames[context])
- {
- warn("Internationalization: Unexpected context for resource type localization found: ‘" + context + "’. This context is not supported.");
- return resourceCode;
- }
- if (!localisedResourceNames[context][resourceCode])
- {
- warn("Internationalization: Unexpected resource type found with code ‘" + resourceCode + ". This resource type must be internationalized.");
- return resourceCode;
- }
- return localisedResourceNames[context][resourceCode];
+ return translateWithContext(context, resourceName);
}
/**
@@ -81,7 +13,7 @@ function getLocalizedResourceAmounts(resources)
.filter(type => resources[type] > 0)
.map(type => sprintf(translate("%(amount)s %(resourceType)s"), {
"amount": resources[type],
- "resourceType": getLocalizedResourceName(type, "withinSentence")
+ "resourceType": translateWithContext("withinSentence", type)
}));
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 ada0ca7..0fe4e0b 100644
--- a/binaries/data/mods/public/gui/common/tooltips.js
+++ b/binaries/data/mods/public/gui/common/tooltips.js
@@ -342,6 +342,7 @@ function getEntityCostComponentsTooltipString(template, trainNum, entity)
return costs;
}
+
function getGatherTooltip(template)
{
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 1708159..9a531f6 100644
--- a/binaries/data/mods/public/gui/session/diplomacy_window.xml
+++ b/binaries/data/mods/public/gui/session/diplomacy_window.xml
@@ -11,7 +11,7 @@
-
+
Name
@@ -23,19 +23,19 @@
Theirs
-
+
A
Ally
-
+
N
Neutral
-
+
E
Enemy
-
+
Tribute
@@ -48,22 +48,17 @@
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/gui/session/menu.js b/binaries/data/mods/public/gui/session/menu.js
index 494bd14..99bfd5e 100644
--- a/binaries/data/mods/public/gui/session/menu.js
+++ b/binaries/data/mods/public/gui/session/menu.js
@@ -22,9 +22,6 @@ const INITIAL_MENU_POSITION = "100%-164 " + MENU_TOP + " 100% " + MENU_BOTTOM;
// Number of pixels per millisecond to move
const MENU_SPEED = 1.2;
-// Available resources in trade and tribute menu
-const RESOURCES = ["food", "wood", "stone", "metal"];
-
// Trade menu: step for probability changes
const STEP = 5;
@@ -237,6 +234,7 @@ function openDiplomacy()
g_IsDiplomacyOpen = true;
let isCeasefireActive = GetSimState().ceasefireActive;
+ let resCodes = g_ResourceData.GetCodes();
// Get offset for one line
let onesize = Engine.GetGUIObjectByName("diplomacyPlayer[0]").size;
@@ -255,7 +253,13 @@ function openDiplomacy()
diplomacyFormatAttackRequestButton(i, myself || playerInactive || isCeasefireActive || !hasAllies || !g_Players[i].isEnemy[g_ViewedPlayer]);
}
- Engine.GetGUIObjectByName("diplomacyDialogPanel").hidden = false;
+ let dialog = Engine.GetGUIObjectByName("diplomacyDialogPanel");
+ let size = dialog.size;
+ let width = 260 + resCodes.length * 10;
+ size.left = -width;
+ size.right = width;
+ dialog.size = size;
+ dialog.hidden = false;
}
function diplomacySetupTexts(i, rowsize)
@@ -305,16 +309,22 @@ function diplomacyFormatStanceButtons(i, hidden)
function diplomacyFormatTributeButtons(i, hidden)
{
- for (let resource of RESOURCES)
+ let resNames = g_ResourceData.GetNames();
+ let resCodes = g_ResourceData.GetCodes();
+ let r = 0;
+ for (let resCode of resCodes)
{
- let button = Engine.GetGUIObjectByName("diplomacyPlayerTribute"+resource[0].toUpperCase()+resource.substring(1)+"["+(i-1)+"]");
+ let button = Engine.GetGUIObjectByName("diplomacyPlayer["+(i-1)+"]_tribute["+r+"]");
+ Engine.GetGUIObjectByName("diplomacyPlayer["+(i-1)+"]_tribute["+r+"]_image").sprite = "stretched:session/icons/resources/"+resCode+".png";
button.hidden = hidden;
+ setPanelObjectPosition(button, r, 8, 0);
+ ++r;
if (hidden)
continue;
button.enabled = controlsPlayer(g_ViewedPlayer);
- button.tooltip = formatTributeTooltip(i, resource, 100);
- button.onpress = (function(i, resource, button) {
+ button.tooltip = formatTributeTooltip(i, resNames[resCode], 100);
+ button.onpress = (function(i, resCode, button) {
// Shift+click to send 500, shift+click+click to send 1000, etc.
// See INPUT_MASSTRIBUTING in input.js
let multiplier = 1;
@@ -327,24 +337,24 @@ function diplomacyFormatTributeButtons(i, hidden)
}
let amounts = {};
- for (let type of RESOURCES)
- amounts[type] = 0;
- amounts[resource] = 100 * multiplier;
+ for (let res of resCodes)
+ amounts[res] = 0;
+ amounts[resCode] = 100 * multiplier,
- button.tooltip = formatTributeTooltip(i, resource, amounts[resource]);
+ button.tooltip = formatTributeTooltip(i, resNames[resCode], amounts[resCode]);
// This is in a closure so that we have access to `player`, `amounts`, and `multiplier` without some
// evil global variable hackery.
g_FlushTributing = function() {
Engine.PostNetworkCommand({ "type": "tribute", "player": i, "amounts": amounts });
multiplier = 1;
- button.tooltip = formatTributeTooltip(i, resource, 100);
+ button.tooltip = formatTributeTooltip(i, resNames[resCode], 100);
};
if (!isBatchTrainPressed)
g_FlushTributing();
};
- })(i, resource, button);
+ })(i, resCode, button);
}
}
@@ -398,37 +408,33 @@ function openTrade()
}
};
- var proba = Engine.GuiInterfaceCall("GetTradingGoods", g_ViewedPlayer);
- var button = {};
- var selec = RESOURCES[0];
- for (var i = 0; i < RESOURCES.length; ++i)
+ let proba = Engine.GuiInterfaceCall("GetTradingGoods", g_ViewedPlayer);
+ let button = {};
+ let resCodes = g_ResourceData.GetCodes();
+ let selec = resCodes[0];
+ hideRemaining("tradeResource[", resCodes.length, "]");
+
+ for (let i = 0; i < resCodes.length; ++i)
{
- var buttonResource = Engine.GetGUIObjectByName("tradeResource["+i+"]");
- if (i > 0)
- {
- var size = Engine.GetGUIObjectByName("tradeResource["+(i-1)+"]").size;
- var width = size.right - size.left;
- size.left += width;
- size.right += width;
- Engine.GetGUIObjectByName("tradeResource["+i+"]").size = size;
- }
- var resource = RESOURCES[i];
- proba[resource] = (proba[resource] ? proba[resource] : 0);
- var buttonResource = Engine.GetGUIObjectByName("tradeResourceButton["+i+"]");
- var icon = Engine.GetGUIObjectByName("tradeResourceIcon["+i+"]");
- icon.sprite = "stretched:session/icons/resources/" + resource + ".png";
- var label = Engine.GetGUIObjectByName("tradeResourceText["+i+"]");
- var buttonUp = Engine.GetGUIObjectByName("tradeArrowUp["+i+"]");
- var buttonDn = Engine.GetGUIObjectByName("tradeArrowDn["+i+"]");
- var iconSel = Engine.GetGUIObjectByName("tradeResourceSelection["+i+"]");
- button[resource] = { "up": buttonUp, "dn": buttonDn, "label": label, "sel": iconSel };
+ let buttonResource = Engine.GetGUIObjectByName("tradeResource["+i+"]");
+ setPanelObjectPosition(buttonResource, i, 8);
+ let resCode = resCodes[i];
+ proba[resCode] = (proba[resCode] ? proba[resCode] : 0);
+ buttonResource = Engine.GetGUIObjectByName("tradeResourceButton["+i+"]");
+ let icon = Engine.GetGUIObjectByName("tradeResourceIcon["+i+"]");
+ icon.sprite = "stretched:session/icons/resources/" + resCode + ".png";
+ let label = Engine.GetGUIObjectByName("tradeResourceText["+i+"]");
+ let buttonUp = Engine.GetGUIObjectByName("tradeArrowUp["+i+"]");
+ let buttonDn = Engine.GetGUIObjectByName("tradeArrowDn["+i+"]");
+ let iconSel = Engine.GetGUIObjectByName("tradeResourceSelection["+i+"]");
+ button[resCode] = { "up": buttonUp, "dn": buttonDn, "label": label, "sel": iconSel };
buttonResource.enabled = controlsPlayer(g_ViewedPlayer);
buttonResource.onpress = (function(resource){
return function() {
if (Engine.HotkeyIsPressed("session.fulltradeswap"))
{
- for (var ress of RESOURCES)
+ for (let ress of resCodes)
proba[ress] = 0;
proba[resource] = 100;
Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba});
@@ -436,7 +442,7 @@ function openTrade()
selec = resource;
updateButtons();
};
- })(resource);
+ })(resCode);
buttonUp.enabled = controlsPlayer(g_ViewedPlayer);
buttonUp.onpress = (function(resource){
@@ -446,7 +452,7 @@ function openTrade()
Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba});
updateButtons();
};
- })(resource);
+ })(resCode);
buttonDn.enabled = controlsPlayer(g_ViewedPlayer);
buttonDn.onpress = (function(resource){
@@ -456,7 +462,7 @@ function openTrade()
Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba});
updateButtons();
};
- })(resource);
+ })(resCode);
}
updateButtons();
@@ -464,7 +470,13 @@ function openTrade()
Engine.GetGUIObjectByName("landTraders").caption = getIdleLandTradersText(traderNumber);
Engine.GetGUIObjectByName("shipTraders").caption = getIdleShipTradersText(traderNumber);
- Engine.GetGUIObjectByName("tradeDialogPanel").hidden = false;
+ let dialog = Engine.GetGUIObjectByName("tradeDialogPanel");
+ let size = dialog.size;
+ let wid = resCodes.length * (58/2);
+ size.left = -(134 + wid);
+ size.right = (134 + wid);
+ dialog.size = size;
+ dialog.hidden = false;
}
function getIdleLandTradersText(traderNumber)
diff --git a/binaries/data/mods/public/gui/session/selection_details.js b/binaries/data/mods/public/gui/session/selection_details.js
index 29acc09..36fa2a6 100644
--- a/binaries/data/mods/public/gui/session/selection_details.js
+++ b/binaries/data/mods/public/gui/session/selection_details.js
@@ -13,10 +13,8 @@ function layoutSelectionMultiple()
function getResourceTypeDisplayName(resourceType)
{
let resourceCode = resourceType.generic;
- if (resourceCode == "treasure")
- return getLocalizedResourceName(resourceType.specific, "firstWord");
- else
- return getLocalizedResourceName(resourceCode, "firstWord");
+ let resourceName = g_ResourceData.GetNames()[(resourceCode == "treasure" ? resourceType.specific : resourceCode)]
+ return getLocalizedResourceName(resourceName, "firstWord");
}
// 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 c21fc4f..ceae7a9 100644
--- a/binaries/data/mods/public/gui/session/selection_panels.js
+++ b/binaries/data/mods/public/gui/session/selection_panels.js
@@ -34,6 +34,8 @@ let g_FormationsInfo = new Map();
let g_SelectionPanels = {};
+let g_BarterSell;
+
g_SelectionPanels.Alert = {
"getMaxNumberOfItems": function()
{
@@ -87,15 +89,14 @@ g_SelectionPanels.Alert = {
g_SelectionPanels.Barter = {
"getMaxNumberOfItems": function()
{
- return 4;
+ return 8;
},
"rowLength": 4,
"getItems": function(unitEntState, selection)
{
if (!unitEntState.barterMarket)
return [];
- // ["food", "wood", "stone", "metal"]
- return BARTER_RESOURCES;
+ return g_ResourceData.GetCodes();
},
"setupButton": function(data)
{
@@ -115,11 +116,14 @@ g_SelectionPanels.Barter = {
if (Engine.HotkeyIsPressed("session.massbarter"))
amountToSell *= BARTER_BUNCH_MULTIPLIER;
+ if (!g_BarterSell)
+ g_BarterSell = g_ResourceData.GetCodes()[0];
+
amount.Sell.caption = "-" + amountToSell;
let prices = data.unitEntState.barterMarket.prices;
amount.Buy.caption = "+" + Math.round(prices.sell[g_BarterSell] / prices.buy[data.item] * amountToSell);
- let resource = getLocalizedResourceName(data.item, "withinSentence");
+ let resource = getLocalizedResourceName(g_ResourceData.GetNames()[data.item], "firstWord");
button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource });
button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource });
@@ -164,8 +168,10 @@ g_SelectionPanels.Barter = {
button.Sell.hidden = false;
selectionIcon.hidden = !isSelected;
- setPanelObjectPosition(button.Sell, data.i, data.rowLength);
- setPanelObjectPosition(button.Buy, data.i + data.rowLength, data.rowLength);
+ let sellPos = data.i + (data.i >= data.rowLength ? data.rowLength : 0);
+ let buyPos = data.i + data.rowLength * (data.i >= data.rowLength ? 2 : 1);
+ setPanelObjectPosition(button.Sell, sellPos, data.rowLength);
+ setPanelObjectPosition(button.Buy, buyPos, data.rowLength);
return true;
}
};
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 fc0e7a6..46976f9 100644
--- a/binaries/data/mods/public/gui/session/selection_panels_helpers.js
+++ b/binaries/data/mods/public/gui/session/selection_panels_helpers.js
@@ -1,18 +1,11 @@
const BARTER_RESOURCE_AMOUNT_TO_SELL = 100;
const BARTER_BUNCH_MULTIPLIER = 5;
-const BARTER_RESOURCES = ["food", "wood", "stone", "metal"];
const BARTER_ACTIONS = ["Sell", "Buy"];
const GATE_ACTIONS = ["lock", "unlock"];
-// upgrade constants
const UPGRADING_NOT_STARTED = -2;
const UPGRADING_CHOSEN_OTHER = -1;
-// ==============================================
-// BARTER HELPERS
-// Resources to sell on barter panel
-var g_BarterSell = "food";
-
function canMoveSelectionIntoFormation(formationTemplate)
{
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
index f32e117..717efce 100644
--- 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
@@ -1,24 +1,24 @@
-
- Exchange resources:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js
index 35af7a7..af267cb 100644
--- a/binaries/data/mods/public/gui/session/session.js
+++ b/binaries/data/mods/public/gui/session/session.js
@@ -122,6 +122,7 @@ var g_EntityStates = {};
var g_TemplateData = {};
var g_TemplateDataWithoutLocalization = {};
var g_TechnologyData = {};
+var g_ResourceData = new Resources();
/**
* Top coordinate of the research list.
@@ -463,10 +464,18 @@ function updateTopPanel()
let viewPlayer = Engine.GetGUIObjectByName("viewPlayer");
viewPlayer.hidden = !g_IsObserver && !g_DevSettings.changePerspective;
- Engine.GetGUIObjectByName("food").hidden = !isPlayer;
- Engine.GetGUIObjectByName("wood").hidden = !isPlayer;
- Engine.GetGUIObjectByName("stone").hidden = !isPlayer;
- Engine.GetGUIObjectByName("metal").hidden = !isPlayer;
+ let resCodes = g_ResourceData.GetCodes();
+ let resNames = g_ResourceData.GetNames();
+ let r = 0;
+ for (let res of resCodes)
+ {
+ Engine.GetGUIObjectByName("resource["+r+"]_icon").sprite = "stretched:session/icons/resources/" + res + ".png";
+ Engine.GetGUIObjectByName("resource["+r+"]").hidden = !isPlayer;
+ ++r;
+ }
+ horizontallyDistributeObjects("resourceCounts", 0, r);
+ hideRemaining("resource[", r, "]");
+
Engine.GetGUIObjectByName("population").hidden = !isPlayer;
Engine.GetGUIObjectByName("diplomacyButton1").hidden = !isPlayer;
Engine.GetGUIObjectByName("tradeButton1").hidden = !isPlayer;
@@ -547,7 +556,8 @@ function leaveGame(willRejoin)
"disconnected": g_Disconnected,
"isReplay": g_IsReplay,
"replayDirectory": !g_HasRejoined && replayDirectory,
- "replaySelectionData": g_ReplaySelectionData
+ "replaySelectionData": g_ReplaySelectionData,
+ "resources": GetSimState().resources
}
});
}
@@ -947,14 +957,18 @@ function getAllyStatTooltip(resource)
function updatePlayerDisplay()
{
- let playerState = GetSimState().players[g_ViewedPlayer];
+ let simState = GetSimState();
+ let playerState = simState.players[g_ViewedPlayer];
if (!playerState)
return;
- for (let res of RESOURCES)
+ let resCodes = g_ResourceData.GetCodes();
+ let resNames = g_ResourceData.GetNames();
+ for (let r = 0; r < resCodes.length; ++r)
{
- Engine.GetGUIObjectByName("resource_" + res).caption = Math.floor(playerState.resourceCounts[res]);
- Engine.GetGUIObjectByName(res).tooltip = getLocalizedResourceName(res, "firstWord") + getAllyStatTooltip(res);
+ let res = resCodes[r];
+ Engine.GetGUIObjectByName("resource["+r+"]").tooltip = getLocalizedResourceName(resNames[res], "firstWord") + getAllyStatTooltip(res);
+ Engine.GetGUIObjectByName("resource["+r+"]_count").caption = Math.floor(playerState.resourceCounts[res]);
}
Engine.GetGUIObjectByName("resourcePop").caption = sprintf(translate("%(popCount)s/%(popLimit)s"), playerState);
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
--- a/binaries/data/mods/public/gui/session/top_panel/resource_food.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
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
--- a/binaries/data/mods/public/gui/session/top_panel/resource_metal.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
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..9d66e40 100644
--- a/binaries/data/mods/public/gui/session/top_panel/resource_population.xml
+++ b/binaries/data/mods/public/gui/session/top_panel/resource_population.xml
@@ -1,5 +1,5 @@
-
+
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
--- a/binaries/data/mods/public/gui/session/top_panel/resource_stone.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
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
--- a/binaries/data/mods/public/gui/session/top_panel/resource_wood.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
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..520aa35
--- /dev/null
+++ b/binaries/data/mods/public/gui/session/top_panel/resources.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/gui/session/trade_window.xml b/binaries/data/mods/public/gui/session/trade_window.xml
index 80b226c..0862bc1 100644
--- a/binaries/data/mods/public/gui/session/trade_window.xml
+++ b/binaries/data/mods/public/gui/session/trade_window.xml
@@ -16,7 +16,7 @@
-
+
diff --git a/binaries/data/mods/public/gui/structree/draw.js b/binaries/data/mods/public/gui/structree/draw.js
index 92c60ce..ee15fb3 100644
--- a/binaries/data/mods/public/gui/structree/draw.js
+++ b/binaries/data/mods/public/gui/structree/draw.js
@@ -235,18 +235,6 @@ function getPositionOffset(idx)
return size;
}
-function hideRemaining(prefix, idx, suffix)
-{
- let obj = Engine.GetGUIObjectByName(prefix + idx + suffix);
- while (obj)
- {
- obj.hidden = true;
- ++idx;
- obj = Engine.GetGUIObjectByName(prefix + idx + suffix);
- }
-}
-
-
/**
* Positions certain elements that only need to be positioned once
* (as does not reposition automatically).
diff --git a/binaries/data/mods/public/gui/structree/load.js b/binaries/data/mods/public/gui/structree/load.js
index 534747b..006de1b 100644
--- a/binaries/data/mods/public/gui/structree/load.js
+++ b/binaries/data/mods/public/gui/structree/load.js
@@ -5,28 +5,24 @@
*/
function getGatherRates(templateName)
{
- // TODO: It would be nice to use the gather rates present in the templates
- // instead of hard-coding the possible rates here.
-
- // We ignore ruins here, as those are not that common and would skew the results
- var types = {
- "food": ["food", "food.fish", "food.fruit", "food.grain", "food.meat", "food.milk"],
- "wood": ["wood", "wood.tree"],
- "stone": ["stone", "stone.rock"],
- "metal": ["metal", "metal.ore"]
- };
- var rates = {};
+ let rates = {};
- for (let type in types)
+ for (let resource of g_ResourceData.GetData())
{
+ let types = [resource.code];
+ for (let subtype of resource.subtypes)
+ // We ignore ruins as those are not that common and skew the results
+ if (subtype !== "ruins")
+ types.push(resource.code + "." + subtype);
+
let count, rate;
- [rate, count] = types[type].reduce(function(sum, t) {
+ [rate, count] = types.reduce(function(sum, t) {
let r = +fetchValue(templateName, "ResourceGatherer/Rates/"+t);
return [sum[0] + (r > 0 ? r : 0), sum[1] + (r > 0 ? 1 : 0)];
}, [0, 0]);
if (rate > 0)
- rates[type] = Math.round(rate / count * 100) / 100;
+ rates[resource.code] = Math.round(rate / count * 100) / 100;
}
if (!Object.keys(rates).length)
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/binaries/data/mods/public/gui/structree/structree.js
+++ b/binaries/data/mods/public/gui/structree/structree.js
@@ -9,6 +9,7 @@ var g_Lists = {};
var g_CivData = {};
var g_SelectedCiv = "";
var g_CallbackSet = false;
+var g_ResourceData = new Resources();
/**
* Initialize the dropdown containing all the available civs
diff --git a/binaries/data/mods/public/gui/summary/counters.js b/binaries/data/mods/public/gui/summary/counters.js
index 562e3bb..1823b67 100644
--- a/binaries/data/mods/public/gui/summary/counters.js
+++ b/binaries/data/mods/public/gui/summary/counters.js
@@ -250,7 +250,7 @@ function calculateUnits(playerState, position)
function calculateResources(playerState, position)
{
- let type = g_ResourcesTypes[position];
+ let type = g_ResourceData.GetCodes()[position];
return formatIncome(
playerState.statistics.resourcesGathered[type],
@@ -262,7 +262,7 @@ function calculateTotalResources(playerState)
let totalGathered = 0;
let totalUsed = 0;
- for (let type of g_ResourcesTypes)
+ for (let type of g_ResourceData.GetCodes())
{
totalGathered += playerState.statistics.resourcesGathered[type];
totalUsed += playerState.statistics.resourcesUsed[type] - playerState.statistics.resourcesSold[type];
@@ -330,7 +330,7 @@ function calculateResourcesTeam(counters)
function calculateResourceExchanged(playerState, position)
{
- let type = g_ResourcesTypes[position];
+ let type = g_ResourceData.GetCodes()[position];
return formatIncome(
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..bd9962c 100644
--- a/binaries/data/mods/public/gui/summary/layout.js
+++ b/binaries/data/mods/public/gui/summary/layout.js
@@ -92,10 +92,6 @@ var g_ScorePanelsData = {
"resources": {
"headings": [
{ "caption": translate("Player name"), "yStart": 26, "width": 200 },
- { "caption": translate("Food"), "yStart": 34, "width": 100 },
- { "caption": translate("Wood"), "yStart": 34, "width": 100 },
- { "caption": translate("Stone"), "yStart": 34, "width": 100 },
- { "caption": translate("Metal"), "yStart": 34, "width": 100 },
{ "caption": translate("Total"), "yStart": 34, "width": 110 },
{
"caption": sprintf(translate("Tributes \n(%(sent)s / %(received)s)"),
@@ -121,10 +117,6 @@ var g_ScorePanelsData = {
}, // width = 510
],
"counters": [
- { "width": 100, "fn": calculateResources, "verticalOffset": 12 },
- { "width": 100, "fn": calculateResources, "verticalOffset": 12 },
- { "width": 100, "fn": calculateResources, "verticalOffset": 12 },
- { "width": 100, "fn": calculateResources, "verticalOffset": 12 },
{ "width": 110, "fn": calculateTotalResources, "verticalOffset": 12 },
{ "width": 121, "fn": calculateTributeSent, "verticalOffset": 12 },
{ "width": 100, "fn": calculateTreasureCollected, "verticalOffset": 12 },
@@ -135,19 +127,11 @@ var g_ScorePanelsData = {
"market": {
"headings": [
{ "caption": translate("Player name"), "yStart": 26, "width": 200 },
- { "caption": translate("Food exchanged"), "yStart": 16, "width": 100 },
- { "caption": translate("Wood exchanged"), "yStart": 16, "width": 100 },
- { "caption": translate("Stone exchanged"), "yStart": 16, "width": 100 },
- { "caption": translate("Metal exchanged"), "yStart": 16, "width": 100 },
{ "caption": translate("Barter efficiency"), "yStart": 16, "width": 100 },
{ "caption": translate("Trade income"), "yStart": 16, "width": 100 }
],
"titleHeadings": [],
"counters": [
- { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 },
- { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 },
- { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 },
- { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 },
{ "width": 100, "fn": calculateBarterEfficiency, "verticalOffset": 12 },
{ "width": 100, "fn": calculateTradeIncome, "verticalOffset": 12 }
],
diff --git a/binaries/data/mods/public/gui/summary/summary.js b/binaries/data/mods/public/gui/summary/summary.js
index 51e5577..f4da805 100644
--- a/binaries/data/mods/public/gui/summary/summary.js
+++ b/binaries/data/mods/public/gui/summary/summary.js
@@ -1,4 +1,4 @@
-const g_MaxHeadingTitle= 8;
+const g_MaxHeadingTitle= 12;
// const for filtering long collective headings
const g_LongHeadingWidth = 250;
@@ -17,7 +17,6 @@ const g_CapturedColor = '[color="255 255 157"]';
const g_BuildingsTypes = [ "total", "House", "Economic", "Outpost", "Military", "Fortress", "CivCentre", "Wonder" ];
const g_UnitsTypes = [ "total", "Infantry", "Worker", "Cavalry", "Champion", "Hero", "Ship", "Trader" ];
-const g_ResourcesTypes = [ "food", "wood", "stone", "metal" ];
// Colors used for gathered and traded resources
const g_IncomeColor = '[color="201 255 200"]';
@@ -34,6 +33,7 @@ var g_PlayerCount = 0;
// Count players without team (or all if teams are not displayed)
var g_WithoutTeam = 0;
var g_GameData;
+var g_ResourceData = new Resources();
function selectPanel(panel)
{
@@ -242,6 +242,46 @@ function init(data)
else
g_Teams = false;
+ // Resource names and counters
+ let resHeads = [];
+ let tradeHeads = [];
+ let resPanel = g_ScorePanelsData.resources;
+ let tradePanel = g_ScorePanelsData.market;
+ let resNames = g_ResourceData.GetNames();
+ let resCodes = g_ResourceData.GetCodes();
+ for (let code of resCodes)
+ {
+ resHeads.push({
+ "caption": translateWithContext("firstWord", resNames[code]),
+ "yStart": 34,
+ "width": 100
+ });
+
+ resPanel.counters.unshift({
+ "width": 100,
+ "fn": calculateResources,
+ "verticalOffset": 12
+ });
+
+ tradeHeads.push({
+ "caption": sprintf(
+ translate("%(resource)s exchanged"), {
+ "resource": translateWithContext("withinSentence", resNames[code])
+ }),
+ "yStart": 16,
+ "width": 100
+ });
+
+ tradePanel.counters.unshift({
+ "width": 100,
+ "fn": calculateResourceExchanged,
+ "verticalOffset": 12
+ });
+ }
+ resPanel.headings.splice.apply(resPanel.headings, [1, 0].concat(resHeads));
+ resPanel.titleHeadings[0].width = (100 * resCodes.length) + 110;
+ tradePanel.headings.splice.apply(tradePanel.headings, [1, 0].concat(tradeHeads));
+
// Erase teams data if teams are not displayed
if (!g_Teams)
{
diff --git a/binaries/data/mods/public/gui/summary/summary.xml b/binaries/data/mods/public/gui/summary/summary.xml
index 2c47f16..015638f 100644
--- a/binaries/data/mods/public/gui/summary/summary.xml
+++ b/binaries/data/mods/public/gui/summary/summary.xml
@@ -103,11 +103,11 @@
Player name
-
+
-
+
@@ -124,7 +124,7 @@
-
+
@@ -132,7 +132,7 @@
-
+
@@ -147,7 +147,7 @@
-
+
diff --git a/binaries/data/mods/public/l10n/messages.json b/binaries/data/mods/public/l10n/messages.json
index 188e3cc..34bd751 100644
--- a/binaries/data/mods/public/l10n/messages.json
+++ b/binaries/data/mods/public/l10n/messages.json
@@ -289,7 +289,7 @@
}
}
},
- {
+ {
"extractor": "json",
"filemasks": [
"gui/credits/texts/**.json"
@@ -562,6 +562,32 @@
"description"
]
}
+ },
+ {
+ "extractor": "json",
+ "filemasks": [
+ "simulation/data/resources/**.json"
+ ],
+ "options": {
+ "keywords": [
+ "name",
+ "subtypes"
+ ],
+ "context": "firstWord"
+ }
+ },
+ {
+ "extractor": "json",
+ "filemasks": [
+ "simulation/data/resources/**.json"
+ ],
+ "options": {
+ "keywords": [
+ "name",
+ "subtypes"
+ ],
+ "context": "withinSentence"
+ }
}
]
},
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/binaries/data/mods/public/simulation/ai/common-api/resources.js
+++ b/binaries/data/mods/public/simulation/ai/common-api/resources.js
@@ -9,7 +9,8 @@ m.Resources = function(amounts = {}, population = 0)
this.population = population > 0 ? population : 0;
};
-m.Resources.prototype.types = []; // This array will be filled in SharedScript.init
+// This array will be filled in SharedScript.init
+m.Resources.prototype.types = [];
m.Resources.prototype.reset = function()
{
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/binaries/data/mods/public/simulation/ai/common-api/shared.js
+++ b/binaries/data/mods/public/simulation/ai/common-api/shared.js
@@ -180,13 +180,10 @@ m.SharedScript.prototype.init = function(state, deserialization)
this.accessibility.init(state, this.terrainAnalyzer);
// Setup resources
- this.resourceTypes = { "food": 0, "wood": 1, "stone": 2, "metal": 2 };
- this.resourceList = [];
- for (let res in this.resourceTypes)
- this.resourceList.push(res);
- m.Resources.prototype.types = this.resourceList;
+ this.resourceInfo = state.resources;
+ m.Resources.prototype.types = state.resources.codes;
// Resource types: 0 = not used for resource maps
- // 1 = abondant resource with small amount each
+ // 1 = abundant resource with small amount each
// 2 = spare resource, but huge amount each
// The following maps are defined in TerrainAnalysis.js and are used for some building placement (cc, dropsites)
// They are updated by checking for create and destroy events for all resources
@@ -197,18 +194,6 @@ m.SharedScript.prototype.init = function(state, deserialization)
this.ccResourceMaps = {}; // Contains maps showing the density of resources, optimized for CC placement.
this.createResourceMaps();
- /** Keep in sync with gui/common/l10n.js */
- this.resourceNames = {
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "food": markForTranslationWithContext("withinSentence", "Food"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "wood": markForTranslationWithContext("withinSentence", "Wood"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "metal": markForTranslationWithContext("withinSentence", "Metal"),
- // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language).
- "stone": markForTranslationWithContext("withinSentence", "Stone"),
- };
-
this.gameState = {};
for (let i in this._players)
{
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..7d79317 100644
--- a/binaries/data/mods/public/simulation/ai/common-api/terrain-analysis.js
+++ b/binaries/data/mods/public/simulation/ai/common-api/terrain-analysis.js
@@ -383,9 +383,9 @@ m.Accessibility.prototype.floodFill = function(startIndex, value, onWater)
/** creates a map of resource density */
m.SharedScript.prototype.createResourceMaps = function()
{
- for (let resource of this.resourceList)
+ for (let resource of this.resourceInfo.codes)
{
- if (this.resourceTypes[resource] !== 1 && this.resourceTypes[resource] !== 2)
+ if (this.resourceInfo.aiInfluenceGroups[resource] === 0)
continue;
// if there is no resourceMap create one with an influence for everything with that resource
if (this.resourceMaps[resource])
@@ -405,11 +405,11 @@ m.SharedScript.prototype.createResourceMaps = function()
let cellSize = this.resourceMaps[resource].cellSize;
let x = Math.floor(ent.position()[0] / cellSize);
let z = Math.floor(ent.position()[1] / cellSize);
- let type = this.resourceTypes[resource];
- let strength = Math.floor(ent.resourceSupplyMax()/this.normalizationFactor[type]);
- this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2, "constant");
- this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2);
- this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[type]/cellSize, strength, "constant");
+ let grp = this.resourceInfo.aiInfluenceGroups[resource];
+ let strength = Math.floor(ent.resourceSupplyMax()/this.normalizationFactor[grp]);
+ this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp]/cellSize, strength/2, "constant");
+ this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp]/cellSize, strength/2);
+ this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[grp]/cellSize, strength, "constant");
}
};
@@ -420,9 +420,9 @@ m.SharedScript.prototype.createResourceMaps = function()
*/
m.SharedScript.prototype.updateResourceMaps = function(events)
{
- for (let resource of this.resourceList)
+ for (let resource of this.resourceInfo.codes)
{
- if (this.resourceTypes[resource] !== 1 && this.resourceTypes[resource] !== 2)
+ if (this.resourceInfo.aiInfluenceGroups[resource] === 0)
continue;
// if there is no resourceMap create one with an influence for everything with that resource
if (this.resourceMaps[resource])
@@ -447,11 +447,11 @@ m.SharedScript.prototype.updateResourceMaps = function(events)
let cellSize = this.resourceMaps[resource].cellSize;
let x = Math.floor(ent.position()[0] / cellSize);
let z = Math.floor(ent.position()[1] / cellSize);
- let type = this.resourceTypes[resource];
- let strength = -Math.floor(ent.resourceSupplyMax()/this.normalizationFactor[type]);
- this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2, "constant");
- this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2);
- this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[type]/cellSize, strength, "constant");
+ let grp = this.resourceInfo.aiInfluenceGroups[resource];
+ let strength = -Math.floor(ent.resourceSupplyMax()/this.normalizationFactor[grp]);
+ this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp]/cellSize, strength/2, "constant");
+ this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp]/cellSize, strength/2);
+ this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[grp]/cellSize, strength, "constant");
}
for (let e of events.Create)
{
@@ -466,11 +466,11 @@ m.SharedScript.prototype.updateResourceMaps = function(events)
let cellSize = this.resourceMaps[resource].cellSize;
let x = Math.floor(ent.position()[0] / cellSize);
let z = Math.floor(ent.position()[1] / cellSize);
- let type = this.resourceTypes[resource];
- let strength = Math.floor(ent.resourceSupplyMax()/this.normalizationFactor[type]);
- this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2, "constant");
- this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[type]/cellSize, strength/2);
- this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[type]/cellSize, strength, "constant");
+ let grp = this.resourceInfo.aiInfluenceGroups[resource];
+ let strength = Math.floor(ent.resourceSupplyMax()/this.normalizationFactor[grp]);
+ this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp]/cellSize, strength/2, "constant");
+ this.resourceMaps[resource].addInfluence(x, z, this.influenceRadius[grp]/cellSize, strength/2);
+ this.ccResourceMaps[resource].addInfluence(x, z, this.ccInfluenceRadius[grp]/cellSize, strength, "constant");
}
};
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/binaries/data/mods/public/simulation/ai/petra/baseManager.js
+++ b/binaries/data/mods/public/simulation/ai/petra/baseManager.js
@@ -55,7 +55,7 @@ m.BaseManager.prototype.init = function(gameState, state)
this.dropsites = {};
this.dropsiteSupplies = {};
this.gatherers = {};
- for (let res of gameState.sharedScript.resourceList)
+ for (let res of gameState.sharedScript.resourceInfo.codes)
{
this.dropsiteSupplies[res] = { "nearby": [], "medium": [], "faraway": [] };
this.gatherers[res] = { "nextCheck": 0, "used": 0, "lost": 0 };
@@ -434,7 +434,7 @@ m.BaseManager.prototype.getResourceLevel = function (gameState, type, nearbyOnly
/** check our resource levels and react accordingly */
m.BaseManager.prototype.checkResourceLevels = function (gameState, queues)
{
- for (let type of gameState.sharedScript.resourceList)
+ for (let type of gameState.sharedScript.resourceInfo.codes)
{
if (type === "food")
{
diff --git a/binaries/data/mods/public/simulation/ai/petra/chatHelper.js b/binaries/data/mods/public/simulation/ai/petra/chatHelper.js
index 60999e8..d50232f 100644
--- a/binaries/data/mods/public/simulation/ai/petra/chatHelper.js
+++ b/binaries/data/mods/public/simulation/ai/petra/chatHelper.js
@@ -91,7 +91,7 @@ m.chatRequestTribute = function(gameState, resource)
"message": message,
"translateMessage": true,
"translateParameters": {"resource": "withinSentence"},
- "parameters": {"resource": gameState.sharedScript.resourceNames[resource]}
+ "parameters": {"resource": gameState.sharedScript.resourceInfo.names[resource]}
});
};
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/binaries/data/mods/public/simulation/ai/petra/config.js
+++ b/binaries/data/mods/public/simulation/ai/petra/config.js
@@ -103,7 +103,26 @@ m.Config = function(difficulty)
"defensive": 0.5
};
- this.resources = ["food", "wood", "stone", "metal"];
+ // See m.QueueManager.prototype.wantedGatherRates()
+ this.queues =
+ {
+ "firstTurn": {
+ "food": 10,
+ "wood": 10,
+ "default": 0
+ },
+ "short": {
+ "food": 200,
+ "wood": 200,
+ "default": 100
+ },
+ "medium": {
+ "default": 0
+ },
+ "long": {
+ "default": 0
+ }
+ };
};
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/binaries/data/mods/public/simulation/ai/petra/headquarters.js
+++ b/binaries/data/mods/public/simulation/ai/petra/headquarters.js
@@ -69,7 +69,7 @@ m.HQ.prototype.init = function(gameState, queues)
this.navalMap = false;
this.navalRegions = {};
- for (let res of gameState.sharedScript.resourceList)
+ for (let res of gameState.sharedScript.resourceInfo.codes)
{
this.wantedRates[res] = 0;
this.currentRates[res] = 0;
@@ -653,7 +653,7 @@ m.HQ.prototype.bulkPickWorkers = function(gameState, baseRef, number)
m.HQ.prototype.getTotalResourceLevel = function(gameState)
{
let total = {};
- for (let res of gameState.sharedScript.resourceList)
+ for (let res of gameState.sharedScript.resourceInfo.codes)
total[res] = 0;
for (let base of this.baseManagers)
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/binaries/data/mods/public/simulation/ai/petra/queueManager.js
+++ b/binaries/data/mods/public/simulation/ai/petra/queueManager.js
@@ -84,8 +84,8 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState)
if (gameState.ai.playedTurn === 0)
{
let ret = {};
- for (let res of gameState.sharedScript.resourceList)
- ret[res] = (res === "food" || res === "wood" ) ? 10 : 0;
+ for (let res of gameState.sharedScript.resourceInfo.codes)
+ ret[res] = this.Config.queues.firstTurn[res] || this.Config.queues.firstTurn.default;
return ret;
}
@@ -97,11 +97,11 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState)
let totalShort = {};
let totalMedium = {};
let totalLong = {};
- for (let res of gameState.sharedScript.resourceList)
+ for (let res of gameState.sharedScript.resourceInfo.codes)
{
- totalShort[res] = (res === "food" || res === "wood" ) ? 200 : 100;
- totalMedium[res] = 0;
- totalLong[res] = 0;
+ totalShort[res] = this.Config.queues.short[res] || this.Config.queues.short.default;
+ totalMedium[res] = this.Config.queues.medium[res] || this.Config.queues.medium.default;
+ totalLong[res] = this.Config.queues.long[res] || this.Config.queues.long.default;
}
let total;
//queueArrays because it's faster.
@@ -133,7 +133,7 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState)
// global rates
let rates = {};
let diff;
- for (let res of gameState.sharedScript.resourceList)
+ for (let res of gameState.sharedScript.resourceInfo.codes)
{
if (current[res] > 0)
{
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/binaries/data/mods/public/simulation/ai/petra/researchManager.js
+++ b/binaries/data/mods/public/simulation/ai/petra/researchManager.js
@@ -100,7 +100,8 @@ m.ResearchManager.prototype.researchWantedTechs = function(gameState, techs)
let cost = template.cost;
let costMax = 0;
for (let res in cost)
- costMax = Math.max(costMax, Math.max(cost[res]-available[res], 0));
+ if (gameState.sharedScript.resourceInfo.codes.indexOf(res))
+ costMax = Math.max(costMax, Math.max(cost[res]-available[res], 0));
if (10*numWorkers < costMax)
continue;
}
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/binaries/data/mods/public/simulation/components/Barter.js
+++ b/binaries/data/mods/public/simulation/components/Barter.js
@@ -1,9 +1,8 @@
-// True price of 100 units of resource (for case if some resource is more worth).
+// The "true price" is a base price of 100 units of resource (for the case of some resources being of more worth than others).
// With current bartering system only relative values makes sense
// so if for example stone is two times more expensive than wood,
// there will 2:1 exchange rate.
-const TRUE_PRICES = { "food": 100, "wood": 100, "stone": 100, "metal": 100 };
-
+//
// Constant part of price difference between true price and buy/sell price.
// In percents.
// Buy price equal to true price plus constant difference.
@@ -21,9 +20,6 @@ const DIFFERENCE_RESTORE = 0.5;
// Interval of timer which slowly restore prices after deals
const RESTORE_TIMER_INTERVAL = 5000;
-// Array of resource names
-const RESOURCES = ["food", "wood", "stone", "metal"];
-
function Barter() {}
Barter.prototype.Schema =
@@ -32,7 +28,7 @@ Barter.prototype.Schema =
Barter.prototype.Init = function()
{
this.priceDifferences = {};
- for (var resource of RESOURCES)
+ for (let resource of Resources.GetCodes())
this.priceDifferences[resource] = 0;
this.restoreTimer = undefined;
};
@@ -40,10 +36,11 @@ Barter.prototype.Init = function()
Barter.prototype.GetPrices = function()
{
var prices = { "buy": {}, "sell": {} };
- for (var resource of RESOURCES)
+ for (let resource of Resources.GetCodes())
{
- prices["buy"][resource] = TRUE_PRICES[resource] * (100 + CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100;
- prices["sell"][resource] = TRUE_PRICES[resource] * (100 - CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100;
+ let truePrice = Resources.GetResource(resource).truePrice;
+ prices.buy[resource] = truePrice * (100 + CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100;
+ prices.sell[resource] = truePrice * (100 - CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100;
}
return prices;
};
@@ -71,12 +68,13 @@ Barter.prototype.ExchangeResources = function(playerEntity, resourceToSell, reso
warn("ExchangeResources: incorrect amount: " + uneval(amount));
return;
}
- if (RESOURCES.indexOf(resourceToSell) == -1)
+ let availResources = Resources.GetCodes();
+ if (availResources.indexOf(resourceToSell) == -1)
{
warn("ExchangeResources: incorrect resource to sell: " + uneval(resourceToSell));
return;
}
- if (RESOURCES.indexOf(resourceToBuy) == -1)
+ if (availResources.indexOf(resourceToBuy) == -1)
{
warn("ExchangeResources: incorrect resource to buy: " + uneval(resourceToBuy));
return;
@@ -123,7 +121,7 @@ Barter.prototype.ExchangeResources = function(playerEntity, resourceToSell, reso
Barter.prototype.ProgressTimeout = function(data)
{
var needRestore = false;
- for (var resource of RESOURCES)
+ for (let resource of Resources.GetCodes())
{
// Calculate value to restore, it should be limited to [-DIFFERENCE_RESTORE; DIFFERENCE_RESTORE] interval
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 b3196a6..bf33b60 100644
--- a/binaries/data/mods/public/simulation/components/Cost.js
+++ b/binaries/data/mods/public/simulation/components/Cost.js
@@ -1,5 +1,7 @@
function Cost() {}
+Cost.prototype.ResourcesSchema = Resources.BuildSchema("nonNegativeDecimal");
+
Cost.prototype.Schema =
"Specifies the construction/training costs of this entity. " +
"" +
@@ -19,16 +21,11 @@ Cost.prototype.Schema =
"" +
" " +
" " +
- "" +
+ "" +
"" +
" " +
- "" +
- "" +
- " " +
- " " +
- " " +
- " " +
- " " +
+ "" +
+ Cost.prototype.ResourcesSchema +
" ";
Cost.prototype.Init = function()
@@ -70,8 +67,16 @@ Cost.prototype.GetResourceCosts = function(owner)
let entityTemplate = cmpTemplateManager.GetTemplate(entityTemplateName);
let costs = {};
- for (let r in this.template.Resources)
- costs[r] = ApplyValueModificationsToTemplate("Cost/Resources/"+r, +this.template.Resources[r], owner, entityTemplate);
+ let resCodes = Resources.GetCodes();
+
+ for (let res in this.template.Resources)
+ {
+ let cost = +this.template.Resources[res];
+ if (resCodes.indexOf(res) < 0)
+ continue;
+ costs[res] = ApplyValueModificationsToTemplate("Cost/Resources/"+res, cost, owner, entityTemplate);
+ }
+
return costs;
};
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
index aaa7855..1b89b40 100644
--- a/binaries/data/mods/public/simulation/components/GuiInterface.js
+++ b/binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -152,6 +152,15 @@ GuiInterface.prototype.GetSimulationState = function()
let cmpBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter);
ret.barterPrices = cmpBarter.GetPrices();
+ // Add Resource Codes, untranslated names and AI Analysis
+ ret.resources = {
+ "codes": Resources.GetCodes(),
+ "names": Resources.GetNames(),
+ "aiInfluenceGroups": {}
+ };
+ for (let res of ret.resources.codes)
+ ret.resources.aiInfluenceGroups[res] = Resources.GetResource(res).aiAnalysisInfluenceGroup || 0;
+
// Add basic statistics to each player
for (let i = 0; i < numPlayers; ++i)
{
@@ -1269,8 +1278,10 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
let result = {
"pieces": [],
- "cost": { "food": 0, "wood": 0, "stone": 0, "metal": 0, "population": 0, "populationBonus": 0, "time": 0 },
+ "cost": { "population": 0, "populationBonus": 0, "time": 0 },
};
+ for (let res of Resources.GetCodes())
+ result.cost[res] = 0;
let previewEntities = [];
if (end.pos)
@@ -1545,13 +1556,9 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
// copied over, so we need to fetch it from the template instead).
// TODO: we should really use a Cost object or at least some utility functions for this, this is mindless
// boilerplate that's probably duplicated in tons of places.
- result.cost.food += tplData.cost.food;
- result.cost.wood += tplData.cost.wood;
- result.cost.stone += tplData.cost.stone;
- result.cost.metal += tplData.cost.metal;
- result.cost.population += tplData.cost.population;
- result.cost.populationBonus += tplData.cost.populationBonus;
- result.cost.time += tplData.cost.time;
+ let entries = Resources.GetCodes().concat("population", "populationBonus", "time");
+ for (let res of entries)
+ result.cost[res] = tplData.cost[res];
}
let canAfford = true;
diff --git a/binaries/data/mods/public/simulation/components/Loot.js b/binaries/data/mods/public/simulation/components/Loot.js
index 3161340..2d0939c 100644
--- a/binaries/data/mods/public/simulation/components/Loot.js
+++ b/binaries/data/mods/public/simulation/components/Loot.js
@@ -1,21 +1,14 @@
function Loot() {}
+Loot.prototype.ResourcesSchema = Resources.BuildSchema("nonNegativeInteger", [ "xp" ]);
+
Loot.prototype.Schema =
- "" +
- " " +
- " " +
- "" +
- " " +
- " " +
- "" +
- " " +
- " " +
- "" +
- " " +
- " " +
- "" +
- " " +
- " ";
+ "Specifies the loot credited when this entity is killed. " +
+ "" +
+ "35 " +
+ "10 " +
+ " " +
+ Loot.prototype.ResourcesSchema;
Loot.prototype.Serialize = null; // we have no dynamic state to save
@@ -26,12 +19,11 @@ Loot.prototype.GetXp = function()
Loot.prototype.GetResources = function()
{
- return {
- "food": +(this.template.food || 0),
- "wood": +(this.template.wood || 0),
- "metal": +(this.template.metal || 0),
- "stone": +(this.template.stone || 0)
- };
+ let ret = {};
+ for (let res of Resources.GetCodes())
+ ret[res] = +(this.template[res] || 0);
+
+ return ret;
};
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 cb137dd..51236ce 100644
--- a/binaries/data/mods/public/simulation/components/Player.js
+++ b/binaries/data/mods/public/simulation/components/Player.js
@@ -18,18 +18,8 @@ Player.prototype.Init = function()
this.popBonuses = 0; // sum of population bonuses of player's entities
this.maxPop = 300; // maximum population
this.trainingBlocked = false; // indicates whether any training queue is currently blocked
- this.resourceCount = {
- "food": 300,
- "wood": 300,
- "metal": 300,
- "stone": 300
- };
- // goods for next trade-route and its proba in % (the sum of probas must be 100)
- this.tradingGoods = [
- { "goods": "wood", "proba": 30 },
- { "goods": "stone", "proba": 35 },
- { "goods": "metal", "proba": 35 },
- ];
+ this.resourceCount = {};
+ this.tradingGoods = []; // goods for next trade-route and its proba in % (the sum of probas must be 100)
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.
this.teamsLocked = false;
this.state = "active"; // game state - one of "active", "defeated", "won"
@@ -44,15 +34,25 @@ Player.prototype.Init = function()
this.cheatsEnabled = false;
this.cheatTimeMultiplier = 1;
this.heroes = [];
- this.resourceNames = {
- "food": markForTranslation("Food"),
- "wood": markForTranslation("Wood"),
- "metal": markForTranslation("Metal"),
- "stone": markForTranslation("Stone"),
- };
+ this.resourceNames = {};
this.disabledTemplates = {};
this.disabledTechnologies = {};
this.startingTechnologies = [];
+
+ // Initial resources and trading goods probability in steps of 5
+ let resCodes = Resources.GetCodes();
+ let quotient = Math.floor(20 / resCodes.length);
+ let remainder = 20 % resCodes.length;
+ for (let i in resCodes)
+ {
+ let res = resCodes[i];
+ this.resourceCount[res] = 300;
+ this.resourceNames[res] = Resources.GetResource(res).name;
+ this.tradingGoods.push({
+ "goods": res,
+ "proba": 5 * (quotient + (+i < remainder ? 1 : 0))
+ });
+ }
};
Player.prototype.SetPlayerID = function(id)
@@ -197,14 +197,9 @@ Player.prototype.UnBlockTraining = function()
Player.prototype.SetResourceCounts = function(resources)
{
- if (resources.food !== undefined)
- this.resourceCount.food = resources.food;
- if (resources.wood !== undefined)
- this.resourceCount.wood = resources.wood;
- if (resources.stone !== undefined)
- this.resourceCount.stone = resources.stone;
- if (resources.metal !== undefined)
- this.resourceCount.metal = resources.metal;
+ for (let res in resources)
+ if (this.resourceCount[res])
+ this.resourceCount[res] = resources[res];
};
Player.prototype.GetResourceCounts = function()
@@ -297,7 +292,8 @@ Player.prototype.SubtractResourcesOrNotify = function(amounts)
// Subtract the resources
for (var type in amounts)
- this.resourceCount[type] -= amounts[type];
+ if (this.resourceCount[type])
+ this.resourceCount[type] -= amounts[type];
return true;
};
@@ -346,7 +342,15 @@ Player.prototype.SetTradingGoods = function(tradingGoods)
if (sumProba != 100) // consistency check
{
error("Player.js SetTradingGoods: " + uneval(tradingGoods));
- tradingGoods = { "food": 20, "wood":20, "stone":30, "metal":30 };
+ let first = true;
+ for (let res of Resources.GetCodes())
+ if (first)
+ {
+ tradingGoods[res] = 100;
+ first = false;
+ }
+ else
+ tradingGoods[res] = 0;
}
this.tradingGoods = [];
diff --git a/binaries/data/mods/public/simulation/components/ProductionQueue.js b/binaries/data/mods/public/simulation/components/ProductionQueue.js
index 6c72202..447b0ff 100644
--- a/binaries/data/mods/public/simulation/components/ProductionQueue.js
+++ b/binaries/data/mods/public/simulation/components/ProductionQueue.js
@@ -3,6 +3,8 @@ const MAX_QUEUE_SIZE = 16;
function ProductionQueue() {}
+ProductionQueue.prototype.ResourceSchema = Resources.BuildSchema("nonNegativeDecimal", [ "time" ]);
+
ProductionQueue.prototype.Schema =
"Allows the building to train new units and research technologies " +
"" +
@@ -31,13 +33,7 @@ ProductionQueue.prototype.Schema =
" " +
"" +
"" +
- "" +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
+ ProductionQueue.prototype.ResourceSchema +
" ";
ProductionQueue.prototype.Init = function()
@@ -260,7 +256,8 @@ ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadat
// TODO: there should probably be a limit on the number of queued batches
// TODO: there should be a way for the GUI to determine whether it's going
// to be possible to add a batch (based on resource costs and length limits)
- var cmpPlayer = QueryOwnerInterface(this.entity);
+ let cmpPlayer = QueryOwnerInterface(this.entity);
+ let resCodes = Resources.GetCodes();
if (this.queue.length < MAX_QUEUE_SIZE)
{
@@ -293,10 +290,13 @@ ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadat
var buildTime = ApplyValueModificationsToTemplate("Cost/BuildTime", +template.Cost.BuildTime, cmpPlayer.GetPlayerID(), template);
var time = timeMult * buildTime;
- for (var r in template.Cost.Resources)
+ for (let res in template.Cost.Resources)
{
- costs[r] = ApplyValueModificationsToTemplate("Cost/Resources/"+r, +template.Cost.Resources[r], cmpPlayer.GetPlayerID(), template);
- totalCosts[r] = Math.floor(count * costs[r]);
+ let cost = +template.Cost.Resources[res];
+ if (resCodes.indexOf(res) < 0)
+ continue;
+ costs[res] = ApplyValueModificationsToTemplate("Cost/Resources/"+res, cost, cmpPlayer.GetPlayerID(), template);
+ totalCosts[res] = Math.floor(count * costs[res]);
}
var population = ApplyValueModificationsToTemplate("Cost/Population", +template.Cost.Population, cmpPlayer.GetPlayerID(), template);
@@ -341,9 +341,13 @@ ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadat
let techCostMultiplier = this.GetTechCostMultiplier();
let time = techCostMultiplier.time * template.researchTime * cmpPlayer.GetCheatTimeMultiplier();
- var cost = {};
+ let cost = {};
for (let res in template.cost)
- cost[res] = Math.floor(techCostMultiplier[res] * template.cost[res]);
+ {
+ if (resCodes.indexOf(res) < 0)
+ continue;
+ cost[res] = Math.floor((techCostMultiplier[res] ? techCostMultiplier[res] : 1) * template.cost[res]);
+ }
// TrySubtractResources should report error to player (they ran out of resources)
if (!cmpPlayer.TrySubtractResources(cost))
@@ -361,7 +365,7 @@ ProductionQueue.prototype.AddBatch = function(templateName, type, count, metadat
"player": cmpPlayer.GetPlayerID(),
"count": 1,
"technologyTemplate": templateName,
- "resources": deepcopy(template.cost), // need to copy to avoid serialization problems
+ "resources": cost,
"productionStarted": false,
"timeTotal": time*1000,
"timeRemaining": time*1000,
@@ -433,8 +437,10 @@ ProductionQueue.prototype.RemoveBatch = function(id)
// Refund the resource cost for this batch
var totalCosts = {};
var cmpStatisticsTracker = QueryPlayerIDInterface(item.player, IID_StatisticsTracker);
- for each (var r in ["food", "wood", "stone", "metal"])
+ for (let r of Resources.GetCodes())
{
+ if (!item.resources[r])
+ continue;
totalCosts[r] = Math.floor(item.count * item.resources[r]);
if (cmpStatisticsTracker)
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..a14ddaf 100644
--- a/binaries/data/mods/public/simulation/components/ResourceDropsite.js
+++ b/binaries/data/mods/public/simulation/components/ResourceDropsite.js
@@ -1,15 +1,12 @@
function ResourceDropsite() {}
+ResourceDropsite.prototype.ResourceChoiceSchema = Resources.BuildChoicesSchema();
+
ResourceDropsite.prototype.Schema =
"" +
"" +
"" +
- "" +
- "food " +
- "wood " +
- "stone " +
- "metal " +
- " " +
+ ResourceDropsite.prototype.ResourceChoiceSchema +
" " +
"
" +
" " +
@@ -24,12 +21,14 @@ ResourceDropsite.prototype.Init = function()
};
/**
- * Returns the list of resource types accepted by this dropsite.
+ * Returns the list of resource types accepted by this dropsite,
+ * as defined by it being referred to in the template and the resource being enabled.
*/
ResourceDropsite.prototype.GetTypes = function()
{
let types = ApplyValueModificationsToEntity("ResourceDropsite/Types", this.template.Types, this.entity);
- return types ? types.split(/\s+/) : [];
+ let resources = Resources.GetCodes();
+ return types.split(/\s+/).filter(type => resources.indexOf(type.toLowerCase()) > -1);
};
/**
diff --git a/binaries/data/mods/public/simulation/components/ResourceGatherer.js b/binaries/data/mods/public/simulation/components/ResourceGatherer.js
index acd5fbd..52f0b44 100644
--- a/binaries/data/mods/public/simulation/components/ResourceGatherer.js
+++ b/binaries/data/mods/public/simulation/components/ResourceGatherer.js
@@ -1,5 +1,8 @@
function ResourceGatherer() {}
+ResourceGatherer.prototype.ResourcesSchema = Resources.BuildSchema("positiveDecimal", [ "treasure" ], true);
+ResourceGatherer.prototype.CapacitiesSchema = Resources.BuildSchema("positiveDecimal");
+
ResourceGatherer.prototype.Schema =
"Lets the unit gather resources from entities that have the ResourceSupply component. " +
"" +
@@ -25,35 +28,10 @@ ResourceGatherer.prototype.Schema =
"" +
" " +
"" +
- "" +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
- " " +
+ ResourceGatherer.prototype.ResourcesSchema +
" " +
"" +
- "" +
- " " +
- " " +
- " " +
- " " +
- " " +
+ ResourceGatherer.prototype.CapacitiesSchema +
" ";
ResourceGatherer.prototype.Init = function()
@@ -137,6 +115,12 @@ ResourceGatherer.prototype.RecalculateGatherRatesAndCapacities = function()
this.rates = {};
for (let r in this.template.Rates)
{
+ let type = r.split(".");
+ let res = Resources.GetResource(type[0]);
+
+ if (!res && type[0] !== "treasure" || (type.length > 1 && res.subtypes.indexOf(type[1]) < 0))
+ continue;
+
let rate = ApplyValueModificationsToEntity("ResourceGatherer/Rates/" + r, +this.template.Rates[r], this.entity);
this.rates[r] = rate * this.baseSpeed;
}
@@ -174,7 +158,7 @@ ResourceGatherer.prototype.GetRange = function()
/**
* Try to gather treasure
- * @return 'true' if treasure is successfully gathered and 'false' in the other case
+ * @return 'true' if treasure is successfully gathered and 'false' if not
*/
ResourceGatherer.prototype.TryInstantGather = function(target)
{
diff --git a/binaries/data/mods/public/simulation/components/ResourceSupply.js b/binaries/data/mods/public/simulation/components/ResourceSupply.js
index 04e95da..7cc580b 100644
--- a/binaries/data/mods/public/simulation/components/ResourceSupply.js
+++ b/binaries/data/mods/public/simulation/components/ResourceSupply.js
@@ -1,5 +1,7 @@
function ResourceSupply() {}
+ResourceSupply.prototype.ResourceChoiceSchema = Resources.BuildChoicesSchema(true, true);
+
ResourceSupply.prototype.Schema =
"Provides a supply of one particular type of resource. " +
"" +
@@ -12,23 +14,8 @@ ResourceSupply.prototype.Schema =
"" +
"Infinity " +
" " +
- "" +
- "" +
- "wood.tree " +
- "wood.ruins " +
- "stone.rock " +
- "stone.ruins " +
- "metal.ore " +
- "food.fish " +
- "food.fruit " +
- "food.grain " +
- "food.meat " +
- "food.milk " +
- "treasure.wood " +
- "treasure.stone " +
- "treasure.metal " +
- "treasure.food " +
- " " +
+ "" +
+ ResourceSupply.prototype.ResourceChoiceSchema +
" " +
"" +
" " +
@@ -45,16 +32,23 @@ ResourceSupply.prototype.Init = function()
this.amount = this.GetMaxAmount();
this.gatherers = []; // list of IDs for each players
- var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); // system component so that's safe.
- var numPlayers = cmpPlayerManager.GetNumPlayers();
- for (var i = 0; i <= numPlayers; ++i) // use "<=" because we want Gaia too.
+ let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); // system component so that's safe.
+ let numPlayers = cmpPlayerManager.GetNumPlayers();
+ for (let i = 0; i <= numPlayers; ++i) // use "<=" because we want Gaia too.
this.gatherers.push([]);
this.infinite = !isFinite(+this.template.Amount);
- [this.type,this.subType] = this.template.Type.split('.');
- this.cachedType = { "generic" : this.type, "specific" : this.subType };
+ [this.type, this.subtype] = this.template.Type.split('.');
+ let resData = Resources.GetResource(this.type);
+ if (this.type === "treasure")
+ resData = { "subtypes": Resources.GetCodes() };
+
+ // Remove entity from gameworld if the resource supplied by this entity is disabled or not valid.
+ if (!resData || resData.subtypes.indexOf(this.subtype) === -1)
+ Engine.DestroyEntity(this.entity);
+ this.cachedType = { "generic" : this.type, "specific" : this.subtype };
};
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..7bed918 100644
--- a/binaries/data/mods/public/simulation/components/ResourceTrickle.js
+++ b/binaries/data/mods/public/simulation/components/ResourceTrickle.js
@@ -1,30 +1,11 @@
function ResourceTrickle() {}
+ResourceTrickle.prototype.ResourcesSchema = Resources.BuildSchema("nonNegativeDecimal");
+
ResourceTrickle.prototype.Schema =
"Controls the resource trickle ability of the unit. " +
"" +
- "" +
- "" +
- "" +
- "" +
- " " +
- " " +
- "" +
- "" +
- "" +
- " " +
- " " +
- "" +
- "" +
- "" +
- " " +
- " " +
- "" +
- "" +
- "" +
- " " +
- " " +
- " " +
+ ResourceTrickle.prototype.ResourcesSchema +
" " +
"" +
"" +
@@ -45,9 +26,14 @@ ResourceTrickle.prototype.GetTimer = function()
ResourceTrickle.prototype.GetRates = function()
{
- var rates = {};
- for (var resource in this.template.Rates)
+ let rates = {};
+ let resCodes = Resources.GetCodes();
+ for (let resource in this.template.Rates)
+ {
+ if (resCodes.indexOf(resource) < 0)
+ continue;
rates[resource] = ApplyValueModificationsToEntity("ResourceTrickle/Rates/"+resource, +this.template.Rates[resource], this.entity);
+ }
return rates;
};
@@ -55,13 +41,9 @@ ResourceTrickle.prototype.GetRates = function()
// Do the actual work here
ResourceTrickle.prototype.Trickle = function(data, lateness)
{
- var cmpPlayer = QueryOwnerInterface(this.entity);
- if (!cmpPlayer)
- return;
-
- var rates = this.GetRates();
- for (var resource in rates)
- cmpPlayer.AddResource(resource, rates[resource]);
+ let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
+ if (cmpPlayer)
+ cmpPlayer.AddResources(this.GetRates());
};
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/binaries/data/mods/public/simulation/components/StatisticsTracker.js
+++ b/binaries/data/mods/public/simulation/components/StatisticsTracker.js
@@ -105,30 +105,18 @@ StatisticsTracker.prototype.Init = function()
this.buildingsCapturedValue = 0;
this.resourcesGathered = {
- "food": 0,
- "wood": 0,
- "metal": 0,
- "stone": 0,
"vegetarianFood": 0
};
- this.resourcesUsed = {
- "food": 0,
- "wood": 0,
- "metal": 0,
- "stone": 0
- };
- this.resourcesSold = {
- "food": 0,
- "wood": 0,
- "metal": 0,
- "stone": 0
- };
- this.resourcesBought = {
- "food": 0,
- "wood": 0,
- "metal": 0,
- "stone": 0
- };
+ this.resourcesUsed = {};
+ this.resourcesSold = {};
+ this.resourcesBought = {};
+ for (let res of Resources.GetCodes())
+ {
+ this.resourcesGathered[res] = 0;
+ this.resourcesUsed[res] = 0;
+ this.resourcesSold[res] = 0;
+ this.resourcesBought[res] = 0;
+ }
this.tributesSent = 0;
this.tributesReceived = 0;
@@ -347,7 +335,8 @@ StatisticsTracker.prototype.IncreaseResourceGatheredCounter = function(type, amo
*/
StatisticsTracker.prototype.IncreaseResourceUsedCounter = function(type, amount)
{
- this.resourcesUsed[type] += amount;
+ if (typeof this.resourcesUsed[type] === "number")
+ this.resourcesUsed[type] += amount;
};
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/binaries/data/mods/public/simulation/components/Trader.js
+++ b/binaries/data/mods/public/simulation/components/Trader.js
@@ -4,9 +4,6 @@
// Additional gain for ships for each garrisoned trader, in percents
const GARRISONED_TRADER_ADDITION = 20;
-// Array of resource names
-const RESOURCES = ["food", "wood", "stone", "metal"];
-
function Trader() {}
Trader.prototype.Schema =
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..7e7f2d2 100644
--- a/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
+++ b/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
@@ -33,6 +33,12 @@ Engine.LoadComponentScript("interfaces/Upgrade.js");
Engine.LoadComponentScript("interfaces/BuildingAI.js");
Engine.LoadComponentScript("GuiInterface.js");
+Resources = {
+ "GetCodes": function() { return [ "food", "metal", "stone", "wood" ] },
+ "GetNames": function() { return { "food": "Food", "metal": "Metal", "stone": "Stone", "wood": "Wood" } },
+ "GetResource": function() { return {}; },
+};
+
var cmp = ConstructComponent(SYSTEM_ENTITY, "GuiInterface");
@@ -330,7 +336,22 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
circularMap: false,
timeElapsed: 0,
gameType: "conquest",
- barterPrices: {buy: {food: 150}, sell: {food: 25}}
+ barterPrices: {buy: {food: 150}, sell: {food: 25}},
+ resources: {
+ codes: [ "food", "metal", "stone", "wood" ],
+ names: {
+ "food": "Food",
+ "metal": "Metal",
+ "stone": "Stone",
+ "wood": "Wood",
+ },
+ aiInfluenceGroups: {
+ "food": 0,
+ "metal": 0,
+ "stone": 0,
+ "wood": 0,
+ }
+ },
});
TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
@@ -449,7 +470,22 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
circularMap: false,
timeElapsed: 0,
gameType: "conquest",
- barterPrices: {buy: {food: 150}, sell: {food: 25}}
+ barterPrices: {buy: {food: 150}, sell: {food: 25}},
+ resources: {
+ codes: [ "food", "metal", "stone", "wood" ],
+ names: {
+ "food": "Food",
+ "metal": "Metal",
+ "stone": "Stone",
+ "wood": "Wood",
+ },
+ aiInfluenceGroups: {
+ "food": 0,
+ "metal": 0,
+ "stone": 0,
+ "wood": 0,
+ }
+ },
});
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 4191a68..7cba52a 100644
--- a/binaries/data/mods/public/simulation/components/tests/test_Player.js
+++ b/binaries/data/mods/public/simulation/components/tests/test_Player.js
@@ -9,6 +9,11 @@ Engine.LoadComponentScript("Timer.js")
ConstructComponent(SYSTEM_ENTITY, "EndGameManager");
ConstructComponent(SYSTEM_ENTITY, "Timer");
+Resources = {
+ "GetCodes": function() { return [ "food", "metal", "stone", "wood" ] },
+ "GetResource": function() { return {}; },
+};
+
var cmpPlayer = ConstructComponent(10, "Player");
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..67c85e7
--- /dev/null
+++ b/binaries/data/mods/public/simulation/data/resources/food.json
@@ -0,0 +1,14 @@
+{
+ "code": "food",
+ "name": "Food",
+ "subtypes": {
+ "fish": "Fish",
+ "fruit": "Fruit",
+ "grain": "Grain",
+ "meat": "Meat",
+ "milk": "Milk"
+ },
+ "truePrice": 100,
+ "aiAnalysisInfluenceGroup": 0,
+ "enabled": true
+}
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..2a8b590
--- /dev/null
+++ b/binaries/data/mods/public/simulation/data/resources/metal.json
@@ -0,0 +1,10 @@
+{
+ "code": "metal",
+ "name": "Metal",
+ "subtypes": {
+ "ore": "Ore"
+ },
+ "truePrice": 100,
+ "aiAnalysisInfluenceGroup": 2,
+ "enabled": true
+}
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..034783e
--- /dev/null
+++ b/binaries/data/mods/public/simulation/data/resources/stone.json
@@ -0,0 +1,11 @@
+{
+ "code": "stone",
+ "name": "Stone",
+ "subtypes": {
+ "rock": "Rock",
+ "ruins": "Ruins"
+ },
+ "truePrice": 100,
+ "aiAnalysisInfluenceGroup": 2,
+ "enabled": true
+}
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..9e5a904
--- /dev/null
+++ b/binaries/data/mods/public/simulation/data/resources/wood.json
@@ -0,0 +1,11 @@
+{
+ "code": "wood",
+ "name": "Wood",
+ "subtypes": {
+ "tree": "Tree",
+ "ruins": "Ruins"
+ },
+ "truePrice": 100,
+ "aiAnalysisInfluenceGroup": 1,
+ "enabled": true
+}
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..af6f747
--- /dev/null
+++ b/binaries/data/mods/public/simulation/helpers/Resources.js
@@ -0,0 +1,2 @@
+
+Resources = new Resources();