Changeset 25235

Timestamp:
Apr 12, 2021, 8:51:39 AM (3 years ago)
Author:
Freagarach
Message:

Attack using cmpAttack instead of UnitAI.

Keep responsibilities separated, allow easier modding of attacking behaviour.
Ideally the BuildingAI attacking routine would be merged in here as well, but not done for now.

Part of #5810
Differential revision: D3816
Comments by: @smiley, @Stan

Location:
ps/trunk/binaries/data/mods/public/simulation/components
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • ps/trunk/binaries/data/mods/public/simulation/components/Attack.js

    r25233 r25235  
    192192};
    193193
    194 Attack.prototype.Serialize = null; // we have no dynamic state to save
    195 
    196194Attack.prototype.GetAttackTypes = function(wantedTypes)
    197195{
     
    441439
    442440    return { "max": max, "min": min, "elevationBonus": elevationBonus };
     441
     442
     443
     444
     445
     446
     447
     448
     449
     450
     451
     452
     453
     454
     455
     456
     457
     458
     459
     460
     461
     462
     463
     464
     465
     466
     467
     468
     469
     470
     471
     472
     473
     474
     475
     476
     477
     478
     479
     480
     481
     482
     483
     484
     485
     486
     487
     488
     489
     490
     491
     492
     493
     494
     495
     496
     497
     498
     499
     500
     501
     502
     503
     504
     505
     506
     507
     508
     509
     510
     511
     512
     513
     514
     515
     516
     517
     518
     519
     520
     521
     522
     523
     524
     525
     526
     527
     528
     529
     530
     531
     532
     533
     534
     535
     536
     537
     538
     539
     540
     541
     542
     543
     544
     545
     546
     547
     548
     549
     550
     551
     552
     553
     554
     555
     556
     557
     558
     559
     560
     561
     562
     563
     564
    443565};
    444566
     
    583705};
    584706
     707
     708
     709
     710
     711
     712
     713
     714
     715
     716
     717
     718
     719
     720
     721
     722
     723
     724
     725
     726
     727
     728
     729
     730
     731
     732
     733
     734
     735
     736
    585737Attack.prototype.OnValueModification = function(msg)
    586738{
  • ps/trunk/binaries/data/mods/public/simulation/components/Builder.js

    r25208 r25235  
    118118Builder.prototype.StopRepairing = function(reason)
    119119{
    120     if (this.timer)
    121     {
    122         let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    123         cmpTimer.CancelTimer(this.timer);
    124         delete this.timer;
    125     }
    126 
    127     if (this.target)
    128     {
    129         let cmpBuilderList = QueryBuilderListInterface(this.target);
    130         if (cmpBuilderList)
    131             cmpBuilderList.RemoveBuilder(this.entity);
    132 
    133         delete this.target;
    134     }
     120    if (!this.target)
     121        return;
     122
     123    let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     124    cmpTimer.CancelTimer(this.timer);
     125    delete this.timer;
     126
     127    let cmpBuilderList = QueryBuilderListInterface(this.target);
     128    if (cmpBuilderList)
     129        cmpBuilderList.RemoveBuilder(this.entity);
     130
     131    delete this.target;
    135132
    136133    let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
     
    138135        cmpVisual.SelectAnimation("idle", false, 1.0);
    139136
    140     // The callerIID component may start repairing again,
     137    // The callerIID component may start again,
    141138    // replacing the callerIID, hence save that.
    142139    let callerIID = this.callerIID;
  • ps/trunk/binaries/data/mods/public/simulation/components/Heal.js

    r25207 r25235  
    167167
    168168/**
    169  * @param {string} reason - The reason why we stopped healing. Currently implemented are:
    170  *  "outOfRange", "targetInvalidated".
     169 * @param {string} reason - The reason why we stopped healing.
    171170 */
    172171Heal.prototype.StopHealing = function(reason)
    173172{
    174     if (this.timer)
    175     {
    176         let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    177         cmpTimer.CancelTimer(this.timer);
    178         delete this.timer;
    179     }
     173    if (!this.target)
     174        return;
     175
     176    let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     177    cmpTimer.CancelTimer(this.timer);
     178    delete this.timer;
     179
     180    delete this.target;
    180181
    181182    let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
     
    183184        cmpVisual.SelectAnimation("idle", false, 1.0);
    184185
    185     delete this.target;
    186 
    187     // The callerIID component may start healing again,
     186    // The callerIID component may start again,
    188187    // replacing the callerIID, hence save that.
    189188    let callerIID = this.callerIID;
     
    200199/**
    201200 * Heal our target entity.
    202  * @params - data and lateness are unused.
     201 * @param data - Unused.
     202 * @param {number} lateness - The offset of the actual call and when it was expected.
    203203 */
    204204Heal.prototype.PerformHeal = function(data, lateness)
  • ps/trunk/binaries/data/mods/public/simulation/components/ResourceGatherer.js

    r25206 r25235  
    218218ResourceGatherer.prototype.StopGathering = function(reason)
    219219{
    220     if (this.timer)
    221     {
    222         let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    223         cmpTimer.CancelTimer(this.timer);
    224         delete this.timer;
    225     }
    226 
    227     if (this.target)
    228     {
    229         let cmpResourceSupply = Engine.QueryInterface(this.target, IID_ResourceSupply);
    230         if (cmpResourceSupply)
    231             cmpResourceSupply.RemoveGatherer(this.entity);
    232         this.RemoveFromPlayerCounter();
    233         delete this.target;
    234     }
     220    if (!this.target)
     221        return;
     222
     223    let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     224    cmpTimer.CancelTimer(this.timer);
     225    delete this.timer;
     226
     227    let cmpResourceSupply = Engine.QueryInterface(this.target, IID_ResourceSupply);
     228    if (cmpResourceSupply)
     229        cmpResourceSupply.RemoveGatherer(this.entity);
     230��   this.RemoveFromPlayerCounter();
     231
     232    delete this.target;
    235233
    236234    let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
     
    238236        cmpVisual.SelectAnimation("idle", false, 1.0);
    239237
    240     // The callerIID component may start gathering again,
     238    // The callerIID component may start again,
    241239    // replacing the callerIID, hence save that.
    242240    let callerIID = this.callerIID;
  • ps/trunk/binaries/data/mods/public/simulation/components/TreasureCollecter.js

    r25206 r25235  
    6666TreasureCollecter.prototype.StopCollecting = function(reason)
    6767{
    68     if (this.timer)
    69     {
    70         let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    71         cmpTimer.CancelTimer(this.timer);
    72         delete this.timer;
    73     }
     68    if (!this.target)
     69        return;
     70
     71    let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     72    cmpTimer.CancelTimer(this.timer);
     73    delete this.timer;
     74
    7475    delete this.target;
    7576
     
    7879        cmpVisual.SelectAnimation("idle", false, 1.0);
    7980
    80     // The callerIID component may start collecting again,
     81    // The callerIID component may start again,
    8182    // replacing the callerIID, hence save that.
    8283    let callerIID = this.callerIID;
  • ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js

    r25217 r25235  
    20902090
    20912091                    this.shouldCheer = false;
    2092                     if (!this.CanAttack(target))
    2093                     {
    2094                         this.SetNextState("COMBAT.FINDINGNEWTARGET");
     2092
     2093                    let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
     2094                    if (!cmpAttack)
     2095                    {
     2096                        this.FinishOrder();
    20952097                        return true;
    20962098                    }
     
    21042106                        }
    21052107
    2106                         this.SetNextState("COMBAT.APPROACHING");
     2108                        this.");
    21072109                        return true;
    2108                     }
    2109 
    2110                     let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
    2111                     this.attackTimers = cmpAttack.GetTimers(this.order.data.attackType);
    2112 
    2113                     // If the repeat time since the last attack hasn't elapsed,
    2114                     // delay this attack to avoid attacking too fast.
    2115                     let prepare = this.attackTimers.prepare;
    2116                     if (this.lastAttacked)
    2117                     {
    2118                         let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    2119                         let repeatLeft = this.lastAttacked + this.attackTimers.repeat - cmpTimer.GetTime();
    2120                         prepare = Math.max(prepare, repeatLeft);
    21212110                    }
    21222111
     
    21242113                        this.SetAnimationVariant("combat");
    21252114
    2126                     this.oldAttackType = this.order.data.attackType;
    2127                     this.SelectAnimation("attack_" + this.order.data.attackType.toLowerCase());
    2128                     this.SetAnimationSync(prepare, this.attackTimers.repeat);
    2129                     this.StartTimer(prepare, this.attackTimers.repeat);
    2130                     // TODO: we should probably only bother syncing projectile attacks, not melee
    2131 
    2132                     // If using a non-default prepare time, re-sync the animation when the timer runs.
    2133                     this.resyncAnimation = prepare != this.attackTimers.prepare;
    2134 
    21352115                    this.FaceTowardsTarget(this.order.data.target);
     2116
     2117
     2118
     2119
     2120
     2121
     2122
     2123
     2124
     2125
    21362126
    21372127                    let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI);
     
    21542144                    if (cmpBuildingAI)
    21552145                        cmpBuildingAI.SetUnitAITarget(0);
    2156                     this.StopTimer();
    2157                     this.ResetAnimation();
    2158                 },
    2159 
    2160                 "Timer": function(msg) {
    2161                     let target = this.order.data.target;
    2162                     let attackType = this.order.data.attackType;
    2163 
    2164                     if (!this.CanAttack(target))
    2165                     {
    2166                         this.SetNextState("COMBAT.FINDINGNEWTARGET");
    2167                         return;
    2168                     }
    2169 
    2170                     this.RememberTargetPosition();
    2171                     if (this.order.data.hunting && this.orderQueue.length > 1 && this.orderQueue[1].type === "Gather")
    2172                         this.RememberTargetPosition(this.orderQueue[1].data);
    2173 
    2174                     let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    2175                     this.lastAttacked = cmpTimer.GetTime() - msg.lateness;
    2176 
    2177                     this.FaceTowardsTarget(target);
    2178 
    2179                     // BuildingAI has it's own attack-routine
    2180                     let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI);
    2181                     if (!cmpBuildingAI)
    2182                     {
    2183                         let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
    2184                         cmpAttack.PerformAttack(attackType, target);
    2185                     }
    2186 
    2187                     // PerformAttack might have triggered messages that moved us to another state.
    2188                     // (use 'ends with' to handle formation members copying our state).
    2189                     if (!this.GetCurrentState().endsWith("COMBAT.ATTACKING"))
    2190                         return;
    2191 
    2192 
    2193                     // Check we can still reach the target for the next attack
    2194                     if (this.CheckTargetAttackRange(target, attackType))
    2195                     {
    2196                         if (this.resyncAnimation)
    2197                         {
    2198                             this.SetAnimationSync(this.attackTimers.repeat, this.attackTimers.repeat);
    2199                             this.resyncAnimation = false;
    2200                         }
    2201                         return;
    2202                     }
    2203 
    2204                     if (this.ShouldChaseTargetedEntity(target, this.order.data.force))
     2146                    let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
     2147                    if (cmpAttack)
     2148                        cmpAttack.StopAttacking();
     2149                },
     2150
     2151                "OutOfRange": function() {
     2152                    if (this.ShouldChaseTargetedEntity(this.order.data.target, this.order.data.force))
    22052153                    {
    22062154                        if (this.CanPack())
     
    22092157                            return;
    22102158                        }
    2211                         this.SetNextState("COMBAT.CHASING");
     2159                        this.SetNextState("CHASING");
    22122160                        return;
    22132161                    }
    2214 
     2162                    this.SetNextState("FINDINGNEWTARGET");
     2163                },
     2164
     2165                "TargetInvalidated": function() {
    22152166                    this.SetNextState("FINDINGNEWTARGET");
    22162167                },
     
    34003351    this.isGuardOf = undefined;
    34013352
    3402     // For preventing increased action rate due to Stop orders or target death.
    3403     this.lastAttacked = undefined;
    3404 
    34053353    this.formationAnimationVariant = undefined;
    34063354    this.cheeringTime = +(this.template.CheeringTime || 0);
     
    47904738        target = cmpFormation.GetClosestMember(this.entity);
    47914739
    4792     if (type != "Ranged")
    4793         return this.CheckTargetRange(target, IID_Attack, type);
    4794 
    4795     let targetCmpPosition = Engine.QueryInterface(target, IID_Position);
    4796     if (!targetCmpPosition || !targetCmpPosition.IsInWorld())
    4797         return false;
    4798 
    4799     let range = this.GetRange(IID_Attack, type, target);
    4800     if (!range)
    4801         return false;
    4802 
    4803     let thisCmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    4804     if (!thisCmpPosition.IsInWorld())
    4805         return false;
    4806 
    4807     let s = thisCmpPosition.GetPosition();
    4808 
    4809     let t = targetCmpPosition.GetPosition();
    4810 
    4811     let h = s.y - t.y + range.elevationBonus;
    4812     let maxRange = Math.sqrt(Math.square(range.max) + 2 * range.max * h);
    4813 
    4814     if (maxRange < 0)
    4815         return false;
    4816 
    4817     let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
    4818     return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, maxRange, false);
     4740    let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
     4741    return cmpAttack && cmpAttack.IsTargetInRange(target, type);
    48194742};
    48204743
  • ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js

    r25208 r25235  
    150150    });
    151151
    152     AddMock(SYSTEM_ENTITY, IID_ObstructionManager, {
    153         "IsInTargetRange": () => true,
    154         "IsInPointRange": () => true
    155     });
    156 
    157152    var unitAI = ConstructComponent(unit, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" });
    158153
     
    195190        "CanAttack": function(v) { return true; },
    196191        "CompareEntitiesByPreference": function(a, b) { return 0; },
     192
     193
    197194    });
    198195
     
    371368            "CanAttack": function(v) { return true; },
    372369            "CompareEntitiesByPreference": function(a, b) { return 0; },
     370
     371
     372
    373373        });
    374374
Note: See TracChangeset for help on using the changeset viewer.