Interface UPK mods Enhanced Perk Tree - XCOM:EU 2012

From Nexus Mods Wiki
Revision as of 02:32, 19 May 2013 by Dubiousintent (talk | contribs) (References)
Jump to: navigation, search

Overview

This page deals with the UPK file changes necessary to implement the Enhanced Perk Tree mod

These changes only deal with altering the behavior of the Perk/Abilities/Psi Abilities interface. UPK changes to implement the perks are implemented here:

All of these changes are applied to XComStrategyGame.upk

Programs and Tools

  • UE Explorer
  • HxD Hex Editor

See Modding Tools - XCOM:EU 2012 for descriptions and links.

Details

There are nine distinct hex changes necessary to implement this portion of the Enhanced Perk Tree mod.

One of the changes requires an identical search/replace in three separate locations, bringing the total number of separate hex changes to eleven.


The upk interface code refers variously to 'rows', 'options', 'branches', and 'columns'

  • Row corresponds with 'option', and relates to the number of ability icons displayed per rank
    • Both 'row' and 'option' are indexed starting at 0
  • Column corresponds with 'branch', and relates to the soldier rank
    • 'column' is indexed starting from 0, but 'branch' is indexed starting from 1


  1. Change the default value of XGStrategySoldier.m_iEnergy from 100 to 0
  2. Three distinct but identical changes in XGFacility_Barracks.HealAndRest to prevent m_iEnergy from being updated
  3. Rewrite XGStrategySoldier.HasPerk to check only bit 0 of aUpgrades[]
  4. Rewrite UISoldierPromotion.CheckForAutoAssignPerk to advance the promotion rank if no valid perks are available at the current rank
  5. Rewrite XGSoldierUI.OnAcceptPromotion to mark the promotion flag in m_iEnergy upon accepting a promotion
  6. Rewrite XGStrategySoldier.PerkLockedOut to remove check to see if another perk at the current rank is already granted
  7. Rewrite XGStrategySoldier.HasAvailablePerksToAssign to check only the flags in m_iEnergy to see if a promotion is available
  8. Rewrite XGSoldierUI.ShouldAutoAssignPerk to check for any selectable perks, and mark m_iEnergy flags if none available
  9. Rewrite UISoldierPromotion.UpdateAbilityData -- this is the primary interface to the actionscript functions


File Changes

XGStrategySoldier.m_iEnergy Default Value

This changes the default value of XGStrategySoldier.m_iEnergy from 100 to 0

Hex Change

In XcomStrategyGame, at offset 0x3BE3D9, change the value from 0x64 to 0x00

Decompiled Code

Original Code
defaultproperties
{
   ...
   m_iEnergy=100
   ...
}

New Code
defaultproperties
{
   ...
   m_iEnergy=0
   ...
}

XGFacility_Barracks.HealAndRest

This change prevents the assignment of a value to m_iEnergy in HealAndRest. This search/replace will occur three times. All three occurences must be changed.

Hex Change

XcomStrategyGame.upk >> XGFacility_Barracks.HealAndRest
original: 0F 19 00 49 28 00 00 09 00 E9 44 00 00 00 01 E9 44 00 00 2C 64
new: 0B 19 00 49 28 00 00 09 00 E9 44 00 00 00 01 E9 44 00 00 2C 64

Decompiled Code

Original Code
kSoldier.m_iEnergy = 100;

New Code
kSoldier.m_iEnergy
100

XGStrategySoldier.HasPerk

This change alters the HasPerk function from:

  • returning true if aUpgrades[perk] > 0

to

  • returning true if bit 0 of aUpgrades[perk] is 1.

This change was made for future support of equippable items that granting perks, and not preventing selection of perks from the perk tree based upon equipped items.

Hex Change

XcomStrategyGame.upk >> XGStrategySoldier.HasPerk
original: 98 45 00 00 AB 1F 00 00 00 00 00 00 96 45 00 00 00 00 00 00 00 00 00 00 98 45 00 00 00 00 00 00 84 04 00 00 1A 78 00 00 35 00 00 00 21 00 00 00 04 97 1A 00 98 45 00 00 35 A7 FB FF FF 7E FA FF FF 00 00 01 ED 44 00 00 25 16 04 3A 97 45 00 00 53
new: 98 45 00 00 AB 1F 00 00 00 00 00 00 96 45 00 00 00 00 00 00 00 00 00 00 98 45 00 00 00 00 00 00 84 04 00 00 1A 78 00 00 31 00 00 00 21 00 00 00 04 9A 9C 1A 00 98 45 00 00 35 A7 FB FF FF 7E FA FF FF 00 00 01 ED 44 00 00 26 16 26 16 04 0B 0B 53

Decompiled Code

Original Code
function bool HasPerk(int iPerk)
{
    return m_kChar.aUpgrades[iPerk] > 0;
    //return ReturnValue;    
}

New Code
function bool HasPerk(int iPerk)
{
    return (m_kChar.aUpgrades[iPerk] & 1) == 1;    
}

UISoldierPromotion.CheckForAutoAssignPerk

This change alters the response to ShouldAutoAssignPerk returning true. The function now simply refreshes the perk tree UI window.

Hex Change

XcomStrategyGame.upk >> UISoldierPromotion.CheckForAutoAssignPerk
original: CF 15 00 00 AB 1F 00 00 00 00 00 00 CE 15 00 00 00 00 00 00 00 00 00 00 CF 15 00 00 00 00 00 00 90 00 00 00 27 11 00 00 EE 00 00 00 CE 00 00 00 07 EB 00 82 84 81 19 1B 91 0F 00 00 00 00 00 00 4A 16 0A 00 96 00 00 00 00 1B 3D 14 00 00 00 00 00 00 16 16 18 3C 00 97 19 19 1B 91 0F 00 00 00 00 00 00 4A 16 0A 00 9C 00 00 00 00 1B 4C 0E 00 00 00 00 00 00 16 0B 00 66 41 00 00 00 1B D1 0F 00 00 00 00 00 00 4A 16 26 16 16 18 2B 00 19 1B 91 0F 00 00 00 00 00 00 4A 16 13 00 C0 3F 00 00 00 1B 7E 24 00 00 00 00 00 00 00 CF 15 00 00 16 16 19 1B 91 0F 00 00 00 00 00 00 4A 16 13 00 00 00 00 00 00 1B A4 23 00 00 00 00 00 00 00 CF 15 00 00 16 19 1B 91 0F 00 00 00 00 00 00 4A 16 0B 00 00 00 00 00 00 1B A5 23 00 00 00 00 00 00 25 16 1B 6E 00 00 00 00 00 00 00 16 04 0B 53
new: CF 15 00 00 AB 1F 00 00 00 00 00 00 CE 15 00 00 00 00 00 00 00 00 00 00 CF 15 00 00 00 00 00 00 90 00 00 00 27 11 00 00 E2 00 00 00 CE 00 00 00 07 72 00 82 19 01 AE 15 00 00 14 00 12 45 00 00 00 1B 05 11 00 00 00 00 00 00 2D 01 AF 15 00 00 16 18 2A 00 19 1B 91 0F 00 00 00 00 00 00 4A 16 13 00 C0 3F 00 00 00 1B 7E 24 00 00 00 00 00 00 00 CF 15 00 00 16 16 1B 0A 13 00 00 00 00 00 00 16 1B 8F 2B 00 00 00 00 00 00 16 06 00 00 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 04 0B 53

Decompiled Code

Original Code
simulated function CheckForAutoAssignPerk()
{
    local int iBranch;

    // End:0xEB
    if((!GetMgr().ISCONTROLLED() || GetMgr().Game().GetNumMissionsTaken() > 1) && GetMgr().ShouldAutoAssignPerk(iBranch))
    {
        GetMgr().SetAbilityTreeBranch(iBranch);
        GetMgr().SetAbilityTreeOption(0);
        AcceptDialogue();
    }
    //return;    
}

New Code
simulated function CheckForAutoAssignPerk()
{
    local int iBranch;

    // End:0xEB
	if(m_kSoldier.HasAvailablePerksToAssign(m_bPsiPromotion) && GetMgr().ShouldAutoAssignPerk(iBranch))
	{
		InitializeTree();
		UpdateAbilityData();

		// continue Loop
		goto 0x00;
	}
    //return;    
}                     

XGSoldierUI.OnAcceptPromotion

This change alters OnAcceptPromotion to set the appropriate flag in m_iEnergy when a valid promotion is accepted.

Hex Change

XcomStrategyGame.upk >> XGSoldierUI.OnAcceptPromotion
original: AE 3F 00 00 AB 1F 00 00 00 00 00 00 AC 3F 00 00 00 00 00 00 00 00 00 00 AE 3F 00 00 00 00 00 00 1E 02 00 00 C2 35 00 00 86 01 00 00 2A 01 00 00 0F 00 AD 3F 00 00 1B F2 0F 00 00 00 00 00 00 35 30 FF FF FF 7D FA FF FF 00 00 35 B4 F9 FF FF 74 FA FF FF 00 00 19 01 79 3F 00 00 09 00 EC 44 00 00 00 01 EC 44 00 00 1B 6F 0E 00 00 00 00 00 00 16 1B 70 0E 00 00 00 00 00 00 16 9A 01 7E 08 00 00 2C 02 16 16 07 BD 00 19 01 79 3F 00 00 2A 00 17 45 00 00 00 1B AD 21 00 00 00 00 00 00 00 AD 3F 00 00 1B 6F 0E 00 00 00 00 00 00 16 9A 01 7E 08 00 00 2C 02 16 16 04 28 07 6F 01 82 81 19 01 79 3F 00 00 13 00 97 45 00 00 00 1B 1B 11 00 00 00 00 00 00 00 AD 3F 00 00 16 16 18 0D 00 81 1B 14 22 00 00 00 00 00 00 16 16 16 19 01 79 3F 00 00 13 00 00 00 00 00 00 1B D3 10 00 00 00 00 00 00 00 AD 3F 00 00 16 19 1B 86 26 00 00 00 00 00 00 16 29 00 00 00 00 00 00 1B E1 21 00 00 00 00 00 00 19 1B 66 26 00 00 00 00 00 00 16 09 00 CB FA FF FF 00 01 CB FA FF FF 16 1B 29 2C 00 00 00 00 00 00 16 06 79 01 1B C3 21 00 00 00 00 00 00 16 04 27 04 3A AE 3F 00 00 53
new: AE 3F 00 00 AB 1F 00 00 00 00 00 00 AC 3F 00 00 00 00 00 00 00 00 00 00 AE 3F 00 00 00 00 00 00 1E 02 00 00 C2 35 00 00 8E 01 00 00 2A 01 00 00 0F 00 AD 3F 00 00 1B F2 0F 00 00 00 00 00 00 35 30 FF FF FF 7D FA FF FF 00 00 35 B4 F9 FF FF 74 FA FF FF 00 00 19 01 79 3F 00 00 09 00 EC 44 00 00 00 01 EC 44 00 00 1B 6F 0E 00 00 00 00 00 00 16 1B 70 0E 00 00 00 00 00 00 16 9A 01 7E 08 00 00 2C 02 16 16 07 80 01 81 19 01 79 3F 00 00 13 00 97 45 00 00 00 1B 1B 11 00 00 00 00 00 00 00 AD 3F 00 00 16 16 19 01 79 3F 00 00 13 00 00 00 00 00 00 1B D3 10 00 00 00 00 00 00 00 AD 3F 00 00 16 19 1B 86 26 00 00 00 00 00 00 16 29 00 00 00 00 00 00 1B E1 21 00 00 00 00 00 00 19 1B 66 26 00 00 00 00 00 00 16 09 00 CB FA FF FF 00 01 CB FA FF FF 16 1B 29 2C 00 00 00 00 00 00 16 0F 19 01 79 3F 00 00 09 00 E9 44 00 00 00 01 E9 44 00 00 9E 19 01 79 3F 00 00 09 00 E9 44 00 00 00 01 E9 44 00 00 94 26 92 93 1B 6F 0E 00 00 00 00 00 00 16 26 16 45 9A 01 7E 08 00 00 2C 02 16 02 00 2C 10 01 00 25 16 16 16 06 8A 01 1B C3 21 00 00 00 00 00 00 16 04 27 0B 53

Decompiled Code

Original Code
function bool OnAcceptPromotion()
{
    local int perk;

    perk = GetPerkInTree(m_kSoldier.m_kSoldier.kClass.eType, GetAbilityTreeBranch(), GetAbilityTreeOption(), m_iCurrentView == 2);
    // End:0xBD
    if(m_kSoldier.PerkLockedOut(perk, GetAbilityTreeBranch(), m_iCurrentView == 2))
    {
        return false;
    }
    // End:0x16F
    if(!m_kSoldier.HasPerk(perk) && !PreviousPerksToAssign())
    {
        m_kSoldier.GivePerk(perk);
        Sound().PlaySFX(SNDLIB().SFX_UI_ChoosePromotion);
        UpdateView();
    }
    // End:0x179
    else
    {
        PlayBadSound();
    }
    return true;
    //return ReturnValue;    
}

New Code
function bool OnAcceptPromotion()
{
    local int perk;

    perk = GetPerkInTree(m_kSoldier.m_kSoldier.kClass.eType, GetAbilityTreeBranch(), GetAbilityTreeOption(), m_iCurrentView == 2);

	// removed redundant check to PerkLockedOut

    if(!m_kSoldier.HasPerk(perk) && !PreviousPerksToAssign())
    {
        m_kSoldier.GivePerk(perk);
        Sound().PlaySFX(SNDLIB().SFX_UI_ChoosePromotion);
        UpdateView();
		
		//set flag indicating soldier has chosen ability at this rank
		m_kSoldier.m_iEnergy = m_kSoldier.m_iEnergy | (1 << (GetAbilityTreeBranch() + (m_iCurrentView == 2 ? 16 : 0)));
    }
    else
    {
        PlayBadSound();
    }
    return true;
    //return ReturnValue;    
}

XGStrategySoldier.PerkLockedOut

This changes the behavior of PerkLockedOut, removing the prior check testing if any other perk available at the current branch was already granted to a soldier, and replacing it with a check against the m_iEnergy flag.

Hex Change

XcomStrategyGame.upk >> XGStrategySoldier.PerkLockedOut
original: 1A 45 00 00 AB 1F 00 00 00 00 00 00 14 45 00 00 00 00 00 00 00 00 00 00 1A 45 00 00 00 00 00 00 21 01 00 00 D7 21 00 00 1E 01 00 00 D6 00 00 00 49 02 00 28 15 07 2F 00 2D 00 18 45 00 00 07 2C 00 97 00 19 45 00 00 1B 05 10 00 00 00 00 00 00 16 16 04 27 06 5B 00 07 49 00 97 00 19 45 00 00 1B 10 10 00 00 00 00 00 00 16 16 04 27 07 5B 00 9A 1B 10 10 00 00 00 00 00 00 16 25 16 04 27 0F 00 16 45 00 00 25 07 11 01 96 00 16 45 00 00 2C 02 16 0F 00 15 45 00 00 19 1B AE 21 00 00 00 00 00 00 16 55 00 4F FC FF FF 00 1B F2 0F 00 00 00 00 00 00 35 30 FF FF FF 7D FA FF FF 00 00 35 B4 F9 FF FF 74 FA FF FF 00 00 01 EC 44 00 00 00 19 45 00 00 00 16 45 00 00 2D 00 18 45 00 00 16 07 03 01 1B 1B 11 00 00 00 00 00 00 00 15 45 00 00 16 04 27 A5 00 16 45 00 00 16 06 66 00 04 28 04 3A 17 45 00 00 53
new: 1A 45 00 00 AB 1F 00 00 00 00 00 00 14 45 00 00 00 00 00 00 00 00 00 00 1A 45 00 00 00 00 00 00 21 01 00 00 D7 21 00 00 F2 00 00 00 D6 00 00 00 49 02 00 28 15 07 2F 00 2D 00 18 45 00 00 07 2C 00 97 00 19 45 00 00 1B 05 10 00 00 00 00 00 00 16 16 04 27 06 5B 00 07 49 00 97 00 19 45 00 00 1B 10 10 00 00 00 00 00 00 16 16 04 27 07 5B 00 9A 1B 10 10 00 00 00 00 00 00 16 25 16 04 27 07 91 00 97 9C 01 E9 44 00 00 94 26 92 93 00 19 45 00 00 26 16 45 2D 00 18 45 00 00 02 00 2C 10 01 00 25 16 16 16 25 16 04 27 04 28 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 04 3A 17 45 00 00 53

Decompiled Code

Original Code
function bool PerkLockedOut(int perk, int branch, optional bool isPsiPerk)
{
    local int iOptions, iPerk;

    isPsiPerk = false;
    // End:0x2F
    if(isPsiPerk)
    {
        // End:0x2C
        if(branch > (GetPsiRank()))
        {
            return true;
        }
    }
    // End:0x5B
    else
    {
        // End:0x49
        if(branch > (GetRank()))
        {
            return true;
        }
        // End:0x5B
        if((GetRank()) == 0)
        {
            return true;
        }
    }
    iOptions = 0;
    J0x66:
    // End:0x111 [Loop If]
    if(iOptions < 2)
    {
        iPerk = perkMgr().GetPerkInTree(m_kSoldier.kClass.eType, branch, iOptions, isPsiPerk);
        // End:0x103
        if(HasPerk(iPerk))
        {
            return true;
        }
        ++ iOptions;
        // [Loop Continue]
        goto J0x66;
    }
    return false;
    //return ReturnValue;    
}

New Code
function bool PerkLockedOut(int perk, int branch, optional bool isPsiPerk)
{
    local int iOptions, iPerk;

    isPsiPerk = false;
    // End:0x2F
    if(isPsiPerk)
    {
        // End:0x2C
        if(branch > (GetPsiRank()))
        {
            return true;
        }
    }
    // End:0x5B
    else
    {
        // End:0x49
        if(branch > (GetRank()))
        {
            return true;
        }
        // End:0x5B
        if((GetRank()) == 0)
        {
            return true;
        }
    }
    if((m_iEnergy & (1 << ((branch-1) + (isPsiPerk ? 16 : 0)))) > 0)
	{
		return true;
	}
    return false;
    //return ReturnValue;    
}

XGStrategySoldier.HasAvailablePerksToAssign

This changes the function HasAvailablePerksToAssign so that it checks the flags in m_iEnergy instead of looking at perks assigned to a soldier.

It also adds support for future mod extensions that will allow the use of perks defined in branches 8-11.

Hex Change

XcomStrategyGame.upk >> XGStrategySoldier.HasAvailablePerksToAssign
original: 13 45 00 00 AB 1F 00 00 00 00 00 00 0E 45 00 00 00 00 00 00 00 00 00 00 13 45 00 00 00 00 00 00 EF 00 00 00 70 1D 00 00 2A 02 00 00 B6 01 00 00 49 02 00 28 15 07 29 00 82 9A 1B 10 10 00 00 00 00 00 00 16 25 16 18 0E 00 9A 1B 05 10 00 00 00 00 00 00 16 25 16 16 04 28 07 3B 01 2D 00 13 45 00 00 07 38 01 1B 1F 11 00 00 00 00 00 00 16 0F 00 11 45 00 00 26 07 38 01 96 00 11 45 00 00 92 1B 05 10 00 00 00 00 00 00 16 26 16 16 07 7C 00 99 00 11 45 00 00 2C 04 16 06 2A 01 0F 00 10 45 00 00 25 07 2A 01 96 00 10 45 00 00 2C 02 16 0F 00 0F 45 00 00 19 1B AE 21 00 00 00 00 00 00 16 27 00 4F FC FF FF 00 1B F2 0F 00 00 00 00 00 00 1B B5 0E 00 00 00 00 00 00 16 00 11 45 00 00 00 10 45 00 00 27 16 07 1C 01 82 81 1B AD 21 00 00 00 00 00 00 00 0F 45 00 00 00 11 45 00 00 27 16 16 18 16 00 81 1B 1B 11 00 00 00 00 00 00 00 0F 45 00 00 16 16 16 04 27 A5 00 10 45 00 00 16 06 87 00 A5 00 11 45 00 00 16 06 4E 00 06 1D 02 0F 00 11 45 00 00 26 07 1D 02 96 00 11 45 00 00 92 1B 10 10 00 00 00 00 00 00 16 26 16 16 0F 00 10 45 00 00 25 07 0F 02 96 00 10 45 00 00 2C 02 16 0F 00 0F 45 00 00 19 1B AE 21 00 00 00 00 00 00 16 27 00 4F FC FF FF 00 1B F2 0F 00 00 00 00 00 00 1B B5 0E 00 00 00 00 00 00 16 00 11 45 00 00 00 10 45 00 00 4A 16 07 01 02 82 81 1B AD 21 00 00 00 00 00 00 00 0F 45 00 00 00 11 45 00 00 4A 16 16 18 16 00 81 1B 1B 11 00 00 00 00 00 00 00 0F 45 00 00 16 16 16 04 27 A5 00 10 45 00 00 16 06 6C 01 A5 00 11 45 00 00 16 06 46 01 04 28 04 3A 12 45 00 00 53
new: 13 45 00 00 AB 1F 00 00 00 00 00 00 0E 45 00 00 00 00 00 00 00 00 00 00 13 45 00 00 00 00 00 00 EF 00 00 00 70 1D 00 00 F2 01 00 00 B6 01 00 00 49 02 00 28 15 07 17 01 2D 00 13 45 00 00 07 6C 00 1B 1F 11 00 00 00 00 00 00 16 0F 00 11 45 00 00 26 07 6C 00 98 00 11 45 00 00 1B 05 10 00 00 00 00 00 00 16 16 07 5E 00 81 1B AD 21 00 00 00 00 00 00 26 00 11 45 00 00 27 16 16 04 27 A5 00 11 45 00 00 16 06 2A 00 0F 00 10 45 00 00 1B 10 10 00 00 00 00 00 00 16 07 A5 00 99 00 10 45 00 00 2C 01 16 07 A5 00 81 1B AD 21 00 00 00 00 00 00 26 2C 08 27 16 16 04 27 07 CA 00 99 00 10 45 00 00 2C 03 16 07 CA 00 81 1B AD 21 00 00 00 00 00 00 26 2C 09 27 16 16 04 27 07 EF 00 99 00 10 45 00 00 2C 04 16 07 EF 00 81 1B AD 21 00 00 00 00 00 00 26 2C 0A 27 16 16 04 27 07 14 01 99 00 10 45 00 00 2C 05 16 07 14 01 81 1B AD 21 00 00 00 00 00 00 26 2C 0B 27 16 16 04 27 06 64 01 0F 00 11 45 00 00 26 07 64 01 98 00 11 45 00 00 1B 10 10 00 00 00 00 00 00 16 16 07 56 01 81 1B AD 21 00 00 00 00 00 00 26 00 11 45 00 00 4A 16 16 04 27 A5 00 11 45 00 00 16 06 22 01 04 28 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 04 3A 12 45 00 00 53

Decompiled Code

Original Code
function bool HasAvailablePerksToAssign(optional bool CheckForPsiPromotion)
{
    local int iIndex, jIndex, iPerk;

    CheckForPsiPromotion = false;
    // End:0x29
    if(((GetRank()) == 0) && (GetPsiRank()) == 0)
    {
        return false;
    }
    // End:0x13B
    if(CheckForPsiPromotion)
    {
        // End:0x138
        if(HasPsiGift())
        {
            iIndex = 1;
            J0x4E:
            // End:0x138 [Loop If]
            if(iIndex < ((GetPsiRank()) + 1))
            {
                // End:0x7C
                if(iIndex >= 4)
                {
                }
                // End:0x12A
                else
                {
                    jIndex = 0;
                    J0x87:
                    // End:0x12A [Loop If]
                    if(jIndex < 2)
                    {
                        iPerk = perkMgr().GetPerkInTree(GetClass(), iIndex, jIndex, true);
                        // End:0x11C
                        if(!PerkLockedOut(iPerk, iIndex, true) && !HasPerk(iPerk))
                        {
                            return true;
                        }
                        ++ jIndex;
                        // [Loop Continue]
                        goto J0x87;
                    }
                }
                ++ iIndex;
                // [Loop Continue]
                goto J0x4E;
            }
        }
    }
    // End:0x21D
    else
    {
        iIndex = 1;
        J0x146:
        // End:0x21D [Loop If]
        if(iIndex < ((GetRank()) + 1))
        {
            jIndex = 0;
            J0x16C:
            // End:0x20F [Loop If]
            if(jIndex < 2)
            {
                iPerk = perkMgr().GetPerkInTree(GetClass(), iIndex, jIndex);
                // End:0x201
                if(!PerkLockedOut(iPerk, iIndex) && !HasPerk(iPerk))
                {
                    return true;
                }
                ++ jIndex;
                // [Loop Continue]
                goto J0x16C;
            }
            ++ iIndex;
            // [Loop Continue]
            goto J0x146;
        }
    }
    return false;
    //return ReturnValue;    
}

New Code
function bool HasAvailablePerksToAssign(optional bool CheckForPsiPromotion)
{
    local int iIndex, jIndex, iPerk;

    CheckForPsiPromotion = false;
	
    if(CheckForPsiPromotion)
    {
        // End:0x138
        if(HasPsiGift())
        {
            iIndex = 1;
            J0x4E:
            // End:0x138 [Loop If]
            if(iIndex <= GetPsiRank())
            {
				if(!PerkLockedOut(1, iIndex, true))
				{
					return true;
				}
                ++ iIndex;
                // [Loop Continue]
                goto J0x4E;
            }
        }
		// non-psi gift increases, put in Psi tree UI
		jIndex = GetRank();
		if(jIndex >= 1)  // first ability increase at level 1 
		{
			if(!PerkLockedOut(1,8,false) //check if bit 8 has been set
			{
				return true;
			}
		}
		if(jIndex >= 3)  // second ability increase at level 3 
		{
			if(!PerkLockedOut(1,9,false) //check if bit 9 has been set
			{
				return true;
			}
		}
		if(jIndex >= 4)  // third ability increase at level 4 
		{
			if(!PerkLockedOut(1,10,false) //check if bit 10 has been set
			{
				return true;
			}
		}
		if(jIndex >= 6)  // fourth ability increase at level 6 
		{
			if(!PerkLockedOut(1,11,false) //check if bit 11 has been set
			{
				return true;
			}
		}
    }
    // End:0x21D
    else
    {
        iIndex = 1;
        J0x146:
        // End:0x21D [Loop If]
        if(iIndex <= GetRank())
        {
            // explicit check to see if this level is promotable
			if(!PerkLockedOut(1, iIndex, true))
			{
				return true;
			}
            ++ iIndex;
            // [Loop Continue]
            goto J0x146;
        }
    }
    return false;
    //return ReturnValue;    
}

XGSoldierUI.ShouldAutoAssignPerk

This changes the test for auto-assigning a perk. It no longer tests whether row0 = row1. Instead it loops over all perks in the current branch, searching for a valid (non-zero) perk that the soldier does not already have. If it finds no valid perks then it marks the bit flag in m_iEnergy to 1 for the current branch and returns true.

Hex Change

XcomStrategyGame.upk >> XGSoldierUI.ShouldAutoAssignPerk
original: C1 3F 00 00 AB 1F 00 00 00 00 00 00 BC 3F 00 00 00 00 00 00 00 00 00 00 C1 3F 00 00 00 00 00 00 8C 02 00 00 22 3F 00 00 86 01 00 00 0E 01 00 00 0F 00 BF 3F 00 00 1B 6F 0E 00 00 00 00 00 00 16 0F 00 BE 3F 00 00 19 1B AE 21 00 00 00 00 00 00 16 65 00 4F FC FF FF 00 1B F2 0F 00 00 00 00 00 00 35 30 FF FF FF 7D FA FF FF 00 00 35 B4 F9 FF FF 74 FA FF FF 00 00 19 01 79 3F 00 00 09 00 EC 44 00 00 00 01 EC 44 00 00 00 BF 3F 00 00 25 9A 01 7E 08 00 00 2C 02 16 16 0F 00 BD 3F 00 00 19 1B AE 21 00 00 00 00 00 00 16 65 00 4F FC FF FF 00 1B F2 0F 00 00 00 00 00 00 35 30 FF FF FF 7D FA FF FF 00 00 35 B4 F9 FF FF 74 FA FF FF 00 00 19 01 79 3F 00 00 09 00 EC 44 00 00 00 01 EC 44 00 00 00 BF 3F 00 00 26 9A 01 7E 08 00 00 2C 02 16 16 07 6A 01 82 82 9A 00 BE 3F 00 00 00 BD 3F 00 00 16 18 0D 00 99 00 BE 3F 00 00 25 16 16 18 0B 00 1B 1D 05 00 00 00 00 00 00 16 16 0F 48 C1 3F 00 00 00 BF 3F 00 00 04 27 0F 48 C1 3F 00 00 1D FF FF FF FF 04 28 04 3A C0 3F 00 00 53
new: C1 3F 00 00 AB 1F 00 00 00 00 00 00 BC 3F 00 00 00 00 00 00 00 00 00 00 C1 3F 00 00 00 00 00 00 8C 02 00 00 22 3F 00 00 82 01 00 00 0E 01 00 00 0F 00 BF 3F 00 00 1B 6F 0E 00 00 00 00 00 00 16 0F 00 BE 3F 00 00 25 07 09 01 96 00 BE 3F 00 00 2C 03 16 0F 00 BD 3F 00 00 19 1B AE 21 00 00 00 00 00 00 16 65 00 4F FC FF FF 00 1B F2 0F 00 00 00 00 00 00 35 30 FF FF FF 7D FA FF FF 00 00 35 B4 F9 FF FF 74 FA FF FF 00 00 19 01 79 3F 00 00 09 00 EC 44 00 00 00 01 EC 44 00 00 00 BF 3F 00 00 00 BE 3F 00 00 9A 01 7E 08 00 00 2C 02 16 16 07 FA 00 97 00 BD 3F 00 00 25 16 07 FA 00 81 19 01 79 3F 00 00 13 00 97 45 00 00 00 1B 1B 11 00 00 00 00 00 00 00 BD 3F 00 00 16 16 04 28 A1 00 BE 3F 00 00 26 16 06 1F 00 0F 19 01 79 3F 00 00 09 00 E9 44 00 00 00 01 E9 44 00 00 9E 19 01 79 3F 00 00 09 00 E9 44 00 00 00 01 E9 44 00 00 94 26 92 93 00 BF 3F 00 00 26 16 45 9A 01 7E 08 00 00 2C 02 16 02 00 2C 10 01 00 25 16 16 16 04 27 0B 0B 0B 0B 0B 0B 0B 04 3A C0 3F 00 00 53

Decompiled Code

Original Code
function bool ShouldAutoAssignPerk(out int iBranchToAssign)
{
    local int iBranch, perkA, perkB;

    iBranch = GetAbilityTreeBranch();
    perkA = perkMgr().GetPerkInTree(m_kSoldier.m_kSoldier.kClass.eType, iBranch, 0, m_iCurrentView == 2);
    perkB = perkMgr().GetPerkInTree(m_kSoldier.m_kSoldier.kClass.eType, iBranch, 1, m_iCurrentView == 2);
    // End:0x16A
    if(((perkA == perkB) && perkA >= 0) && CanPromote())
    {
        iBranchToAssign = iBranch;
        return true;
    }
    iBranchToAssign = -1;
    return false;
    //return ReturnValue;    
}

New Code
function bool ShouldAutoAssignPerk(out int iBranchToAssign)
{
    local int iBranch, perkA, perkB;

    iBranch = GetAbilityTreeBranch();
	
	perkA = 0; // use as loop counter
	
	if(perkA < 3)
	{
		perkB = perkMgr().GetPerkInTree(m_kSoldier.m_kSoldier.kClass.eType, iBranch, perkA, m_iCurrentView == 2)
		if(perkB > 0)
		{
			if(!m_kSoldier.HasPerk(perkB))
			{
				return false;
			}
		}
	}
	
	m_kSoldier.m_iEnergy = m_kSoldier.m_iEnergy | (1 << (iBranch - 1 + (m_iCurrentView == 2 ? 16 : 0)));
    iBranchToAssign = -1;
    return true;
    //return ReturnValue;    
}

UISoldierPromotion.UpdateAbilityData

This function has been extensively rewritten. It is the primary interface to the actionscript that implements the perk tree UI.

This new function does the following:

  1. Allows display of up to 3 ability icons
  2. Suppresses display of ability icons above the soldier's current rank
  3. Suppresses display of ability icons that GetPerkInTree returns 0 for
  4. Sets the Column mode:
    1. mode = 0 : already promoted - cyan border, icons are highlighted if soldier HasPerk, dimmed if !HasPerk (shows previously selected perks)
    2. mode = 1 : current promotion - yellow border, icons are highlighted if soldier !HasPerk, dimmed if soldier HasPerk (shows perks that can be selected)
    3. mode = 2 : future promotion - dimmed yellow border, icons are invisible (shows soldier has reached rank, but cannot select perks until previous selection is made)
    4. mode = 3 : above rank - border and icons invisible (if soldier has not reached the rank necessary)
  5. Enables display of rank string for Psionic ranks

Hex Change

XcomStrategyGame.upk >> BLANK
original: E5 15 00 00 AB 1F 00 00 00 00 00 00 DE 15 00 00 00 00 00 00 00 00 00 00 E5 15 00 00 00 00 00 00 4A 01 00 00 90 27 00 00 B9 05 00 00 09 04 00 00 07 45 00 2D 01 AF 15 00 00 0F 00 E2 15 00 00 2C 03 0F 00 E1 15 00 00 19 01 AE 15 00 00 0A 00 02 45 00 00 00 1B 05 10 00 00 00 00 00 00 16 06 7A 00 0F 00 E2 15 00 00 2C 07 0F 00 E1 15 00 00 19 01 AE 15 00 00 0A 00 00 45 00 00 00 1B 10 10 00 00 00 00 00 00 16 14 2D 00 DF 15 00 00 27 0F 00 E5 15 00 00 25 07 AB 05 96 00 E5 15 00 00 00 E2 15 00 00 16 14 2D 00 E0 15 00 00 27 0F 00 E4 15 00 00 25 07 F8 02 96 00 E4 15 00 00 2C 02 16 07 18 01 97 00 E4 15 00 00 25 16 07 F3 00 9A 00 E5 15 00 00 25 16 06 F8 02 06 18 01 07 18 01 82 F2 2D 01 AF 15 00 00 28 16 18 0E 00 9A 00 E5 15 00 00 2C 05 16 16 06 F8 02 0F 00 E3 15 00 00 19 1B 91 0F 00 00 00 00 00 00 4A 16 6D 00 D3 3F 00 00 00 1B F2 0F 00 00 00 00 00 00 35 30 FF FF FF 7D FA FF FF 00 00 35 B4 F9 FF FF 74 FA FF FF 00 00 19 01 AE 15 00 00 09 00 EC 44 00 00 00 01 EC 44 00 00 92 00 E5 15 00 00 26 16 00 E4 15 00 00 2D 01 AF 15 00 00 16 07 F6 01 84 19 01 AE 15 00 00 13 00 97 45 00 00 00 1B 1B 11 00 00 00 00 00 00 00 E3 15 00 00 16 18 15 00 99 00 E5 15 00 00 00 E1 15 00 00 16 16 14 2D 00 E0 15 00 00 28 07 5D 02 99 00 E5 15 00 00 00 E1 15 00 00 16 1B 19 02 00 00 00 00 00 00 00 E5 15 00 00 00 E4 15 00 00 1F 75 6E 6B 6E 6F 77 6E 00 19 01 AE 15 00 00 13 00 97 45 00 00 00 1B 1B 11 00 00 00 00 00 00 00 E3 15 00 00 16 16 06 EA 02 1B 19 02 00 00 00 00 00 00 00 E5 15 00 00 00 E4 15 00 00 12 20 A1 FE FF FF 34 00 1B FA FF FF 00 1B F1 0F 00 00 00 00 00 00 00 E3 15 00 00 19 1B 91 0F 00 00 00 00 00 00 4A 16 0A 00 D8 00 00 00 00 1B AE 21 00 00 00 00 00 00 16 16 19 01 AE 15 00 00 13 00 97 45 00 00 00 1B 1B 11 00 00 00 00 00 00 00 E3 15 00 00 16 16 A3 00 E4 15 00 00 16 06 BF 00 07 6A 04 2D 00 E0 15 00 00 07 DA 03 2D 00 DF 15 00 00 14 2D 00 DF 15 00 00 28 19 1B 91 0F 00 00 00 00 00 00 4A 16 16 00 00 00 00 00 00 1B A4 23 00 00 00 00 00 00 92 00 E5 15 00 00 26 16 16 1B 28 02 00 00 00 00 00 00 00 E5 15 00 00 EB 19 19 2E 92 FE FF FF 19 12 20 FD FE FF FF 0A 00 3B FB FF FF 00 1C 92 FD FF FF 16 09 00 25 FB FF FF 00 01 25 FB FF FF 09 00 01 FB FF FF 00 01 01 FB FF FF 18 00 EC F9 FF FF 00 1B 12 10 00 00 00 00 00 00 92 00 E5 15 00 00 26 16 4A 4A 16 16 26 16 06 67 04 1B 28 02 00 00 00 00 00 00 00 E5 15 00 00 EB 19 19 2E 92 FE FF FF 19 12 20 FD FE FF FF 0A 00 3B FB FF FF 00 1C 92 FD FF FF 16 09 00 25 FB FF FF 00 01 25 FB FF FF 09 00 01 FB FF FF 00 01 01 FB FF FF 18 00 EC F9 FF FF 00 1B 12 10 00 00 00 00 00 00 92 00 E5 15 00 00 26 16 4A 4A 16 16 2C 02 16 06 9D 05 07 11 05 99 00 E5 15 00 00 00 E1 15 00 00 16 1B 28 02 00 00 00 00 00 00 00 E5 15 00 00 EB 19 19 2E 92 FE FF FF 19 12 20 FD FE FF FF 0A 00 3B FB FF FF 00 1C 92 FD FF FF 16 09 00 25 FB FF FF 00 01 25 FB FF FF 09 00 01 FB FF FF 00 01 01 FB FF FF 18 00 EC F9 FF FF 00 1B 12 10 00 00 00 00 00 00 92 00 E5 15 00 00 26 16 4A 4A 16 16 2C 03 16 06 9D 05 1B 28 02 00 00 00 00 00 00 00 E5 15 00 00 EB 19 19 2E 92 FE FF FF 19 12 20 FD FE FF FF 0A 00 3B FB FF FF 00 1C 92 FD FF FF 16 09 00 25 FB FF FF 00 01 25 FB FF FF 09 00 01 FB FF FF 00 01 01 FB FF FF 18 00 EC F9 FF FF 00 1B 12 10 00 00 00 00 00 00 92 00 E5 15 00 00 26 16 4A 4A 16 16 25 16 A3 00 E5 15 00 00 16 06 91 00 1C E8 15 00 00 4A 16 04 0B 53
new: E5 15 00 00 AB 1F 00 00 00 00 00 00 DE 15 00 00 00 00 00 00 00 00 00 00 E5 15 00 00 00 00 00 00 4A 01 00 00 90 27 00 00 25 05 00 00 09 04 00 00 07 45 00 2D 01 AF 15 00 00 0F 00 E2 15 00 00 2C 03 0F 00 E1 15 00 00 19 01 AE 15 00 00 0A 00 02 45 00 00 00 1B 05 10 00 00 00 00 00 00 16 06 7A 00 0F 00 E2 15 00 00 2C 07 0F 00 E1 15 00 00 19 01 AE 15 00 00 0A 00 00 45 00 00 00 1B 10 10 00 00 00 00 00 00 16 14 2D 00 DF 15 00 00 27 0F 00 E5 15 00 00 25 07 AA 03 96 00 E5 15 00 00 2C 07 16 07 D6 00 97 92 00 E5 15 00 00 26 16 00 E1 15 00 00 16 0F 00 E4 15 00 00 2C 03 14 2D 00 DF 15 00 00 28 06 FF 02 14 2D 00 E0 15 00 00 9A 9C 19 01 AE 15 00 00 09 00 E9 44 00 00 00 01 E9 44 00 00 94 26 92 00 E5 15 00 00 45 2D 01 AF 15 00 00 02 00 2C 10 01 00 25 16 16 16 25 16 07 84 02 2D 00 DF 15 00 00 0F 00 E4 15 00 00 25 07 84 02 96 00 E4 15 00 00 2C 04 16 0F 00 E3 15 00 00 19 1B 91 0F 00 00 00 00 00 00 4A 16 6D 00 D3 3F 00 00 00 1B F2 0F 00 00 00 00 00 00 35 30 FF FF FF 7D FA FF FF 00 00 35 B4 F9 FF FF 74 FA FF FF 00 00 19 01 AE 15 00 00 09 00 EC 44 00 00 00 01 EC 44 00 00 92 00 E5 15 00 00 26 16 00 E4 15 00 00 2D 01 AF 15 00 00 16 07 76 02 97 00 E3 15 00 00 25 16 1B 19 02 00 00 00 00 00 00 00 E5 15 00 00 00 E4 15 00 00 12 20 A1 FE FF FF 34 00 1B FA FF FF 00 1B F1 0F 00 00 00 00 00 00 00 E3 15 00 00 19 1B 91 0F 00 00 00 00 00 00 4A 16 0A 00 D8 00 00 00 00 1B AE 21 00 00 00 00 00 00 16 16 19 01 AE 15 00 00 13 00 97 45 00 00 00 1B 1B 11 00 00 00 00 00 00 00 E3 15 00 00 16 16 A3 00 E4 15 00 00 16 06 3C 01 07 F4 02 2D 00 E0 15 00 00 07 E5 02 2D 00 DF 15 00 00 0F 00 E4 15 00 00 26 14 2D 00 DF 15 00 00 28 19 1B 91 0F 00 00 00 00 00 00 4A 16 16 00 00 00 00 00 00 1B A4 23 00 00 00 00 00 00 92 00 E5 15 00 00 26 16 16 06 F1 02 0F 00 E4 15 00 00 2C 02 06 FF 02 0F 00 E4 15 00 00 25 1B 28 02 00 00 00 00 00 00 00 E5 15 00 00 EB 19 19 2E 92 FE FF FF 19 12 20 FD FE FF FF 0A 00 3B FB FF FF 00 1C 92 FD FF FF 16 09 00 25 FB FF FF 00 01 25 FB FF FF 09 00 01 FB FF FF 00 01 01 FB FF FF 18 00 EC F9 FF FF 00 1B 12 10 00 00 00 00 00 00 92 00 E5 15 00 00 26 16 28 2D 01 AF 15 00 00 16 16 00 E4 15 00 00 16 A3 00 E5 15 00 00 16 06 91 00 1C E8 15 00 00 4A 16 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 04 0B 53

Decompiled Code

Original Code
protected simulated function UpdateAbilityData()
{
    local int column, Row, perk, abilityColumns, SoldierRank;

    local bool isColumnPromotable, isFirstPromotableColumn;

    // End:0x45
    if(m_bPsiPromotion)
    {
        abilityColumns = 3;
        SoldierRank = m_kSoldier.GetPsiRank();
    }
    // End:0x7A
    else
    {
        abilityColumns = 7;
        SoldierRank = m_kSoldier.GetRank();
    }
    isFirstPromotableColumn = true;
    column = 0;
    J0x91:
    // End:0x5AB [Loop If]
    if(column < abilityColumns)
    {
        isColumnPromotable = true;
        Row = 0;
        J0xBF:
        // End:0x2F8 [Loop If]
        if(Row < 3)
        {
            // End:0x118
            if(Row < 0)
            {
                // End:0xF3
                if(column == 0)
                {
                    // [Explicit Break]
                    goto J0x2F8;
                }
                // End:0x118
                else
                {
                    // End:0x118
                    if((m_bPsiPromotion == false) && column == 5)
                    {
                        // [Explicit Break]
                        goto J0x2F8;
                    }
                }
            }
            perk = GetMgr().GetPerkInTree(m_kSoldier.m_kSoldier.kClass.eType, column + 1, Row, m_bPsiPromotion);
            // End:0x1F6
            if(m_kSoldier.HasPerk(perk) || column >= SoldierRank)
            {
                isColumnPromotable = false;
            }
            // End:0x25D
            if(column >= SoldierRank)
            {
                AS_SetAbilityIcon(column, Row, "unknown", m_kSoldier.HasPerk(perk));
            }
            // End:0x2EA
            else
            {
                AS_SetAbilityIcon(column, Row, class'UIUtilities'.static.GetPerkIconLabel(perk, GetMgr().perkMgr()), m_kSoldier.HasPerk(perk));
            }
            ++ Row;
            J0x2F8:
            // [Loop Continue]
            goto J0xBF;
        }
        // End:0x46A
        if(isColumnPromotable)
        {
            // End:0x3DA
            if(isFirstPromotableColumn)
            {
                isFirstPromotableColumn = false;
                GetMgr().SetAbilityTreeBranch(column + 1);
                AS_SetColumnData(column, Caps(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.GetRankString(column + 1)), 1);
            }
            // End:0x467
            else
            {
                AS_SetColumnData(column, Caps(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.GetRankString(column + 1)), 2);
            }
        }
        // End:0x59D
        else
        {
            // End:0x511
            if(column >= SoldierRank)
            {
                AS_SetColumnData(column, Caps(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.GetRankString(column + 1)), 3);
            }
            // End:0x59D
            else
            {
                AS_SetColumnData(column, Caps(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.GetRankString(column + 1)), 0);
            }
        }
        ++ column;
        // [Loop Continue]
        goto J0x91;
    }
    RealizeSelected();
    //return;    
}

New Code
protected simulated function UpdateAbilityData()
{
    local int column, Row, perk, abilityColumns, SoldierRank;

    local bool isColumnPromotable, isFirstPromotableColumn;

    // End:0x45
    if(m_bPsiPromotion)
    {
        abilityColumns = 3;
        SoldierRank = m_kSoldier.GetPsiRank();
    }
    // End:0x7A
    else
    {
        abilityColumns = 7;
        SoldierRank = m_kSoldier.GetRank();
    }
    isFirstPromotableColumn = true;
    column = 0;
    J0x91:
    // End:0x5AB [Loop If]
    if(column < 7) // loop all the way up, in order to suppress brackets above current rank
    {
		if(column+1 > SoldierRank) // turn off columns above current soldier rank
		{
			row = 3;
			isFirstPromotableColumn = false;
		}
		else
		{

			// new check to see if column is promotable... based on data stored in m_iEnergy
			// regular rank promotions are marked in bits 0-6 (1 means has accepted promotion at that rank)
			// psi rank promotions are marked in bits 16-18
			isColumnPromotable = ((m_kSoldier.m_iEnergy & (1 << (column + (m_bPsiPromotion ? 16 : 0)))) == 0);


			// only display icons up to the first promotable column
			if(isFirstPromotableColumn)
			{
				Row = 0;
				J0xBF:
				// End:0x2F8 [Loop If]
				if(Row < 4)
				{
					// keep call to retrieve perk, as before
					perk = GetMgr().GetPerkInTree(m_kSoldier.m_kSoldier.kClass.eType, column + 1, Row, m_bPsiPromotion);

					// only display icons for spots with valid perks
					if(perk > 0) 
					{
						AS_SetAbilityIcon(column, Row, class'UIUtilities'.static.GetPerkIconLabel(perk, GetMgr().perkMgr()), m_kSoldier.HasPerk(perk));
					}
					++ Row;
					// loop continue
					goto J0xBF;
				}
			}
			//temporarily re-use the row variable to store the AS_SetColumnData mode
			if(isColumPromotable)
			{
				if(isFirstPromotableColumn)
				{
					// mode 1 -- column is highlighted and active
					row = 1; 
					
					// activate this branch -- makes it valid to select perks
					isFirstPromotableColumn = false;
					GetMgr().SetAbilityTreeBranch(column + 1);
				}
				else
				{
					// mode 2 -- column is faded and inactive
					row = 2; 
				}
			}
			else
			{
				// mode 0 -- column has been promoted -- column highlighting is off
				row = 0; 
			}
		}
		AS_SetColumnData(column, Caps(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.GetRankString(column + 1), false, m_bPsiPromotion), row);
        ++ column;
        // [Loop Continue]
        goto J0x91;
    }
    RealizeSelected();
    //return;    
}

References

Referred to by this article:

That refer to this article: