Ticket #30: trading_2011_10_22.diff

File trading_2011_10_22.diff, 25.9 KB (added by fcxSanya, 13 years ago)
  • binaries/data/mods/public/gui/session/session.xml

     
    535535                        <!-- Resource carrying icon/counter -->
    536536                        <object size="0 40 48 88" type="image" name="resourceCarryingIcon" style="resourceIcon"/>
    537537                        <object size="0 80 48 100" type="text" name="resourceCarryingText" style="statsText"/>
    538 
    539538                    </object>
    540539
    541540                    <!-- Big unit icon -->
     
    690689                    </object>
    691690                </object>
    692691
     692                <object name="unitTradingPanel"
     693                    size="14 12 100% 100%"
     694                >
     695                    <object size="0 0 100% 100%">
     696                        <repeat count="4">
     697                            <object name="unitTradingButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottom">
     698                                <object name="unitTradingIcon[n]" type="image" ghost="true" size="3 3 43 43"/>
     699                            </object>
     700                        </repeat>
     701                    </object>
     702                </object>
     703
    693704                <object name="unitQueuePanel"
    694705                    size="4 -56 100% 0"
    695706                    type="image"
  • binaries/data/mods/public/gui/session/selection_details.js

     
    135135    //      getGUIObjectByName("resourceCarryingText").hidden = true;
    136136    //  }
    137137    }
     138    // Use the same indicators for traders
     139    else if (entState.trader && entState.trader.goods.amount > 0)
     140    {
     141        getGUIObjectByName("resourceCarryingIcon").hidden = false;
     142        getGUIObjectByName("resourceCarryingText").hidden = false;
     143        getGUIObjectByName("resourceCarryingIcon").cell_id = RESOURCE_ICON_CELL_IDS[entState.trader.goods.type];
     144        getGUIObjectByName("resourceCarryingText").caption = entState.trader.goods.amount;
     145    }
    138146    else
    139147    {
    140148        getGUIObjectByName("resourceCarryingIcon").hidden = true;
  • binaries/data/mods/public/gui/session/utility_functions.js

     
    8282            player.offline = true;
    8383}
    8484
     85function hasClass(entState, className)
     86{
     87    if (entState.identity)
     88    {
     89        var classes = entState.identity.classes;
     90        if (classes && classes.length)
     91            return (classes.indexOf(className) != -1);
     92    }
     93    return false;
     94}
     95
    8596function isUnit(entState)
    8697{
    8798    if (entState.identity)
  • binaries/data/mods/public/gui/session/input.js

     
    195195                }
    196196            }
    197197            break;
     198        case "setup-trade-route":
     199            // If ground or sea trade possible
     200            if ((entState.trader && hasClass(entState, "Organic") && (playerOwned || allyOwned) && hasClass(targetState, "Market")) ||
     201                (entState.trader && hasClass(entState, "Ship") && (playerOwned || allyOwned) && hasClass(targetState, "SeaMarket")))
     202                return {"possible": true};
     203            break;
    198204        case "gather":
    199205            if (targetState.resourceSupply && (playerOwned || gaiaOwned))
    200206            {
     
    218224        case "attack":
    219225            if (entState.attack && targetState.hitpoints && enemyOwned)
    220226                return {"possible": true};
     227            break;
    221228        }
    222229    }
    223230    if (action == "move")
     
    302309    else
    303310    {
    304311        var actionInfo = undefined;
    305         if ((actionInfo = getActionInfo("gather", target)).possible)
     312        if (getActionInfo("setup-trade-route", target).possible)
     313            return {"type": "setup-trade-route", "cursor": "action-setup-trade-route", "target": target};
     314        else if ((actionInfo = getActionInfo("gather", target)).possible)
    306315            return {"type": "gather", "cursor": actionInfo.cursor, "target": target};
    307316        else if ((actionInfo = getActionInfo("returnresource", target)).possible)
    308317            return {"type": "returnresource", "cursor": actionInfo.cursor, "target": target};
     
    880889                updateBuildingPlacementPreview();
    881890                break;
    882891            }
    883 
    884892            break;
    885893
    886894        }
     
    926934        Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] });
    927935        return true;
    928936
     937    case "setup-trade-route":
     938        Engine.PostNetworkCommand({"type": "setup-trade-route", "entities": selection, "target": action.target});
     939        return true;
     940
    929941    case "garrison":
    930942        Engine.PostNetworkCommand({"type": "garrison", "entities": selection, "target": action.target, "queued": queued});
    931943        // TODO: Play a sound?
     
    10081020    inputState = INPUT_BUILDING_PLACEMENT;
    10091021}
    10101022
     1023// Called by GUI when user changes preferred trading goods
     1024function selectTradingPreferredGoods(data)
     1025{
     1026    warn(data.trader + " " + data.preferredGoods);
     1027    Engine.PostNetworkCommand({"type": "select-trading-goods", "trader": data.trader, "preferredGoods": data.preferredGoods });
     1028}
     1029
    10111030// Batch training:
    10121031// When the user shift-clicks, we set these variables and switch to INPUT_BATCHTRAINING
    10131032// When the user releases shift, or clicks on a different training button, we create the batched units
  • binaries/data/mods/public/gui/session/unit_commands.js

     
    1313const UNIT_PANEL_BASE = -52; // QUEUE: The offset above the main panel (will often be negative)
    1414const UNIT_PANEL_HEIGHT = 44; // QUEUE: The height needed for a row of buttons
    1515
     16// Trading constants
     17const TRADING_RESOURCES = ["food", "wood", "stone", "metal"];
     18
    1619// The number of currently visible buttons (used to optimise showing/hiding)
    17 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Construction": 0, "Command": 0, "Stance": 0};
     20var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Trading": 0, "Construction": 0, "Command": 0, "Stance": 0};
    1821
    1922// Unit panels are panels with row(s) of buttons
    20 var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Construction", "Research", "Stance", "Command"];
     23var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Trading", "Construction", "Research", "Stance", "Command"];
    2124
    2225// Lay out a row of centered buttons (does not work inside a loop like the other function)
    2326function layoutButtonRowCentered(rowNumber, guiName, startIndex, endIndex, width)
     
    112115function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
    113116{
    114117    usedPanels[guiName] = 1;
     118
     119    // Use separate logic for trading panel
     120    if (guiName == "Trading")
     121    {
     122        setupUnitTradingPanel(unitEntState);
     123        return;
     124    }
     125
    115126    var numberOfItems = items.length;
    116127    var selection = g_Selection.toList();
    117128    var garrisonGroups = new EntityGroups();
     
    354365    g_unitPanelButtons[guiName] = numButtons;
    355366}
    356367
     368// Sets up "unit trading panel" - special case for setupUnitPanel
     369function setupUnitTradingPanel(unitEntState)
     370{
     371    for (var i = 0; i < TRADING_RESOURCES.length; i++)
     372    {
     373        var resource = TRADING_RESOURCES[i];
     374        var button = getGUIObjectByName("unitTradingButton["+i+"]");
     375        button.size = (i * 46) + " 0 " + ((i + 1) * 46) + " 46";
     376        var selectTradingPreferredGoodsData = { "trader": unitEntState.id, "preferredGoods": resource };
     377        button.onpress = (function(e){ return function() { selectTradingPreferredGoods(e); } })(selectTradingPreferredGoodsData);
     378        button.enabled = true;
     379        button.tooltip = "Set " + resource + " as trading goods";
     380        var icon = getGUIObjectByName("unitTradingIcon["+i+"]");
     381        var preferredGoods = unitEntState.trader.preferredGoods;
     382        var imageNameSuffix = (resource == preferredGoods) ? "selected" : "inactive";
     383        icon.sprite = "stretched:session/resources/" + resource + "_" + imageNameSuffix + ".png";
     384    }
     385}
     386
    357387// Updates right Unit Commands Panel - runs in the main session loop via updateSelectionDetails()
    358388function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection)
    359389{
     
    427457            setupUnitPanel("Queue", usedPanels, entState, entState.training.queue,
    428458                function (item) { removeFromTrainingQueue(entState.id, item.id); } );
    429459
     460        if (entState.trader)
     461        {
     462            setupUnitPanel("Trading", usedPanels, entState, [], null);
     463        }
     464
    430465//      supplementalDetailsPanel.hidden = false;
    431466//      commandsPanel.hidden = isInvisible;
    432467    }
  • binaries/data/mods/public/simulation/helpers/Commands.js

     
    323323        }
    324324        break;
    325325
     326    case "setup-trade-route":
     327        for each (var ent in cmd.entities)
     328        {
     329            var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
     330            if (cmpUnitAI)
     331                cmpUnitAI.SetupTradeRoute(cmd.target);
     332        }
     333        break;
     334
     335    case "select-trading-goods":
     336        var cmpTrader = Engine.QueryInterface(cmd.trader, IID_Trader);
     337        cmpTrader.SetPreferredGoods(cmd.preferredGoods);
     338        break;
     339
    326340    default:
    327341        error("Ignoring unrecognised command type '" + cmd.type + "'");
    328342    }
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    179179        };
    180180    }
    181181
     182    var cmpTrader = Engine.QueryInterface(ent, IID_Trader);
     183    if (cmpTrader)
     184    {
     185        ret.trader = {
     186            "goods": cmpTrader.GetGoods(),
     187            "preferredGoods": cmpTrader.GetPreferredGoods()
     188        };
     189    }
     190
    182191    var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation);
    183192    if (cmpFoundation)
    184193    {
  • binaries/data/mods/public/simulation/components/interfaces/Trader.js

     
     1Engine.RegisterInterface("Trader");
  • binaries/data/mods/public/simulation/components/Identity.js

     
    8989                        "<value>Worker</value>" +
    9090                        "<value>CitizenSoldier</value>" +
    9191                        "<value>Trade</value>" +
     92                        "<value>Market</value>" +
     93                        "<value>SeaMarket</value>" +
    9294                        "<value>Warship</value>" +
    9395                        "<value>SeaCreature</value>" +
    9496                        "<value>ForestPlant</value>" +
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    334334            return;
    335335        }
    336336    },
    337    
     337
     338    "Order.Trade": function(msg) {
     339        if (this.MoveToMarket(this.order.data.firstMarket))
     340        {
     341            // We've started walking to the first market
     342            this.SetNextState("INDIVIDUAL.TRADE.APPROACHINGFIRSTMARKET");
     343        }
     344    },
     345
    338346    "Order.Repair": function(msg) {
    339347        // Try to move within range
    340348        if (this.MoveToTargetRange(this.order.data.target, IID_Builder))
     
    10071015            },
    10081016        },
    10091017
     1018        "TRADE": {
     1019            "APPROACHINGFIRSTMARKET": {
     1020                "enter": function () {
     1021                    this.SelectAnimation("move");
     1022                },
     1023
     1024                "MoveCompleted": function() {
     1025                    this.PerformTrade();
     1026                    if (this.MoveToMarket(this.order.data.secondMarket))
     1027                    {
     1028                        // We've started walking to the second market
     1029                        this.SetNextState("INDIVIDUAL.TRADE.APPROACHINGSECONDMARKET");
     1030                    }
     1031                },
     1032            },
     1033
     1034            "APPROACHINGSECONDMARKET": {
     1035                "enter": function () {
     1036                    this.SelectAnimation("move");
     1037                },
     1038
     1039                "MoveCompleted": function() {
     1040                    this.order.data.firstPass = false;
     1041                    this.PerformTrade();
     1042                    if (this.MoveToMarket(this.order.data.firstMarket))
     1043                    {
     1044                        // We've started walking to the first market
     1045                        this.SetNextState("INDIVIDUAL.TRADE.APPROACHINGFIRSTMARKET");
     1046                    }
     1047                },
     1048            },
     1049        },
     1050
    10101051        "REPAIR": {
    10111052            "APPROACHING": {
    10121053                "enter": function () {
     
    23132354    this.AddOrder("ReturnResource", { "target": target }, queued);
    23142355};
    23152356
     2357UnitAI.prototype.SetupTradeRoute = function(target, queued)
     2358{
     2359    if (!this.CanTrade(target))
     2360    {
     2361        this.WalkToTarget(target, queued);
     2362        return;
     2363    }
     2364
     2365    var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
     2366    var secondMarketChanged = cmpTrader.SetTargetMarket(target);
     2367    if (secondMarketChanged)
     2368    {
     2369        this.AddOrder("Trade", { "firstMarket": cmpTrader.GetFirstMarket(), "secondMarket": cmpTrader.GetSecondMarket() }, queued);
     2370    }
     2371}
     2372
     2373UnitAI.prototype.MoveToMarket = function(targetMarket)
     2374{
     2375    if (this.MoveToTarget(targetMarket))
     2376    {
     2377        // We've started walking to the first market
     2378        return true;
     2379    }
     2380    else
     2381    {
     2382        // We can't reach the frist market.
     2383        // Give up.
     2384        this.StopMoving();
     2385        this.FinishOrder();
     2386        return false;
     2387    }
     2388}
     2389
     2390UnitAI.prototype.PerformTrade = function()
     2391{
     2392    var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
     2393    cmpTrader.PerformTrade();
     2394}
     2395
    23162396UnitAI.prototype.Repair = function(target, autocontinue, queued)
    23172397{
    23182398    if (!this.CanRepair(target))
     
    25052585    return true;
    25062586};
    25072587
     2588UnitAI.prototype.CanTrade = function(target)
     2589{
     2590    // Formation controllers should always respond to commands
     2591    // (then the individual units can make up their own minds)
     2592    if (this.IsFormationController())
     2593        return true;
     2594
     2595    // Verify that we're able to respond to Trade commands
     2596    var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
     2597    if (!cmpTrader)
     2598        return false;
     2599
     2600    return true;
     2601}
     2602
    25082603UnitAI.prototype.CanRepair = function(target)
    25092604{
    25102605    // Formation controllers should always respond to commands
  • binaries/data/mods/public/simulation/components/Trader.js

     
     1// This constant used to adjust gain value depending on distance
     2const DISTANCE_FACTOR = 1 / 50;
     3
     4// Additional gain for trading perfromed between markets of different players, in percents
     5const INTERNATIONAL_TRADING_ADDITION = 50;
     6// Additional gain for ships for each garrisoned trader, in percents
     7const GARRISONED_TRADER_ADDITION = 20;
     8
     9function Trader() {}
     10
     11Trader.prototype.Schema =
     12    "<a:help>Lets the unit generate resouces while moving between markets (or docks in case of water trading).</a:help>" +
     13    "<empty/>";
     14
     15Trader.prototype.Init = function()
     16{
     17    this.firstMarket = null;
     18    this.secondMarket = null;
     19    // Gain from one pass between markets
     20    this.gain = null;
     21    // Selected resource for trading
     22    this.preferredGoods = "metal";
     23    // Currently carried goods
     24    this.goods = { "type": null, "amount": 0 };
     25}
     26
     27Trader.prototype.CalculateGain = function()
     28{
     29    var cmpFirstMarketPosition = Engine.QueryInterface(this.firstMarket, IID_Position);
     30    var cmpSecondMarketPosition = Engine.QueryInterface(this.secondMarket, IID_Position);
     31    var firstMarketPosition = cmpFirstMarketPosition.GetPosition2D();
     32    var secondMarketPosition = cmpSecondMarketPosition.GetPosition2D();
     33    // Calculate ordinary Euclidean distance between markets.
     34    // We don't use pathfinder, because ordinary distance looks more fair.
     35    var distance = Math.sqrt(Math.pow(firstMarketPosition.x - secondMarketPosition.x, 2) + Math.pow(firstMarketPosition.y - secondMarketPosition.y, 2));
     36    // We calculate gain as square of distance to encourage trading between remote markets
     37    this.gain = Math.round(Math.pow(distance * DISTANCE_FACTOR, 2));
     38    // If markets belongs to different players, multiple gain to INTERNATIONAL_TRADING_MULTIPLIER
     39    var cmpFirstMarketOwnership = Engine.QueryInterface(this.firstMarket, IID_Ownership);
     40    var cmpSecondMarketOwnership = Engine.QueryInterface(this.secondMarket, IID_Ownership);
     41    if (cmpFirstMarketOwnership.GetOwner() != cmpSecondMarketOwnership.GetOwner())
     42        this.gain *= (100 + INTERNATIONAL_TRADING_ADDITION) / 100;
     43    // For ship increase gain for each garrisoned trader
     44    var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     45    if (cmpIdentity.HasClass("Ship"))
     46    {
     47        warn("Ship");
     48        var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
     49        if (cmpGarrisonHolder)
     50        {
     51            warn("Garrison holder");
     52            var garrisonedTradersCount = 0;
     53            for each (var entity in cmpGarrisonHolder.GetEntities())
     54            {
     55                var cmpGarrisonedUnitTrader = Engine.QueryInterface(entity, IID_Trader);
     56                if (cmpGarrisonedUnitTrader)
     57                    garrisonedTradersCount++;
     58            }
     59            warn("Garrisoned traders count: " + garrisonedTradersCount);
     60            this.gain *= (100 + GARRISONED_TRADER_ADDITION * garrisonedTradersCount) / 100;
     61        }
     62    }
     63    this.gain = Math.round(this.gain);
     64    warn("gain: " + this.gain);
     65}
     66
     67// Set target as last of target markets.
     68// Return true if both markets defined and second market was changed,
     69// i.e. we should start trading.
     70Trader.prototype.SetTargetMarket = function(target)
     71{
     72    if (this.secondMarket)
     73    {
     74        if (target != this.secondMarket)
     75        {
     76            this.firstMarket = this.secondMarket;
     77            this.secondMarket = target;
     78        }
     79        else
     80        {
     81            return false;
     82        }
     83    }
     84    else if (this.firstMarket)
     85    {
     86        if (target != this.firstMarket)
     87        {
     88            this.secondMarket = target;
     89        }
     90        else
     91        {
     92            return false;
     93        }
     94    }
     95    else
     96    {
     97        this.firstMarket = target;
     98        return false;
     99    }
     100    this.CalculateGain();
     101    // Drop carried goods
     102    this.goods.amount = 0;
     103    return true;
     104}
     105
     106Trader.prototype.GetFirstMarket = function()
     107{
     108    return this.firstMarket;
     109}
     110
     111Trader.prototype.GetSecondMarket = function()
     112{
     113    return this.secondMarket;
     114}
     115
     116Trader.prototype.GetPreferredGoods = function()
     117{
     118    return this.preferredGoods;
     119}
     120
     121Trader.prototype.SetPreferredGoods = function(preferredGoods)
     122{
     123    this.preferredGoods = preferredGoods;
     124}
     125
     126Trader.prototype.PerformTrade = function()
     127{
     128    if (this.goods.amount > 0)
     129    {
     130        var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
     131        cmpPlayer.AddResource(this.goods.type, this.goods.amount);
     132    }
     133    this.goods.type = this.preferredGoods;
     134    this.goods.amount = this.gain;
     135}
     136
     137Trader.prototype.GetGoods = function()
     138{
     139    return this.goods;
     140}
     141
     142Engine.RegisterComponentType(IID_Trader, "Trader", Trader);
  • binaries/data/mods/public/simulation/templates/template_unit_support_trader.xml

     
    1818    <GenericName>Trader</GenericName>
    1919    <Rollover>Trade was a very important part of ancient civilisation - effective trading and control of trade routes equaled wealth. Trade took place by many forms from foot to caravans to merchant ships. One of the most notorious examples of the power of trade was the Silk Road.</Rollover>
    2020    <Tooltip>Trades resources between allied Markets.</Tooltip>
    21     <Classes datatype="tokens">Trade</Classes>
    2221  </Identity>
     22  <Trader/>
    2323  <UnitMotion>
    2424    <WalkSpeed>7.0</WalkSpeed>
    2525  </UnitMotion>
  • binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml

     
    2424  <Identity>
    2525    <GenericName>Market</GenericName>
    2626    <Tooltip>Create Trade units and Barter resources. (Currently a useless structure)</Tooltip>
    27     <Classes datatype="tokens">Town</Classes>
     27    <Classes datatype="tokens">Town Market</Classes>
    2828    <Icon>structures/market.png</Icon>
    2929  </Identity>
    3030  <Obstruction>
  • binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml

     
    3838    <BarHeight>0.5</BarHeight>
    3939    <HeightOffset>6.0</HeightOffset>
    4040  </StatusBars>
     41  <Trader/>
    4142  <UnitMotion>
    4243    <WalkSpeed>10.5</WalkSpeed>
    4344  </UnitMotion>
  • binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml

     
    2727  <Identity>
    2828    <GenericName>Dock</GenericName>
    2929    <Tooltip>Build upon a shoreline to construct naval vessels and to open sea trade.</Tooltip>
    30     <Classes datatype="tokens">Town</Classes>
     30    <Classes datatype="tokens">Town Market SeaMarket</Classes>
    3131    <Icon>structures/dock.png</Icon>
    3232  </Identity>
    3333  <Obstruction>
  • binaries/data/mods/public/art/textures/cursors/action-setup-trade-route.txt

     
     11 1