Jump to content

R&D Tactical bug hunts (suppression and alien skipping turn)


johnnylump

Recommended Posts

In my latest playthrough, I'm growing increasingly concerned about some bugs in the tactical game that have been raised on the 2k forums but never addressed. Perhaps we can find the flaws in the code and correct them. As time permits, I'll poke around and post what may be relevant functions.

Specifically:

 

* SUPPRESSION BUG #1 -- NOT FIXED: An alien (and presumably an X-Com trooper) actively suppressing an enemy is immune to explosives damage. There may be other cases when aliens are immune to explosives as well. BUG WITNESSED SINCE 3/15 PATCH

 

* SUPPRESSION BUG #2 -- POSSIBLY PARTIALLY FIXED BY DEVS? An X-Com trooper is suppressing an alien. Another trooper kills that alien. The suppressing X-Com troopers suddenly gets sort of unglued from the terrain and is no longer able to take cover, effectively removing the trooper from the mission. Sometimes a save-and-reload solves this, but not always. It's possible the above are related somehow. Update: This occurred after the 3/15 patch when a soldier was suppressing a floater that used to its launch ability (and was subject to a reaction fire shot that missed), landed out of view, and then was promptly killed by another trooper.

 

* FLANKED ALIEN SKIP TURN -- FIXED BY AMINERI Alien skipping turn: Instead of taking 20-percent shots with rookies, I'm putting my guys on overwatch more often when there is an alien in view but behind cover -- a new tactic for me. I've seen the aliens respond with overwatches of their own or shooting sometimes, but as often as not they take no action at all, skipping their turn. This frequently allows for an easy flanking move and kill -- that would and should be much more dangerous if the alien had simply overwatched. This is one is increasingly troublesome and makes me want to play the game less; I feel like I have to avoid what should be a valid tactic (overwatching an enemy in view) and take low-percentage shots to avoid exploiting this issue. FIXED WITH XCOMGAME.UPK CODE CHANGE BY AMINERI, BELOW

Note: This fix does not impact aliens under suppression.

 

* ALIENS NEVER HUNKER DOWN -- FIXED BY AMINERI Aliens have the ability to hunker down like X-Com, but they almost never do so. Amineri discovered a coding error in the alien AI that prevents them from considering the option. FIXED WITH XCOMGAME.UPK CODE CHANGE BY AMINERI, BELOW

 

* MIND-CONTROLLED ALIEN DEATH MORALE LOSS -- POSSIBLY FIXED BY DEVS Aliens mind-controlled by an x-com psi soldier caused morale loss when killed to X-Com troopers. APPARENTLY FIXED IN 3/15 PATCH -- CODE CHANGE PRESENT

 

* MISSING SKYRANGER ESCAPE BOX -- NOT FIXED Skyranger escape box sometimes disappears during missions; it usually reappears after a reload, but some maps it stays invisble BUG STILL PRESENT IN 3/15 PATCH

 

* OVERWATCHED SQUAD SIGHT PISTOLS -- FIXED WITH DEFAULTGAMECORE.INI CHANGE Snipers on overwatch with the Squadsight perk are able to fire their pistols at squad-sighted targets. This is silly. FIX: CHANGE iReactionRange OF ALL PISTOLS FROM -1 TO 27 in DefaultGameCore.ini

 

* COMBAT DRUGS BUG -- FIX WITH LOCALIZATION EDIT The Combat Drugs perk allegedly doesn't do what it says it will. The code looks like it does do what it says. There are reports it also applies +20 aim, which is not in the tooltip. If present, it can be added to the tooltip with INT files change. Update: It does indeed grant +20 aim, so it's the tooltop that is faulty. FIX: EDIT COMBAT DRUGS ENTRIES IN THE INT FILES TO NOTE +20 AIM.

 

* SNAP SHOT BUG -- STILL PRESENT? The snap shot penalty is applied to the second shot of Double Tap, meaning Snap Shot soldiers are penalized for taking Double Tap. Still there?

 

* DISABLING SHOT BUG -- STILL PRESENT Using Disabling Shot on overwatching aliens causes the overwatching alien to take unlimited reaction shots during its turn.

 

* REVIVE BUG -- FIXED BY AMINERI -- Revive was supposed to grant one move to the revived soldier (so they wouldn't just be shot again). Code fix below.

 

Amineri's hex change allowing alien AI to consider hunker down

XComGame.upk >> XGAIAbilityDM.AI_DefenseScore
Change
0F 00 03 89 00 00 38 3F F9 38 44 1E 9A 99 99 3E 38 44 AB 38 3F 36 19 01 B2 88 00 00 09 00 98 30 00 00 00 01 98 30 00 00 1E CD CC CC 3D 16 16 
to
0F 00 03 89 00 00 F4 1E 9A 99 99 3E AB 38 3F 36 19 01 B2 88 00 00 09 00 98 30 00 00 00 01 98 30 00 00 1E CD CC CC 3D 16 16 0B 0B 0B 0B 0B 0B 

Amineri's hex change for revive:

XComGame.upk >> XGUnit.GiveOneMove
original hex:
4A B4 00 00 50 55 00 00 00 00 00 00 35 B4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 31 1B 00 00 97 59 03 00 38 00 00 00 30 00 00 00 07 35 00 9A 1B 64 33 00 00 00 00 00 00 16 25 16 1B 59 62 00 00 00 00 00 00 26 16 07 35 00 97 01 B6 30 00 00 26 16 0F 01 B6 30 00 00 26 04 0B 53 

new hex: (virtual 0x34)
4A B4 00 00 50 55 00 00 00 00 00 00 35 B4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 31 1B 00 00 97 59 03 00 34 00 00 00 30 00 00 00 1B 59 62 00 00 00 00 00 00 26 16 0F 01 B6 30 00 00 26 1B E8 5D 00 00 00 00 00 00 16 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 04 0B 53  

Amineri's hex change for alien skipping turn:

XComGame.upk >> XGAIAbilityDM.AI_GetScoreModifier()

before:
07 F6 01 9A 19 00 BA 88 00 00 0A 00 1C 7C 00 00 00 1B 1E 35 00 00 00 00 00 00 16 26 16 07 F6 01 82 81 19 01 B0 88 00 00 0A 00 C2 8A 00 00 00 2D 01 C2 8A 00 00 16 18 23 00 97 36 19 01 B1 88 00 00 09 00 77 BB 00 00 00 01 77 BB 00 00 25 16 16 B6 00 B8 88 00 00 AC 1E 9A 99 99 3E 38 3F 19 01 B0 88 00 00 09 00 8C 8A 00 00 00 01 8C 8A 00 00 16 16 04 00 B8 88 00 00 04 3A B9 88 00 00 53 
 
after:
07 F7 01 9A 19 00 BA 88 00 00 0A 00 1C 7C 00 00 00 1B 1E 35 00 00 00 00 00 00 16 26 16 07 F7 01 82 81 19 01 B0 88 00 00 0A 00 C2 8A 00 00 00 2D 01 C2 8A 00 00 16 18 23 00 97 36 19 01 B2 88 00 00 09 00 91 30 00 00 00 01 91 30 00 00 25 16 16 B6 00 B8 88 00 00 AC 1E 9A 99 99 3E 38 3F 36 19 01 B2 88 00 00 09 00 91 30 00 00 00 01 91 30 00 00 16 16 04 00 B8 88 00 00 00 B8 88 00 00 53 

Edited by johnnylump
Link to comment
Share on other sites

  • Replies 89
  • Created
  • Last Reply

Top Posters In This Topic

It is fairly easy to force the aliens to skip their turn. If the alien is flanked, and any XCOM unit in sight of the alien is on overwatch, and the alien shot % is low enough (roughly under 50%) then the alien will skip its turn.

 

This works even if the unit flanking is not the one on overwatch. This allows the somewhat cheeseball tactic of dashing a unit into a flanking position, and going into overwatch with another unit.

 

The alien seems to still take a shot if the unit isn't in cover, is in low cover but the alien has is close enough to get a range bonus to aim, etc. Sectoids at long range against targets in low cover won't shoot (65% - 20% = 45%) which is where I got the 50% number from.

Link to comment
Share on other sites

I've never noticed this suppression bugs, and for the aliens skipping their turn, since I removed the UnlimmitedAmmo tag from all weapons except laser weaps, I assumed they were reloading... but sometimes they're just ambushing me with their silent overwatch ... and yes, I've stopped picking mind control only because of this

Link to comment
Share on other sites

I've seen alien reloading animations; this isn't that.

 

I'm pretty sure I've seen the alien-skip turn even when not flanked ... just a couple of my guys on overwatch, so the alien couldn't move.

 

Weirdly, I think I saw an alien hunker down once in a recent mission! Never seen that before.

 

I imagine the mind-control panic loss is an error of an omitted exception somewhere.

 

The 50%-to-shoot may come from here (In XGAIBehavior):

 

simulated function float GetMoveOffenseScore()
{
    local int iHitChance;
    local float fBaseVal;

    // End:0x37
    if(m_kUnit.GetRemainingActions() == 1)
    {
        fBaseVal = 0.010;
    }
    // End:0x46
    else
    {
        fBaseVal = 1.0;
    }
    // End:0xAF
    if(ShouldAttackCivilians())
    {
        // End:0xA9
        if((m_kUnit.m_arrVisibleCivilians.Length == 0) && m_kUnit.m_arrCiviliansInRange.Length == 0)
        {
            return fBaseVal;
        }
        return 0.010;
    }
    // End:0xDE
    if(m_kUnit.m_arrEnemiesInRange.Length == 0)
    {
        return fBaseVal;
    }
    // End:0x12A
    if(m_kUnit.IsBeingSuppressed() || m_kAbilityDM.m_bShouldAvoidMovement)
    {
        return 0.010;
    }
    // End:0x1DB
    if(m_kUnit.GetRemainingActions() > 1)
    {
        // End:0x17D
        if(!m_kUnit.IsInCover())
        {
            return fBaseVal;
        }
        // End:0x1D5
        if(m_kAttackTarget != none)
        {
            iHitChance = int(GetHitChance(m_kAttackTarget));
            // End:0x1D5
            if(iHitChance < 50)
            {
                return (100.0 - float(iHitChance)) * 0.010;
            }
        }
        return 0.50;
    }
    return 0.010;
    //return ReturnValue;    
}

 

Warspace lowers 50 number; the author reports possibly seeing some effect -- aliens shooting more -- but it's hard to definitively connect to the code change. I haven't seen where this is called from though.

Edited by johnnylump
Link to comment
Share on other sites

Will browsing through the arrays trying to find the correct one to use to determine offset to squadsight aim penalty, I came across the following code that appears to be related to the teleport bug. It's not one of the bugs you mentioned, but it a pretty important one (although it's mostly been addressed by Firaxis now).

 

The code is in XGAIBehavior.MoveToPoint:

 

if((m_kUnit.m_arrEnemiesSeenBy.Length == 0) && !IsPathVisible(vDestination, vWarpTo))
{
WarpTo(vDestination);
}

 

This would explain why hunkering down lead to teleporting. Since all the XCOM units' sight radius was reduced, m_arrEnemiesSeenBy was empty, and the alien path wasn't visible for the same reason. The aliens acted kind of like the Weeping Angels from the new Doctor Who series. They are only locked in place while you look at them. Don't blink!

Link to comment
Share on other sites

Issue: Loss of morale when mind-controlled Alien is killed.

 

The relevant code seems to happen here, in XComGame >> XGUnit >> OnMoraleEvent()

if((XComTacticalGRI(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kBattle.IsA('XGBattle_SP') && isHuman()) && (iMoraleEvent == 2) || iMoraleEvent == 3)
    {
        ++ m_iFallenComrades;
        UpdateUnitBuffs();
    }

 

 

isHuman ultimately is a native function that checks against your pawntype, so it's hard to imagine that was written to include alien pawns, particularly given isHuman is called from a number of places, so I will assume it does what you'd expect.

 

The parens around the iMoraleEvents are a bit odd. The hex for the conditional reads as follows

 

 

07 70 01 82 82 19 19 2E 64 2D 00 00 19 12 20 4F FE FF FF 0A 00 D8 F9 FF FF 00 1C F6 FB FF FF 16 09 00 98 F9 FF FF 00 01 98 F9 FF FF 09 00 71 2D 00 00 00 01 71 2D 00 00 0B 00 CE FF FF FF 00 C5 21 50 74 00 00 00 00 00 00 16 18 0B 00 1B 82 3D 00 00 00 00 00 00 16 16 18 20 00 84 9A 00 F8 B4 00 00 2C 02 16 18 0E 00 9A 00 F8 B4 00 00 2C 03 16 16 16

 

 

Morale Events are not enumerated that I have found. Poking around the code, I see

 

0: Called from XGAction_TakeDamage >> (STATE) Executing

1: Called from XGAction_CriticallyWounded >> (STATE) CriticallyWounding

2: Called from XGUnit >> OnDeath (only for non-robotic units)

Also from XComTacticalCheatManager

3: Not found

4: Called from XGAction_FirePsi >> InternalCompleteAction

Also from XGAction_BecomePossessed >> (STATE) Executing

5: Called from XGUnit >> OnSeeZombieSpawn

6: Called from XGUnit >> EndState (maybe a reset)

7: Called from XGAction_Berserk >> DoIntimidate (Muton ability)

 

But very likely this is in order, and the problem is in XGUnit >> OnDeath. The relevant code is here:

 

if(!kIsRobotic && GetSquad().GetPermanentIndex(self) != -1)
    {
        GetSquad().OnMoraleEvent(2, self);
    }

 

 

Now, if IsHuman does NOT include SHIVs, it might work as a substitute, although kIsRobotic is a boolean and IsHuman is a function, so one would need extra bytes to make the substitution -- probably findable in the large OnDeath function, However, I wonder if isHuman() includes civilians, both VIPs and those in terror missions.

 

Also, OnDeath contains a call to the native function ClearSuppressingEnemies(). This may be where the suppressor of a killed unit becomes unglued from cover. The only other call to ClearSuppressingEnemies() in XGUnit is in ProcessSuppression, which is structured like this: kUnit.ClearSuppressingEnemies() ... kUnit is a parameter in ProcessSuppression, when it's called, the parameter is "self"

 

(However, come to think of it, I haven't seen the suppression bug since the last update, but I haven't tested for it, either, so I'm not sure if this represents the fix, or if it's still broken)

Link to comment
Share on other sites

I've found this morale event 3. XGSquad.OnMoraleEvent calls XGUnit.OnMoraleEvent passing arg 3 if the killed unit is 3 levels above the watching unit

 

 

function OnMoraleEvent(XGTacticalGameCore.EMoraleEvent EEvent, XGUnit kIgnoreUnit)
{
    local int I, iNumReadyForPanic, iNumPanicked;
    local XGCharacter_Soldier kSoldier, kDead;
    local XGUnit kUnit;

    iNumReadyForPanic = 0;
    I = 0;
    J0x16:
    // End:0xD3 [Loop If]
    if(I < (GetNumMembers()))
    {
        kUnit = GetMemberAt(I);
        // End:0xBA
        if((!kUnit.IsAliveAndWell() || kUnit.IsPanicking()) || kUnit.IsPanicked())
        {
        }
        // End:0xC5
        else
        {
            ++ iNumReadyForPanic;
        }
        ++ I;
        // [Loop Continue]
        goto J0x16;
    }
    iNumReadyForPanic /= float(2);
    iNumPanicked = 0;
    I = 0;
    J0xF8:
    // End:0x307 [Loop If]
    if(I < (GetNumMembers()))
    {
        kUnit = GetMemberAt(I);
        // End:0x19E
        if((!kUnit.IsAliveAndWell() || kUnit == kIgnoreUnit) || !kUnit.CanSee(kIgnoreUnit))
        {
        }
        // End:0x2F9
        else
        {
            // End:0x2AD
            if(EEvent == 2)
            {
                kDead = XGCharacter_Soldier(kIgnoreUnit.GetCharacter());
                kSoldier = XGCharacter_Soldier(kUnit.GetCharacter());
                // End:0x2AD
                if((kDead != none) && kSoldier != none)
                {
                    // End:0x2AD
                    if(kDead.m_kSoldier.iRank >= (kSoldier.m_kSoldier.iRank + 3))
                    {
                        EEvent = 3;
                    }
                }
            }
            // End:0x2F9
            if(kUnit.OnMoraleEvent(EEvent, iNumPanicked >= iNumReadyForPanic))
            {
                ++ iNumPanicked;
            }
        }
        ++ I;
        // [Loop Continue]
        goto J0xF8;
    }
    //return;    
}

although it seems it's just left overs from an unimplemented solution, since in xgunit it doesn't make any distinction. I'd got to check hex code again, but in the lack of parentheses I'd say the last OR takes precedence over the preceding AND. I wrote a condition where last two elements were obligatory but only one of them and the decompiled code looked just like this. Dunno if it's because how operations are stacked that it will always resolve the last one, or if actualy some logical operator takes precedence over another just like in maths. It'd be good to know

Link to comment
Share on other sites

it just occured to me that perhaps that GetSquad().GetPermanentIndex(self) != -1 may be checking if unit is in player squad, so maybe it can be shortened somehow... I guess that just checking if it was human would cause panick when civilians die... that'd be cool :)

Link to comment
Share on other sites

function AddUnit(XGUnit kUnit, optional bool bTemporary)
{
    local int iPermanentIndex;

    bTemporary = false;
    // End:0xB2
    if(!kUnit.IsPossessed() && !bTemporary)
    {
        iPermanentIndex = GetPermanentIndex(kUnit);
        // End:0x93
        if(iPermanentIndex == -1)
        {
            AddUnitToEnd(kUnit);
            AddPermanentUnit(kUnit);
        }
        // End:0xAF
        else
        {
            ReAddUnit(kUnit, iPermanentIndex);
        }
    }
    // End:0xC5
    else
    {
        AddUnitToEnd(kUnit);
    }
    //return;    
}
simulated function int GetPermanentIndex(XGUnit kUnit)
{
    local int I;

    I = 0;
    J0x0B:
    // End:0x5B [Loop If]
    if(I < m_iNumPermanentUnits)
    {
        // End:0x4D
        if(m_arrPermanentMembers[i] == kUnit)
        {
            return I;
        }
        ++ I;
        // [Loop Continue]
        goto J0x0B;
    }
    return -1;
    //return ReturnValue;    
}

 

Hrm, my brain is a bit fried today but that looks like it should prevent the event from firing for non-permanent members of the squad, like VIPs and mc aliens.

Edited by johnnylump
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...