Ticket #3277: trading_v5.patch

File trading_v5.patch, 16.1 KB (added by otero, 8 years ago)
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    16841684    let firstMarket = cmpEntityTrader.GetFirstMarket();
    16851685    let secondMarket = cmpEntityTrader.GetSecondMarket();
    16861686    let result = null;
    1687     if (data.target === firstMarket)
     1687    if (!firstMarket)
     1688        result = { "type": "set first" };
     1689    else if (!secondMarket)
    16881690    {
    16891691        result = {
     1692            "type": "set second",
     1693            "gain": cmpEntityTrader.CalculateGain(firstMarket, data.target),
     1694        };
     1695    }
     1696    else if (data.target === firstMarket)
     1697    {
     1698        result = {
    16901699            "type": "is first",
    16911700            "hasBothMarkets": cmpEntityTrader.HasBothMarkets()
    16921701        };
     
    17001709            "gain": cmpEntityTrader.GetGoods().amount,
    17011710        };
    17021711    }
    1703     else if (!firstMarket)
    1704     {
    1705         result = { "type": "set first" };
    1706     }
    1707     else if (!secondMarket)
    1708     {
    1709         result = {
    1710             "type": "set second",
    1711             "gain": cmpEntityTrader.CalculateGain(firstMarket, data.target),
    1712         };
    1713     }
    17141712    else
    17151713    {
    17161714        // Else both markets are not null and target is different from them
  • binaries/data/mods/public/simulation/components/Market.js

     
     1/**
     2 @file Market.js
     3 Component used for the management of market and trade units in the field
     4*/
     5function Market() {}
     6
     7Market.prototype.Schema = "<a:component type='system'/><empty/>";
     8
     9Market.prototype.Init = function()
     10{
     11    this.tradeEntities = []; // List of trade entities from which this market works as a target
     12};
     13
     14/**
     15 Subscribes a new trader to this market.
     16 @param {Object} trader - The trader object expected being subscribed inside the market.
     17*/
     18
     19Market.prototype.SubscribeTrader = function(trader)
     20{
     21    if (this.tradeEntities.indexOf(trader) === -1)
     22        this.tradeEntities.push(trader);
     23};
     24
     25Market.prototype.OnDestroy = function()
     26{
     27    this.NotifySubscribedTraders();
     28};
     29
     30Market.prototype.OnOwnershipChanged = function(msg)
     31{
     32    if (msg.to != -1 && msg.from != -1)
     33        this.NotifySubscribedTraders();
     34};
     35
     36Market.prototype.OnDiplomacyChanged = function(msg)
     37{
     38    this.NotifySubscribedTraders();
     39}
     40
     41/**
     42 Notifies traders subscribed to this market to change their trading behavior
     43*/
     44Market.prototype.NotifySubscribedTraders = function() {
     45
     46    let cmpUnitAI = undefined;
     47    // When the market is invalidated for an entity, this should be unsubscribed
     48    // from the market. But this creates a change in the size of the array,
     49    // generating an error that when the iterator arrives to half of the sub array
     50    // that would be eliminated, the upper half will be the new lower half. This
     51    // generate a problem because then the array returns null after the iterator
     52    // has moved passed the half for the next half of elements. To eliminate
     53    // this problem we create a temporal array where the index of the trade
     54    // elements that will be unsubscribed are temporarily saved and
     55    // eliminated in the future
     56    let entitiesToEliminate = [];
     57    for(let trader of this.tradeEntities)
     58    {
     59        if (!trader)
     60            continue;
     61
     62        cmpUnitAI = Engine.QueryInterface(trader, IID_UnitAI);
     63        if (cmpUnitAI && !cmpUnitAI.CanTrade(this.entity))
     64        {
     65            // Unsubscribe the trader from the market
     66            entitiesToEliminate.push(this.tradeEntities.indexOf(trader));
     67            cmpUnitAI.InvalidateTradingMarket(this.entity);
     68        }
     69    }
     70
     71    // Unsubscribe the entities from this market
     72    for(let index of entitiesToEliminate)
     73    {
     74        this.tradeEntities.splice(index, 1);
     75    }
     76}
     77
     78Engine.RegisterComponentType(IID_Market, "Market", Market);
  • binaries/data/mods/public/simulation/components/Trader.js

     
    1 // See helpers/TraderGain.js for the CalculateTaderGain() function which works out how many 
    2 // resources a trader gets 
     1// See helpers/TraderGain.js for the CalculateTaderGain() function which works out how many
     2// resources a trader gets
    33
    44// Additional gain for ships for each garrisoned trader, in percents
    55const GARRISONED_TRADER_ADDITION = 20;
     
    6161                gain.market2Gain = Math.round(garrisonMultiplier * gain.market2Gain);
    6262        }
    6363    }
    64    
     64
    6565    return gain;
    6666};
    6767
     
    166166    var cmpTraderPlayer = QueryOwnerInterface(this.entity, IID_Player);
    167167    var traderPlayerId = cmpTraderPlayer.GetPlayerID();
    168168    var cmpTargetPlayer = QueryOwnerInterface(target, IID_Player);
     169    if (!cmpTargetPlayer)
     170        return false;
    169171    var targetPlayerId = cmpTargetPlayer.GetPlayerID();
    170172    var ownershipSuitableForTrading = cmpTraderPlayer.IsAlly(targetPlayerId) || cmpTraderPlayer.IsNeutral(targetPlayerId);
    171173    if (!ownershipSuitableForTrading)
     
    270272        this.goods.amount = this.CalculateGain(this.markets[0], this.markets[1]);
    271273};
    272274
     275Trader.prototype.InvalidateFirstMarket = function()
     276{
     277    this.markets[0] = INVALID_ENTITY;
     278}
     279
     280Trader.prototype.InvalidateSecondMarket = function()
     281{
     282    this.markets[1] = INVALID_ENTITY;
     283}
     284
    273285Engine.RegisterComponentType(IID_Trader, "Trader", Trader);
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    5858// There some targeting options:
    5959//   targetVisibleEnemies: anything in vision range is a viable target
    6060//   targetAttackersAlways: anything that hurts us is a viable target,
    61 //    possibly overriding user orders!
     61//  possibly overriding user orders!
    6262//   targetAttackersPassive: anything that hurts us is a viable target,
    63 //    if we're on a passive/unforced order (e.g. gathering/building)
     63//  if we're on a passive/unforced order (e.g. gathering/building)
    6464// There are some response options, triggered when targets are detected:
    6565//   respondFlee: run away
    6666//   respondChase: start chasing after the enemy
    6767//   respondChaseBeyondVision: start chasing, and don't stop even if it's out
    68 //    of this unit's vision range (though still visible to the player)
     68//  of this unit's vision range (though still visible to the player)
    6969//   respondStandGround: attack enemy but don't move at all
    7070//   respondHoldGround: attack enemy but don't move far from current position
    7171// TODO: maybe add targetAggressiveEnemies (don't worry about lone scouts,
     
    349349            return;
    350350        }
    351351
    352         // Check if we need to move    TODO implement a better way to know if we are on the shoreline
     352        // Check if we need to move  TODO implement a better way to know if we are on the shoreline
    353353        var needToMove = true;
    354354        var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    355355        if (this.lastShorelinePosition && cmpPosition && (this.lastShorelinePosition.x == cmpPosition.GetPosition().x)
    356             && (this.lastShorelinePosition.z == cmpPosition.GetPosition().z))
     356            && (this.lastShorelinePosition.z == cmpPosition.GetPosition().z))
    357357        {
    358358            // we were already on the shoreline, and have not moved since
    359359            if (DistanceBetweenEntities(this.entity, this.order.data.target) < 50)
     
    10801080                var cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder);
    10811081                if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity))
    10821082                {
    1083                     this.pickup = this.order.data.target;       // temporary, deleted in "leave"
     1083                    this.pickup = this.order.data.target;      // temporary, deleted in "leave"
    10841084                    Engine.PostMessage(this.pickup, MT_PickupRequested, { "entity": this.entity });
    10851085                }
    10861086            },
     
    13871387            var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
    13881388            var cmpHealth = Engine.QueryInterface(this.isGuardOf, IID_Health);
    13891389            if (cmpIdentity && cmpIdentity.HasClass("Support") &&
    1390                 cmpHealth && cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints())
     1390                cmpHealth && cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints())
    13911391            {
    13921392                if (this.CanHeal(this.isGuardOf))
    13931393                    this.PushOrderFront("Heal", { "target": this.isGuardOf, "force": false });
     
    17951795                    }
    17961796                    // Check the target is still alive and attackable
    17971797                    if (this.TargetIsAlive(target) &&
    1798                         this.CanAttack(target, this.order.data.forceResponse || null) &&
    1799                         !this.CheckTargetAttackRange(target, this.order.data.attackType))
     1798                        this.CanAttack(target, this.order.data.forceResponse || null) &&
     1799                        !this.CheckTargetAttackRange(target, this.order.data.attackType))
    18001800                    {
    18011801                        // Can't reach it - try to chase after it
    18021802                        if (this.ShouldChaseTargetedEntity(target, this.order.data.force))
     
    20372037                    var cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply);
    20382038                    var cmpMirage = Engine.QueryInterface(this.gatheringTarget, IID_Mirage);
    20392039                    if ((!cmpMirage || !cmpMirage.Mirages(IID_ResourceSupply)) &&
    2040                         (!cmpSupply || !cmpSupply.AddGatherer(cmpOwnership.GetOwner(), this.entity)))
     2040                        (!cmpSupply || !cmpSupply.AddGatherer(cmpOwnership.GetOwner(), this.entity)))
    20412041                    {
    20422042                        // Save the current order's data in case we need it later
    20432043                        var oldType = this.order.data.type;
     
    26142614                        this.PerformTradeAndMoveToNextMarket(this.order.data.target);
    26152615                },
    26162616            },
     2617
     2618            "INVALIDATINGMARKET": {
     2619                "enter": function() {
     2620                    this.StartTimer(50,1000);
     2621                },
     2622
     2623                "Timer": function(msg) {
     2624                    if(!this.CheckTargetVisible(this.order.data.target))
     2625                        this.InvalidateTradingMarket(this.order.data.target);
     2626                },
     2627
     2628                "leave": function() {
     2629                    this.StopTimer();
     2630                    this.StopTrading();
     2631                },
     2632
     2633                "MoveCompleted": function() {
     2634                    this.StopTimer();
     2635                    this.SetNextState("IDLE");
     2636                }
     2637
     2638            },
     2639
    26172640        },
    26182641
    26192642        "REPAIR": {
     
    27242747                    let dropsiteTypes = cmpResourceDropsite.GetTypes();
    27252748                    cmpResourceGatherer.CommitResources(dropsiteTypes);
    27262749                    this.SetGathererAnimationOverride();
    2727                 } 
     2750                }
    27282751
    27292752                // We finished building it.
    27302753                // Switch to the next order (if any)
     
    27992822                var cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder);
    28002823                if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity))
    28012824                {
    2802                     this.pickup = this.order.data.target;       // temporary, deleted in "leave"
     2825                    this.pickup = this.order.data.target;      // temporary, deleted in "leave"
    28032826                    Engine.PostMessage(this.pickup, MT_PickupRequested, { "entity": this.entity });
    28042827                }
    28052828            },
     
    30543077    "ANIMAL": {
    30553078        "Attacked": function(msg) {
    30563079            if (this.template.NaturalBehaviour == "skittish" ||
    3057                 this.template.NaturalBehaviour == "passive")
     3080                this.template.NaturalBehaviour == "passive")
    30583081            {
    30593082                this.Flee(msg.data.attacker, false);
    30603083            }
     
    34363459    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    34373460    if (this.losRangeQuery)
    34383461        this.SetupRangeQuery(cmpRangeManager.IsActiveQueryEnabled(this.losRangeQuery));
    3439  
     3462
    34403463    if (this.IsHealer() && this.losHealRangeQuery)
    34413464        this.SetupHealRangeQuery(cmpRangeManager.IsActiveQueryEnabled(this.losHealRangeQuery));
    34423465};
     
    51565179        return;
    51575180    }
    51585181
    5159     var marketsChanged = this.SetTargetMarket(target, source);
     5182    let marketsChanged = this.SetTargetMarket(target, source);
    51605183    if (!marketsChanged)
    51615184        return;
    51625185
    5163     var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
     5186    let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
     5187    let cmpMarket = Engine.QueryInterface(target, IID_Market);
     5188    if(!cmpTrader || !cmpMarket)
     5189        return;
     5190
     5191    cmpMarket.SubscribeTrader(this.entity);
     5192
    51645193    if (cmpTrader.HasBothMarkets())
    51655194    {
    51665195        let data = {
     
    51795208        if (this.IsFormationController())
    51805209        {
    51815210            this.CallMemberFunction("AddOrder", ["Trade", data, queued]);
    5182             var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
     5211            let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
    51835212            if (cmpFormation)
    51845213                cmpFormation.Disband();
    51855214        }
     
    52275256    return ok;
    52285257};
    52295258
     5259/**
     5260 * Makes a routing trade invalid sending the trader unit to the next
     5261 * possible market.
     5262 */
     5263UnitAI.prototype.InvalidateTradingMarket = function(market)
     5264{
     5265    // Find the entity inside the one of the markets in the trader
     5266    //
     5267    // If the market is found as the first market then send the trader to the
     5268    // second market.
     5269    //
     5270    // If the market is found in the second market then send the trader to the
     5271    // first market
     5272    //
     5273    // If the source and target have been captured then stop
     5274    // trading
     5275    let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
     5276
     5277    // Check if the market is visible, otherwise add it to the Queue
     5278    if(cmpTrader && !this.CheckTargetVisible(market))
     5279    {
     5280        if (!cmpTrader.GetFirstMarket() && !cmpTrader.GetSecondMarket())
     5281            this.StopTrading();
     5282        else if(cmpTrader.GetFirstMarket() === market)
     5283        {
     5284            cmpTrader.InvalidateFirstMarket();
     5285            if(this.order.data.target === market)
     5286                this.MoveToMarket(cmpTrader.GetSecondMarket());
     5287        }
     5288        else if(cmpTrader.GetSecondMarket() === market)
     5289        {
     5290            cmpTrader.InvalidateSecondMarket();
     5291            if(this.order.data.target === market)
     5292                this.MoveToMarket(cmpTrader.GetFirstMarket());
     5293        }
     5294    }
     5295    else if(this.GetCurrentState() !== "INDIVIDUAL.TRADE.INVALIDATINGMARKET")
     5296    {
     5297        this.order.data.target = market;
     5298        this.SetNextState("INDIVIDUAL.TRADE.INVALIDATINGMARKET");
     5299    }
     5300};
     5301
    52305302UnitAI.prototype.PerformTradeAndMoveToNextMarket = function(currentMarket)
    52315303{
    52325304    if (!this.CanTrade(currentMarket))
     
    52435315    }
    52445316
    52455317    let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
     5318    if(!cmpTrader)
     5319    {
     5320        this.StopTrading();
     5321        return;
     5322    }
     5323
    52465324    cmpTrader.PerformTrade(currentMarket);
    52475325    let amount = cmpTrader.GetGoods().amount;
    52485326    if (!amount || !amount.traderGain)
     
    52715349UnitAI.prototype.StopTrading = function()
    52725350{
    52735351    this.StopMoving();
    5274     this.FinishOrder();
    5275     var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
     5352    let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
    52765353    cmpTrader.StopTrading();
    52775354};
    52785355
     
    57755852    if (cmpOwnership && IsOwnedByPlayer(cmpOwnership.GetOwner(), target))
    57765853        return true;
    57775854    return cmpPlayer && cmpPlayer.HasSharedDropsites() && cmpResourceDropsite.IsShared() &&
    5778            cmpOwnership && IsOwnedByMutualAllyOfPlayer(cmpOwnership.GetOwner(), target);
     5855           cmpOwnership && IsOwnedByMutualAllyOfPlayer(cmpOwnership.GetOwner(), target);
    57795856};
    57805857
    57815858UnitAI.prototype.CanTrade = function(target)
  • binaries/data/mods/public/simulation/components/interfaces/Market.js

     
     1Engine.RegisterInterface("Market");
  • binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml

     
    6464  <Vision>
    6565    <Range>32</Range>
    6666  </Vision>
     67  <Market/>
    6768  <VisualActor>
    6869    <FoundationActor>structures/fndn_5x5.xml</FoundationActor>
    6970  </VisualActor>
  • binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml

     
    7575   <Vision>
    7676    <Range>40</Range>
    7777  </Vision>
     78  <Market/>
    7879  <VisualActor>
    7980    <FoundationActor>structures/fndn_4x4_dock.xml</FoundationActor>
    8081  </VisualActor>