parent
d81516defd
commit
8cbab40137
@ -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
|
||||
|
BIN
binaries/data/mods/public/art/textures/ui/session/resources/food_inactive.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/resources/food_inactive.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/resources/food_selected.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/resources/food_selected.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/resources/metal_inactive.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/resources/metal_inactive.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/resources/metal_selected.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/resources/metal_selected.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/resources/stone_inactive.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/resources/stone_inactive.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/resources/stone_selected.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/resources/stone_selected.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/resources/wood_inactive.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/resources/wood_inactive.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
binaries/data/mods/public/art/textures/ui/session/resources/wood_selected.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/resources/wood_selected.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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
|
||||
|
@ -524,6 +524,31 @@
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<object name="unitBarterPanel" hidden="true"
|
||||
size="5 5 100% 100%"
|
||||
>
|
||||
<object ghost="true" style="resourceText" type="text" size="0 0 100% 18">Exchange resources:</object>
|
||||
<object size="0 18 100% 64">
|
||||
<repeat count="4">
|
||||
<object name="unitBarterSellButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottomBold">
|
||||
<object name="unitBarterSellIcon[n]" type="image" ghost="true" size="3 3 43 43"/>
|
||||
<object name="unitBarterSellAmount[n]" ghost="true" style="resourceText" type="text" size="0 0 100% 50%"/>
|
||||
</object>
|
||||
</repeat>
|
||||
</object>
|
||||
<object size="0 64 100% 110">
|
||||
<repeat count="4">
|
||||
<object name="unitBarterBuyButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottomBold">
|
||||
<object name="unitBarterBuyIcon[n]" type="image" ghost="true" size="3 3 43 43"/>
|
||||
<object name="unitBarterBuyAmount[n]" ghost="true" style="resourceText" type="text" size="0 0 100% 50%"/>
|
||||
</object>
|
||||
</repeat>
|
||||
</object>
|
||||
<object name="PerformDealButton" type="button" style="StoneButton" size="2 112 100%-7 142">
|
||||
<object ghost="true" style="statsText" type="text" size="0 0 100% 100%">Barter</object>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<!-- Stance Selection -->
|
||||
<object name="unitStancePanel"
|
||||
style="TranslucentPanel"
|
||||
|
@ -13,11 +13,21 @@ const COMMANDS_PANEL_WIDTH = 228;
|
||||
const UNIT_PANEL_BASE = -52; // QUEUE: The offset above the main panel (will often be negative)
|
||||
const UNIT_PANEL_HEIGHT = 44; // QUEUE: The height needed for a row of buttons
|
||||
|
||||
// Barter constants
|
||||
const BARTER_RESOURCE_AMOUNT_TO_SELL = 100;
|
||||
const BARTER_BUNCH_MULTIPLIER = 5;
|
||||
const BARTER_RESOURCES = ["food", "wood", "stone", "metal"];
|
||||
const BARTER_ACTIONS = ["Sell", "Buy"];
|
||||
|
||||
// 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"];
|
||||
|
||||
// Indexes of resources to sell and buy on barter panel
|
||||
var g_barterSell = 0;
|
||||
var g_barterBuy = 1;
|
||||
|
||||
// Lay out a row of centered buttons (does not work inside a loop like the other function)
|
||||
function layoutButtonRowCentered(rowNumber, guiName, startIndex, endIndex, width)
|
||||
@ -108,6 +118,16 @@ function layoutButtonRow(rowNumber, guiName, buttonSideLength, buttonSpacer, sta
|
||||
}
|
||||
}
|
||||
|
||||
function selectBarterResourceToSell(resourceIndex)
|
||||
{
|
||||
g_barterSell = resourceIndex;
|
||||
// g_barterBuy should be set to different value in case if it is the same as g_barterSell
|
||||
// (it is no make sense to exchange resource to the same one).
|
||||
// We change it cyclic to next value.
|
||||
if (g_barterBuy == g_barterSell)
|
||||
g_barterBuy = (g_barterBuy + 1) % BARTER_RESOURCES.length;
|
||||
}
|
||||
|
||||
// Sets up "unit panels" - the panels with rows of icons (Helper function for updateUnitDisplay)
|
||||
function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
{
|
||||
@ -368,6 +388,65 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
|
||||
g_unitPanelButtons[guiName] = numButtons;
|
||||
}
|
||||
|
||||
// Sets up "unit barter panel" - special case for setupUnitPanel
|
||||
function setupUnitBarterPanel(unitEntState)
|
||||
{
|
||||
// Amount of player's resource to exchange
|
||||
var amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
|
||||
if (Engine.HotkeyIsPressed("session.massbarter"))
|
||||
amountToSell *= BARTER_BUNCH_MULTIPLIER;
|
||||
// One pass for each resource
|
||||
for (var i = 0; i < BARTER_RESOURCES.length; i++)
|
||||
{
|
||||
var resource = BARTER_RESOURCES[i];
|
||||
// One pass for 'sell' row and another for 'buy'
|
||||
for (var j = 0; j < 2; j++)
|
||||
{
|
||||
var selectedResourceIndex = [g_barterSell, g_barterBuy][j];
|
||||
var action = BARTER_ACTIONS[j];
|
||||
|
||||
var imageNameSuffix = (i == selectedResourceIndex) ? "selected" : "inactive";
|
||||
var icon = getGUIObjectByName("unitBarter" + action + "Icon["+i+"]");
|
||||
|
||||
var button = getGUIObjectByName("unitBarter" + action + "Button["+i+"]");
|
||||
button.size = (i * 46) + " 0 " + ((i + 1) * 46) + " 46";
|
||||
var amountToBuy;
|
||||
// In 'buy' row show black icon in place corresponding to selected resource in 'sell' row
|
||||
if (j == 1 && i == g_barterSell)
|
||||
{
|
||||
button.enabled = false;
|
||||
button.tooltip = "";
|
||||
icon.sprite = "";
|
||||
amountToBuy = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
button.enabled = true;
|
||||
button.tooltip = action + " " + resource;
|
||||
icon.sprite = "stretched:session/resources/" + resource + "_" + imageNameSuffix + ".png";
|
||||
var sellPrice = unitEntState.barterMarket.prices["sell"][BARTER_RESOURCES[g_barterSell]];
|
||||
var buyPrice = unitEntState.barterMarket.prices["buy"][resource];
|
||||
amountToBuy = "+" + Math.round(sellPrice / buyPrice * amountToSell);
|
||||
}
|
||||
var amount;
|
||||
if (j == 0)
|
||||
{
|
||||
button.onpress = (function(i){ return function() { selectBarterResourceToSell(i); } })(i);
|
||||
amount = (i == g_barterSell) ? "-" + amountToSell : "";
|
||||
}
|
||||
else
|
||||
{
|
||||
button.onpress = (function(i){ return function() { g_barterBuy = i; } })(i);
|
||||
amount = amountToBuy;
|
||||
}
|
||||
getGUIObjectByName("unitBarter" + action + "Amount["+i+"]").caption = amount;
|
||||
}
|
||||
}
|
||||
var performDealButton = getGUIObjectByName("PerformDealButton");
|
||||
var exchangeResourcesParameters = { "sell": BARTER_RESOURCES[g_barterSell], "buy": BARTER_RESOURCES[g_barterBuy], "amount": amountToSell };
|
||||
performDealButton.onpress = function() { exchangeResources(exchangeResourcesParameters) };
|
||||
}
|
||||
|
||||
// Updates right Unit Commands Panel - runs in the main session loop via updateSelectionDetails()
|
||||
function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection)
|
||||
{
|
||||
@ -424,6 +503,13 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
|
||||
function (item) { performStance(entState.id, item); } );
|
||||
}
|
||||
|
||||
getGUIObjectByName("unitBarterPanel").hidden = !entState.barterMarket;
|
||||
if (entState.barterMarket)
|
||||
{
|
||||
usedPanels["Barter"] = 1;
|
||||
setupUnitBarterPanel(entState);
|
||||
}
|
||||
|
||||
if (entState.buildEntities && entState.buildEntities.length)
|
||||
{
|
||||
setupUnitPanel("Construction", usedPanels, entState, entState.buildEntities, startBuildingPlacement);
|
||||
|
139
binaries/data/mods/public/simulation/components/Barter.js
Normal file
139
binaries/data/mods/public/simulation/components/Barter.js
Normal file
@ -0,0 +1,139 @@
|
||||
// True price of 100 units of resource (for case if some resource is more worth).
|
||||
// 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.
|
||||
// Sell price equal to true price minus constant difference.
|
||||
const CONSTANT_DIFFERENCE = 10;
|
||||
|
||||
// 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;
|
||||
|
||||
// Array of resource names
|
||||
const RESOURCES = ["food", "wood", "stone", "metal"];
|
||||
|
||||
function Barter() {}
|
||||
|
||||
Barter.prototype.Schema =
|
||||
"<a:component type='system'/><empty/>";
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -82,6 +82,7 @@ Identity.prototype.Schema =
|
||||
"<value>CivCentre</value>" +
|
||||
"<value>Economic</value>" +
|
||||
"<value>Defensive</value>" +
|
||||
"<value>BarterMarket</value>" +
|
||||
"<value>Village</value>" +
|
||||
"<value>Town</value>" +
|
||||
"<value>City</value>" +
|
||||
|
@ -0,0 +1 @@
|
||||
Engine.RegisterInterface("Barter");
|
@ -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));
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user