diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg
index 7d11ca9afb..83770fc98f 100644
--- a/binaries/data/config/default.cfg
+++ b/binaries/data/config/default.cfg
@@ -179,6 +179,7 @@ hotkey.session.kill = Delete ; Destroy selected units
hotkey.session.garrison = Ctrl ; Modifier to garrison when clicking on building
hotkey.session.queue = Shift ; Modifier to queue unit orders instead of replacing
hotkey.session.batchtrain = Shift ; Modifier to train units in batches
+hotkey.session.massbarter = Shift ; Modifier to barter bunch of resources
hotkey.session.deselectgroup = Ctrl ; Modifier to deselect units when clicking group icon, instead of selecting
hotkey.session.rotate.cw = RightBracket ; Rotate building placement preview clockwise
hotkey.session.rotate.ccw = LeftBracket ; Rotate building placement preview anticlockwise
diff --git a/binaries/data/mods/public/art/textures/ui/session/resources/food_inactive.png b/binaries/data/mods/public/art/textures/ui/session/resources/food_inactive.png
new file mode 100644
index 0000000000..aa6adbd8bf
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/resources/food_inactive.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a1740836d5cff952e983725dc598ffacb6ff7a675fd6fb0eea16f4de42301d87
+size 8058
diff --git a/binaries/data/mods/public/art/textures/ui/session/resources/food_selected.png b/binaries/data/mods/public/art/textures/ui/session/resources/food_selected.png
new file mode 100644
index 0000000000..d5d8fcfe28
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/resources/food_selected.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4ba89588689c34ed997e33d54fc81438a3c96e3bbddd0528fa42392249cd5d0a
+size 12337
diff --git a/binaries/data/mods/public/art/textures/ui/session/resources/metal_inactive.png b/binaries/data/mods/public/art/textures/ui/session/resources/metal_inactive.png
new file mode 100644
index 0000000000..981cb90e08
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/resources/metal_inactive.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9a71ea32e213cd8a176a4c1d8b1d1ec82c29ffcf05f7ef58dcd04dfd02749f30
+size 8170
diff --git a/binaries/data/mods/public/art/textures/ui/session/resources/metal_selected.png b/binaries/data/mods/public/art/textures/ui/session/resources/metal_selected.png
new file mode 100644
index 0000000000..9882135dce
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/resources/metal_selected.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c95c090f576894162665dd8890b9d6c90649f2cfb016e693aaa118b924728247
+size 11839
diff --git a/binaries/data/mods/public/art/textures/ui/session/resources/stone_inactive.png b/binaries/data/mods/public/art/textures/ui/session/resources/stone_inactive.png
new file mode 100644
index 0000000000..f273569ef6
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/resources/stone_inactive.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a1039395a9129f05fdf8da7ec85222f5f440a8a70aca3bd1c3e507ee538fedb4
+size 7976
diff --git a/binaries/data/mods/public/art/textures/ui/session/resources/stone_selected.png b/binaries/data/mods/public/art/textures/ui/session/resources/stone_selected.png
new file mode 100644
index 0000000000..1004db214e
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/resources/stone_selected.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fe94df8df4ead787a4f4141489adec6cfc847a7213964ce2a350cfd9fa58a894
+size 11973
diff --git a/binaries/data/mods/public/art/textures/ui/session/resources/wood_inactive.png b/binaries/data/mods/public/art/textures/ui/session/resources/wood_inactive.png
new file mode 100644
index 0000000000..5890579c77
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/resources/wood_inactive.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e43be7f72e0a7d162d463d97d62d1f5c4cd4419dfe85198feb7889e976ae6a8f
+size 9654
diff --git a/binaries/data/mods/public/art/textures/ui/session/resources/wood_selected.png b/binaries/data/mods/public/art/textures/ui/session/resources/wood_selected.png
new file mode 100644
index 0000000000..9f980d32de
--- /dev/null
+++ b/binaries/data/mods/public/art/textures/ui/session/resources/wood_selected.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:da6b1c590428dca431ec028ac3855080b092439553c1dbce4d2f92e2ba0b2702
+size 14070
diff --git a/binaries/data/mods/public/gui/session/input.js b/binaries/data/mods/public/gui/session/input.js
index b8c939574f..9a7068f601 100644
--- a/binaries/data/mods/public/gui/session/input.js
+++ b/binaries/data/mods/public/gui/session/input.js
@@ -1008,6 +1008,13 @@ function startBuildingPlacement(buildEntType)
inputState = INPUT_BUILDING_PLACEMENT;
}
+// Called by GUI when user clicks exchange resources button
+function exchangeResources(command)
+{
+ Engine.PostNetworkCommand({"type": "barter", "sell": command.sell, "buy": command.buy, "amount": command.amount});
+}
+
+
// Batch training:
// When the user shift-clicks, we set these variables and switch to INPUT_BATCHTRAINING
// When the user releases shift, or clicks on a different training button, we create the batched units
diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml
index 5746bf2c8b..780c512efc 100644
--- a/binaries/data/mods/public/gui/session/session.xml
+++ b/binaries/data/mods/public/gui/session/session.xml
@@ -524,6 +524,31 @@
+
+
";
+
+Barter.prototype.Init = function()
+{
+ this.priceDifferences = {};
+ for each (var resource in RESOURCES)
+ this.priceDifferences[resource] = 0;
+ this.restoreTimer = undefined;
+};
+
+Barter.prototype.GetPrices = function()
+{
+ var prices = { "buy": {}, "sell": {} };
+ for each (var resource in RESOURCES)
+ {
+ 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;
+ }
+ return prices;
+};
+
+Barter.prototype.PlayerHasMarket = function(playerEntity)
+{
+ var cmpPlayer = Engine.QueryInterface(playerEntity, IID_Player);
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ var entities = cmpRangeManager.GetEntitiesByPlayer(cmpPlayer.GetPlayerID());
+ for each (var entity in entities)
+ {
+ var cmpFoundation = Engine.QueryInterface(entity, IID_Foundation);
+ var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
+ if (!cmpFoundation && cmpIdentity.HasClass("BarterMarket"))
+ return true;
+ }
+ return false;
+}
+
+Barter.prototype.ExchangeResources = function(playerEntity, resourceToSell, resourceToBuy, amount)
+{
+ // Data verification
+ if (amount <= 0)
+ {
+ warn("ExchangeResources: incorrect amount: " + uneval(amount));
+ return;
+ }
+ if (RESOURCES.indexOf(resourceToSell) == -1)
+ {
+ warn("ExchangeResources: incorrect resource to sell: " + uneval(resourceToSell));
+ return;
+ }
+ if (RESOURCES.indexOf(resourceToBuy) == -1)
+ {
+ warn("ExchangeResources: incorrect resource to buy: " + uneval(resourceToBuy));
+ return;
+ }
+ if (!this.PlayerHasMarket(playerEntity))
+ {
+ warn("ExchangeResources: player has no markets");
+ return;
+ }
+
+ var cmpPlayer = Engine.QueryInterface(playerEntity, IID_Player);
+ var prices = this.GetPrices();
+ var amountsToSubtract = {};
+ amountsToSubtract[resourceToSell] = amount;
+ if (cmpPlayer.TrySubtractResources(amountsToSubtract))
+ {
+ var amountToAdd = Math.round(prices["sell"][resourceToSell] / prices["buy"][resourceToBuy] * amount);
+ cmpPlayer.AddResource(resourceToBuy, amountToAdd);
+ var numberOfDeals = Math.round(amount / 100);
+
+ // Increase price difference for both exchange resources.
+ // Overal price difference (constant + dynamic) can't exceed +-99%
+ // so both buy/sell prices limited to [1%; 199%] interval.
+ this.priceDifferences[resourceToSell] -= DIFFERENCE_PER_DEAL * numberOfDeals;
+ this.priceDifferences[resourceToSell] = Math.min(99-CONSTANT_DIFFERENCE, Math.max(CONSTANT_DIFFERENCE-99, this.priceDifferences[resourceToSell]));
+ this.priceDifferences[resourceToBuy] += DIFFERENCE_PER_DEAL * numberOfDeals;
+ this.priceDifferences[resourceToBuy] = Math.min(99-CONSTANT_DIFFERENCE, Math.max(CONSTANT_DIFFERENCE-99, this.priceDifferences[resourceToBuy]));
+ }
+
+ if (this.restoreTimer == undefined)
+ {
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ this.restoreTimer = cmpTimer.SetInterval(this.entity, IID_Barter, "ProgressTimeout", RESTORE_TIMER_INTERVAL, RESTORE_TIMER_INTERVAL, {});
+ }
+};
+
+Barter.prototype.ProgressTimeout = function(data)
+{
+ var needRestore = false;
+ for each (var resource in RESOURCES)
+ {
+ // 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]));
+ differenceRestore = -differenceRestore;
+ this.priceDifferences[resource] += differenceRestore;
+ // If price difference still exists then set flag to run timer again
+ if (this.priceDifferences[resource] != 0)
+ needRestore = true;
+ }
+
+ if (!needRestore)
+ {
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.CancelTimer(this.restoreTimer);
+ this.restoreTimer = undefined;
+ }
+}
+
+Engine.RegisterComponentType(IID_Barter, "Barter", Barter);
+
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
index 81a328e646..525353638f 100644
--- a/binaries/data/mods/public/simulation/components/GuiInterface.js
+++ b/binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -254,6 +254,12 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
};
}
+ if (!cmpFoundation && cmpIdentity.HasClass("BarterMarket"))
+ {
+ var cmpBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter);
+ ret.barterMarket = { "prices": cmpBarter.GetPrices() };
+ }
+
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
ret.visibility = cmpRangeManager.GetLosVisibility(ent, player, false);
diff --git a/binaries/data/mods/public/simulation/components/Identity.js b/binaries/data/mods/public/simulation/components/Identity.js
index 1fada40684..54341e21ea 100644
--- a/binaries/data/mods/public/simulation/components/Identity.js
+++ b/binaries/data/mods/public/simulation/components/Identity.js
@@ -82,6 +82,7 @@ Identity.prototype.Schema =
"CivCentre" +
"Economic" +
"Defensive" +
+ "BarterMarket" +
"Village" +
"Town" +
"City" +
diff --git a/binaries/data/mods/public/simulation/components/interfaces/Barter.js b/binaries/data/mods/public/simulation/components/interfaces/Barter.js
new file mode 100644
index 0000000000..057359cce6
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/interfaces/Barter.js
@@ -0,0 +1 @@
+Engine.RegisterInterface("Barter");
diff --git a/binaries/data/mods/public/simulation/helpers/Commands.js b/binaries/data/mods/public/simulation/helpers/Commands.js
index 75de58e168..2f630df7c0 100644
--- a/binaries/data/mods/public/simulation/helpers/Commands.js
+++ b/binaries/data/mods/public/simulation/helpers/Commands.js
@@ -394,6 +394,11 @@ function ProcessCommand(player, cmd)
}
break;
+ case "barter":
+ var cmpBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter);
+ cmpBarter.ExchangeResources(playerEnt, cmd.sell, cmd.buy, cmd.amount);
+ break;
+
default:
error("Invalid command: unknown command type: "+uneval(cmd));
}
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml b/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
index 5037edf887..126bf488ee 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
@@ -23,8 +23,8 @@
Market
- Create Trade units and Barter resources. (Currently a useless structure)
- Town
+ Create Trade units and Barter resources.
+ Town BarterMarket
structures/market.png
diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp
index 4aca8fa4f2..acad2019c4 100644
--- a/source/simulation2/Simulation2.cpp
+++ b/source/simulation2/Simulation2.cpp
@@ -120,6 +120,7 @@ public:
componentManager.AddComponent(SYSTEM_ENTITY, cid, noParam)
LOAD_SCRIPTED_COMPONENT("AIInterface");
+ LOAD_SCRIPTED_COMPONENT("Barter");
LOAD_SCRIPTED_COMPONENT("EndGameManager");
LOAD_SCRIPTED_COMPONENT("GuiInterface");
LOAD_SCRIPTED_COMPONENT("PlayerManager");