Index: source/simulation2/Simulation2.cpp
===================================================================
--- source/simulation2/Simulation2.cpp	(revision 10281)
+++ source/simulation2/Simulation2.cpp	(working copy)
@@ -101,6 +101,7 @@
 				LOGERROR(L"Can't find component type " L##name); \
 			m_ComponentManager.AddComponent(SYSTEM_ENTITY, cid, noParam)
 
+			LOAD_SCRIPTED_COMPONENT("GlobalBarter");
 			LOAD_SCRIPTED_COMPONENT("AIInterface");
 			LOAD_SCRIPTED_COMPONENT("EndGameManager");
 			LOAD_SCRIPTED_COMPONENT("GuiInterface");
Index: binaries/data/mods/public/gui/session/session.xml
===================================================================
--- binaries/data/mods/public/gui/session/session.xml	(revision 10281)
+++ binaries/data/mods/public/gui/session/session.xml	(working copy)
@@ -493,6 +493,21 @@
 					</object>
 				</object>
 
+				<object name="unitBarterPanel"
+					size="3 6 100% 100%"
+				>
+					<object name="unitBarterBuy" ghost="true" style="resourceText" type="text" size="0 0 46 46">Buy:</object>
+					<object name="unitBarterSell" ghost="true" style="resourceText" type="text" size="0 46 46 92">Sell:</object>
+					<object size="46 0 100% 100%">
+						<repeat count="6">
+							<object name="unitBarterButton[n]" hidden="true" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottomBold" z="100">
+								<object name="unitBarterIcon[n]" type="image" ghost="true" size="3 3 43 43"/>
+								<object name="unitBarterPrice[n]" ghost="true" style="resourceText" type="text" size="0 0 100% 50%"/>
+							</object>
+						</repeat>
+					</object>
+				</object>
+
 				<!-- Stance Selection -->
 				<object name="unitStancePanel"
 					style="TranslucentPanel"
Index: binaries/data/mods/public/gui/session/input.js
===================================================================
--- binaries/data/mods/public/gui/session/input.js	(revision 10281)
+++ binaries/data/mods/public/gui/session/input.js	(working copy)
@@ -1008,6 +1008,13 @@
 	inputState = INPUT_BUILDING_PLACEMENT;
 }
 
+// Called by GUI when user clicks exchange resources button
+function exchangeResources(command)
+{
+	Engine.PostNetworkCommand({"type": "barter", "action": command.action, "resource": command.resource});
+}
+
+
 // 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
Index: binaries/data/mods/public/gui/session/unit_commands.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_commands.js	(revision 10281)
+++ binaries/data/mods/public/gui/session/unit_commands.js	(working copy)
@@ -14,10 +14,10 @@
 const UNIT_PANEL_HEIGHT = 44; // QUEUE: The height needed for a row of buttons
 
 // The number of currently visible buttons (used to optimise showing/hiding)
-var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Construction": 0, "Command": 0, "Stance": 0};
+var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Barter": 0, "Training": 0, "Construction": 0, "Command": 0, "Stance": 0};
 
 // Unit panels are panels with row(s) of buttons
-var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Construction", "Research", "Stance", "Command"];
+var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Barter", "Training", "Construction", "Research", "Stance", "Command"];
 
 // Lay out a row of centered buttons (does not work inside a loop like the other function)
 function layoutButtonRowCentered(rowNumber, guiName, startIndex, endIndex, width)
@@ -323,6 +323,29 @@
 	else if (guiName == "Formation" || guiName == "Garrison" || guiName == "Command")
 		rowLength = 4;
 
+	if (guiName == "Barter")
+	{
+		numButtons = 6;
+		rowLength = 3;
+
+		for (var i = 0; i < numButtons; i++)
+		{
+			var action = ["buy", "sell"][Math.floor(i/3)];
+			var resource = ["food", "wood", "stone"][i%3];
+
+			var button = getGUIObjectByName("unitBarterButton["+i+"]");
+			button.hidden = false;
+			button.tooltip = action + " 100 " + resource + " for " + unitEntState.barterPrices[action][resource] + " metal";
+			var command = { "action": action, "resource": resource };
+			button.onpress = (function(e) { return function() { exchangeResources(e) } })(command);
+		
+			var icon = getGUIObjectByName("unitBarterIcon["+i+"]");
+			icon.sprite = "stretched:session/resources/" + resource + ".png";
+
+			getGUIObjectByName("unitBarterPrice["+i+"]").caption = unitEntState.barterPrices[action][resource];
+		}
+	}
+
 	var numRows = Math.ceil(numButtons / rowLength);
 	var buttonSideLength = getGUIObjectByName("unit"+guiName+"Button[0]").size.bottom;
 	var buttonSpacer = buttonSideLength+1;
@@ -410,6 +433,11 @@
 				function (item) { performStance(entState.id, item); } );
 		}
 
+		if (entState.barterMarket)
+		{
+			setupUnitPanel("Barter", usedPanels, entState, [], null );
+		}
+
 		if (entState.buildEntities && entState.buildEntities.length)
 		{
 			setupUnitPanel("Construction", usedPanels, entState, entState.buildEntities, startBuildingPlacement);
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js	(revision 10281)
+++ binaries/data/mods/public/simulation/helpers/Commands.js	(working copy)
@@ -319,6 +319,11 @@
 		}
 		break;
 
+	case "barter":
+		var cmpGlobalBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_GlobalBarter);
+		cmpGlobalBarter.ExchangeResources(playerEnt, cmd.action, cmd.resource);
+		break;
+
 	default:
 		error("Ignoring unrecognised command type '" + cmd.type + "'");
 	}
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js	(revision 10281)
+++ binaries/data/mods/public/simulation/components/GuiInterface.js	(working copy)
@@ -135,6 +135,13 @@
 			"classes": cmpIdentity.GetClassesList(),
 			"selectionGroupName": cmpIdentity.GetSelectionGroupName()
 		};
+
+		if (cmpIdentity.GetClassesList().indexOf("BarterMarket") != -1)
+		{
+			ret.barterMarket = true;
+			var cmpGlobalBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_GlobalBarter);
+			ret.barterPrices = cmpGlobalBarter.GetPrices();
+		}
 	}
 	
 	var cmpPosition = Engine.QueryInterface(ent, IID_Position);
Index: binaries/data/mods/public/simulation/components/interfaces/GlobalBarter.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/GlobalBarter.js	(revision 0)
+++ binaries/data/mods/public/simulation/components/interfaces/GlobalBarter.js	(revision 0)
@@ -0,0 +1 @@
+Engine.RegisterInterface("GlobalBarter");
Index: binaries/data/mods/public/simulation/components/Identity.js
===================================================================
--- binaries/data/mods/public/simulation/components/Identity.js	(revision 10281)
+++ binaries/data/mods/public/simulation/components/Identity.js	(working copy)
@@ -81,6 +81,7 @@
 						"<value>Civic</value>" +
 						"<value>CivCentre</value>" +
 						"<value>Economic</value>" +
+						"<value>BarterMarket</value>" +
 						"<value>Defensive</value>" +
 						"<value>Village</value>" +
 						"<value>Town</value>" +
Index: binaries/data/mods/public/simulation/components/GlobalBarter.js
===================================================================
--- binaries/data/mods/public/simulation/components/GlobalBarter.js	(revision 0)
+++ binaries/data/mods/public/simulation/components/GlobalBarter.js	(revision 0)
@@ -0,0 +1,109 @@
+// true price of 100 resource in metal (for case if some resource is more worth)
+const TRUE_PRICES = { "food": 100, "wood": 200, "stone": 300 };
+
+// constant part of price different between true price and buy/sell price
+// in percents
+// buy price equal to true price plus constant difference
+// sell price equal to true price minus constant difference
+const CONSTANT_DIFFERENCE = 25;
+
+// additional difference of prices, added after each deal to specified resource price
+// in percents
+const DIFFERENCE_PER_DEAL = 5;
+
+// price difference which restored each restore timer tick
+// in percents
+const DIFFERENCE_RESTORE = 2;
+
+// interval of timer which slowly restore prices after deals
+const RESTORE_TIMER_INTERVAL = 5000;
+
+function GlobalBarter() {}
+
+GlobalBarter.prototype.Schema =
+	"<a:component type='system'/><empty/>";
+
+GlobalBarter.prototype.Init = function()
+{
+	this.priceDifferences = {};
+	for each (var resource in ["food", "wood", "stone"])
+		this.priceDifferences[resource] = 0;
+	this.restoreTimer = undefined;
+};
+
+GlobalBarter.prototype.GetPrices = function()
+{
+	var prices = { "buy": {}, "sell": {} };
+	for each (var resource in ["food", "wood", "stone"])
+	{
+		prices["buy"][resource] = Math.round(TRUE_PRICES[resource] * (100 + CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100);
+		prices["sell"][resource] = Math.round(TRUE_PRICES[resource] * (100 - CONSTANT_DIFFERENCE + this.priceDifferences[resource]) / 100);
+	}
+	return prices;
+};
+
+GlobalBarter.prototype.ExchangeResources = function(playerEntity, action, resource)
+{
+	var cmpPlayer = Engine.QueryInterface(playerEntity, IID_Player);
+	var amountsToSubtract = { 
+		"food": 0,
+		"wood": 0,
+		"stone": 0,
+		"metal": 0
+	};
+	var prices = this.GetPrices();
+	switch (action)
+	{
+	case "buy":
+		amountsToSubtract["metal"] = prices["buy"][resource];
+		if (cmpPlayer.TrySubtractResources(amountsToSubtract))
+		{
+			cmpPlayer.AddResource(resource, 100);
+			if (this.priceDifferences[resource] + DIFFERENCE_PER_DEAL < 100 - CONSTANT_DIFFERENCE)
+				this.priceDifferences[resource] += DIFFERENCE_PER_DEAL;
+		}
+		break;
+	case "sell":
+		amountsToSubtract[resource] = 100;
+		if (cmpPlayer.TrySubtractResources(amountsToSubtract))
+		{
+			cmpPlayer.AddResource("metal", prices["sell"][resource]);
+			if (this.priceDifferences[resource] - DIFFERENCE_PER_DEAL > CONSTANT_DIFFERENCE - 100)
+				this.priceDifferences[resource] -= DIFFERENCE_PER_DEAL;
+		}
+		break;
+	}
+	if (this.restoreTimer == undefined)
+	{
+		var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+		this.restoreTimer = cmpTimer.SetTimeout(this.entity, IID_GlobalBarter, "ProgressTimeout", RESTORE_TIMER_INTERVAL, {});
+	}
+};
+
+GlobalBarter.prototype.ProgressTimeout = function(data)
+{
+	this.restoreTimer = undefined;
+
+	var needRestore = false;
+	for each (var resource in ["food", "wood", "stone"])
+	{
+		// calculate value to restore, it should be:
+		// clamping into [-DIFFERENCE_RESTORE; DIFFERENCE_RESTORE] interval
+		var differenceRestore = Math.min(DIFFERENCE_RESTORE, Math.max(-DIFFERENCE_RESTORE, this.priceDifferences[resource]));
+		// change sign, because we need to restore price
+		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);
+		this.restoreTimer = cmpTimer.SetTimeout(this.entity, IID_GlobalBarter, "ProgressTimeout", RESTORE_TIMER_INTERVAL, data);
+	}
+}
+
+Engine.RegisterComponentType(IID_GlobalBarter, "GlobalBarter", GlobalBarter);
+
Index: binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml	(revision 10281)
+++ binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml	(working copy)
@@ -23,8 +23,8 @@
   </Health>
   <Identity>
     <GenericName>Market</GenericName>
-    <Tooltip>Create Trade units and Barter resources. (Currently a useless structure)</Tooltip>
-    <Classes datatype="tokens">Town</Classes>
+    <Tooltip>Create Trade units and Barter resources.</Tooltip>
+    <Classes datatype="tokens">Town BarterMarket</Classes>
     <Icon>structures/market.png</Icon>
   </Identity>
   <Obstruction>
Index: binaries/data/mods/public/art/textures/ui/session/resources/food.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: binaries/data/mods/public/art/textures/ui/session/resources/food.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: binaries/data/mods/public/art/textures/ui/session/resources/stone.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: binaries/data/mods/public/art/textures/ui/session/resources/stone.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: binaries/data/mods/public/art/textures/ui/session/resources/wood.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: binaries/data/mods/public/art/textures/ui/session/resources/wood.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

