Ahli / sc2xml

11 stars 1 forks source link

Units not always attacking the closest unit after being dropped #104

Open Ahli opened 1 year ago

Ahli commented 1 year ago

The Immortal and other units do not always fire at the closest unit when dropped.

In this video it fires at a Marine instead of the tank that is right next to it.

The weapon's target sort is distance, so in theory this does not make sense unless the Immortal acquired the Marine from within the Warp Prism or via other means.

update: Edited with new knowledge, removing changes that do not address this issue!

Ahli commented 1 year ago

A theory what could have happened:

Update:

Another theory that is more likely since it does not involve any unexpected bugs:

  1. Prism drops Immortal
  2. The dropped Immortal is idle as weapons only acquire targets every few milliseconds,.
  3. The Marine damages the Prism creating an alarm. Weapons usually cause alarms that trigger nearby idle units to come and attack the unit that attacked.
  4. The alarmed Immortal is attacking the Marine that shot at the Prism right after the Immortal was dropped

Weapons do not scan for an enemy every game update (remember Stalkers attack-moving into Widow Mines? That is the same issue. That issue was resolved by making the minScanRange 0.5 higher than the weapon range of pretty much all weapons).

Omniskeptic managed to recreate the issue in manual tests where the Prism is not flying over the Marines

Ahli commented 1 year ago

Here is the map that allows you to replicate the issue. It runs automatically with triggers showing the broken scenario: test map In the triggers, you can change the unit type as the disabled code/comments suggest.

It happens to Marines, Siege Tanks, Immortals, Zealots, etc. So, in short: all combat units.

When the dropped unit cannot fire at the Marine that attacked the Prism, it will continue to scan the surrounding and find a better target => the Siege Tank. But the scans may only happen after a few milliseconds, so you see the Zealot walk back and forth once before committing to attack the tank

To confirm that the alarm mechanic (also called "call for help") is causing this, we can make Marine's damage effect not cause effects via:

    <CEffectDamage id="GuassRifle">
        <ResponseFlags index="Acquire" value="0"/>
    </CEffectDamage>

like this: image

Now the units will attack the Tank after standing still for a few miliseconds

Ahli commented 1 year ago

Fix

When the unloaded unit is ordered to Stop, it will immediately scan for nearby targets.

Since Stop is the idle command, this is fine for Medivac, Overlord and Warp Prism since they have no rally point.

    <CAbilTransport id="MedivacTransport">
        <UnloadCargoEffect value="MedivacUnloadCargoSetEffect"/>
    </CAbilTransport>
    <CAbilTransport id="OverlordTransport">
        <UnloadCargoEffect value="OrderStopToTriggerWeaponScan"/>
    </CAbilTransport>

    <CEffectSet id="MedivacUnloadCargoSet">
        <EditorCategories value=""/>
        <EffectArray value="SiegeTankUnloadDelayAB"/>
        <EffectArray value="OrderStopToTriggerWeaponScan"/>
    </CEffectSet>
    <CEffectSet id="WarpPrismUnloadCargoSetEffect">
        <EffectArray value="OrderStopToTriggerWeaponScan"/>
    </CEffectSet>
    <CEffectIssueOrder id="OrderStopToTriggerWeaponScan">
        <Abil value="stop"/>
    </CEffectIssueOrder>

With above changes, the Immortal will attack the nearby Tank.

Btw, I just noticed that Prisms are the only ones that have an effect that removes a behavior related to Disruptors, so there is potentially another rare bug lurking in transports...

update: ⚠️ balance implications ⚠️ Since this fix would make drops where fast firing AA units like Marines are involved play out a bit more different as the dropped units are likely to damage/kill the high priority targets like tanks more often. Drops vs Zerg and Protoss are in theory less likely to be impacted as the transport receives less hits. However, this is still a buff to drops since the dropped unit will attack immediately instead of randomly waiting a few milliseconds after being unloaded. That means, that the units are more likely to get an extra hit on the target

Joshua-Leibold commented 5 months ago

The fix offered by Ahli above will not suffice for units that do not use "Weapon Acquire: Distance" (for instance, the colossus will maintain its target after being picked up).

I've created my own solution: add an expiring behavior to unloading units that upon expire scans around them for a unit that is within weapons range and issues an "attack once" order towards it. The expiration time is currently set to 0.125 in order to give a little bit of a delay to play their drop animation/ so it doesn't look so instantaneous

<!-- Behaviours -->
<CBehaviorBuff id="UnloadAcquireTarget">
    <EditorCategories value="AbilityorEffectType:Units"/>
    <Duration value="0.125"/>
    <FinalEffect value="AcquireTargetUnloadSearch"/>
    <Modification>
        <ModifyFlags index="OrdersUninterruptible" value="1"/>
    </Modification>
</CBehaviorBuff>

<!-- Effects -->
     <!-- Search around the unloaded unit-->
<CEffectEnumArea id="AcquireTargetUnloadSearch">
    <ImpactLocation Value="SourceUnit"/>
    <SearchFilters value="-;Player,Ally,Neutral"/>
    <TargetSorts>
        <SortArray value="TSThreatensCyclone"/>
        <SortArray value="TSDistance"/>
    </TargetSorts>
    <AreaArray MaxCount="1" Radius="10" Effect="AcquireTargetUnloadIssueAttack"/>
    <SearchFlags index="ExtendByUnitRadius" value="1"/>
</CEffectEnumArea>

     <!-- Attack (once) the unit found with the search -->
<CEffectIssueOrder id="AcquireTargetUnloadIssueAttack">
    <ValidatorArray value="WeaponInRange"/>
    <EditorCategories value=""/>
    <WhichUnit Value="Source"/>
    <Abil value="attack"/>
    <CmdFlags index="AttackOnce" value="1"/>
    <Player Value="Source"/>
    <Target Value="TargetUnit"/>
</CEffectIssueOrder>

<!-- Abilities-->
     <!-- Add behavior to unloading units. Triggers search for nearest unit within weapon range to attack -->
<CAbilTransport id="MedivacTransport">
    <UnloadCargoBehavior value="UnloadAcquireTarget"/>
</CAbilTransport>
<CAbilTransport id="OverlordTransport">
    <UnloadCargoBehavior value="UnloadAcquireTarget"/>
</CAbilTransport>
<CAbilTransport id="WarpPrismTransport">
    <UnloadCargoBehavior value="UnloadAcquireTarget"/>
</CAbilTransport>