Jump to content

UI Mod - Bigger Perk Tree


Amineri

Recommended Posts

For my own purposed I've converted Peasly Wellbott's mod into UPKmodder-compatible files. I'm attaching them here in case you find them useful (like if a patch comes out and you want to update the mod).

 

 

 

MODFILEVERSION=4
UPKFILE=XComStrategyGame.upk
GUID=31 9C 3B 3F 9C 5D E4 40 AB AF 92 8E 25 65 74 F2 // XComStrategyGame_EW_patch1.upk
FUNCTION=DismissSoldierActionCallback@XGSoldierUI

//Prevents the Dismissal call from pulling soldiers out of the labs prematurely

[BEFORE_HEX]
[HEADER]

[/HEADER]
[code]
//if((GENELABS()) != none)
07 9C 00 77 1B F2 10 00 00 00 00 00 00 16 2A 16 

	//GENELABS().RemoveSoldierFromGeneLabs(kSoldier)
	19 1B F2 10 00 00 00 00 00 00 16 13 00 9A 35 00 00 00 1B 37 29 00 00 00 00 00 00 00 A8 4C 00 00 16 

//if((CYBERNETICSLAB()) != none)
07 D5 00 77 1B 9A 07 00 00 00 00 00 00 16 2A 16 

[/CODE]
[/BEFORE_HEX]


[AFTER_HEX]
[HEADER]

[/HEADER]
[code]
//if((GENELABS()) != none)
07 9C 00 9A 1B F2 10 00 00 00 00 00 00 16 2A 16 

	//GENELABS().RemoveSoldierFromGeneLabs(kSoldier)
	19 1B F2 10 00 00 00 00 00 00 16 13 00 9A 35 00 00 00 1B 37 29 00 00 00 00 00 00 00 A8 4C 00 00 16 

//if((CYBERNETICSLAB()) != none)
07 D5 00 9A 1B 9A 07 00 00 00 00 00 00 16 2A 16 

[/CODE]
[/AFTER_HEX] 
MODFILEVERSION=4
UPKFILE=XComStrategyGame.upk
GUID=31 9C 3B 3F 9C 5D E4 40 AB AF 92 8E 25 65 74 F2 // XComStrategyGame_EW_patch1.upk
FUNCTION=UpdateMainMenu@XGSoldierUI

[BEFORE_HEX]
[HEADER]

[/HEADER]
[code]
	//kOption.iState = 1
	0F 35 C3 FA FF FF B7 F9 FF FF 00 01 00 07 4D 00 00 26  

	//kOption.strHelp = m_strNoDismissWhileAugmenting
	0F 35 07 F9 FF FF B7 F9 FF FF 00 01 00 07 4D 00 00 01 41 4C 00 00 

//else
06 96 0B 

	//if(m_kSoldier.IsInGeneticsLab())
	07 5F 0A 19 01 78 4C 00 00 0A 00 0D 53 00 00 00 1B E9 17 00 00 00 00 00 00 16 

		//kOption.iState = 1
		0F 35 C3 FA FF FF B7 F9 FF FF 00 01 00 07 4D 00 00 26 


[/CODE]
[/BEFORE_HEX]


[AFTER_HEX]
[HEADER]

[/HEADER]
[code]
	//kOption.iState = 0 
	0F 35 C3 FA FF FF B7 F9 FF FF 00 01 00 07 4D 00 00 25  

	//kOption.strHelp = m_strNoDismissWhileAugmenting
	0F 35 07 F9 FF FF B7 F9 FF FF 00 01 00 07 4D 00 00 01 41 4C 00 00 

//else
06 96 0B 

	//if(m_kSoldier.IsInGeneticsLab())
	07 5F 0A 19 01 78 4C 00 00 0A 00 0D 53 00 00 00 1B E9 17 00 00 00 00 00 00 16 

		//kOption.iState = 0
		0F 35 C3 FA FF FF B7 F9 FF FF 00 01 00 07 4D 00 00 25 

[/CODE]
[/AFTER_HEX] 
MODFILEVERSION=4
UPKFILE=XComStrategyGame.upk
GUID=31 9C 3B 3F 9C 5D E4 40 AB AF 92 8E 25 65 74 F2 // XComStrategyGame_EW_patch1.upk
FUNCTION=GiveBottomTreePerks@XGStrategySoldier

[BEFORE_HEX]
[HEADER]
38 01 00 00 E8 00 00 00 
[/HEADER]
[code]
//iOption = 1
0F 00 1F 53 00 00 26 

//iBranch = 1
0F 00 1E 53 00 00 26 

//if(iBranch < 8)
07 35 01 96 00 1E 53 00 00 2C 08 16 

	//iPerk = PERKS().GetPerkInTree(GetClass(), iBranch, iOption)
	0F 00 1D 53 00 00 38 3A 19 1B DA 27 00 00 00 00 00 00 16 27 00 1C FF FF FF 00 1B DD 12 00 00 00 00 00 00 1B 64 11 00 00 00 00 00 00 16 00 1E 53 00 00 00 1F 53 00 00 4A 16 

	//kPerk = PERKS().GetPerk(iPerk)
	0F 00 1C 53 00 00 19 1B DA 27 00 00 00 00 00 00 16 13 00 ED F8 FF FF 00 1B D7 12 00 00 00 00 00 00 00 1D 53 00 00 16 

	//ePType = byte(kPerk.iCategory)
	0F 00 1B 53 00 00 38 3D 35 CA FB FF FF D6 F9 FF FF 00 00 00 1C 53 00 00 

	//if(bActiveOnly)
	07 14 01 2D 00 20 53 00 00 

		//if(ePType == 0)
		07 11 01 9A 38 3A 00 1B 53 00 00 38 3A 24 00 16 

			//if(iPerk == 16)
			07 FE 00 9A 00 1D 53 00 00 2C 10 16 

			//else
			06 27 01 

			//GivePerk(iPerk)
			1B DB 13 00 00 00 00 00 00 00 1D 53 00 00 16 

		//else
		06 27 01 

		//GivePerk(iPerk)
		1B DB 13 00 00 00 00 00 00 00 1D 53 00 00 16 

	//++ iBranch
	A5 00 1E 53 00 00 16 

	//while loop
	06 16 00 

//return
04 0B 

//EOS
53 	
[/CODE]
[/BEFORE_HEX]


[AFTER_HEX]
[HEADER]
F4 00 00 00 E8 00 00 00 
[/HEADER]
[code]
//if(GetStatus() == 0)
07 28 00 9A 38 3A 1B 51 13 00 00 00 00 00 00 16 38 3A 24 00 16 

	//self.Destroy()
	19 17 03 00 B2 FF FF FF 00 61 17 16 

//else
06 F1 00 

	//if(GetStatus() == 5)
	07 6E 00 9A 38 3A 1B 51 13 00 00 00 00 00 00 16 38 3A 24 05 16 

		//CYBERNETICSLAB().RemoveSoldierFromCyberneticsLab(self)
		19 1B 9A 07 00 00 00 00 00 00 16 13 00 00 33 00 00 00 1B 36 29 00 00 00 00 00 00 17 16 

		//DEMOUltimateSoldier(4, true)
		1B E2 08 00 00 00 00 00 00 24 04 27 16 

	//else
	06 F1 00 

		//if(GetStatus() == 4)
		07 F1 00 9A 38 3A 1B 51 13 00 00 00 00 00 00 16 38 3A 24 04 16 

			//GENELABS().RemoveSoldierFromGeneLabs(self)
			19 1B F2 10 00 00 00 00 00 00 16 13 00 9A 35 00 00 00 1B 37 29 00 00 00 00 00 00 17 16 

			//DemoUltimateSoldier(4, false)
			1B E2 08 00 00 00 00 00 00 24 04 28 16 

			//null ops
			0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 

//return
04 0B 

//EOS
53 

[/CODE]
[/AFTER_HEX] 
MODFILEVERSION=4
UPKFILE=XComStrategyGame.upk
GUID=31 9C 3B 3F 9C 5D E4 40 AB AF 92 8E 25 65 74 F2 // XComStrategyGame_EW_patch1.upk
FUNCTION=DEMOUltimateSoldier@XGStrategySoldier

[BEFORE_HEX]
[HEADER]
AE 00 00 00 7E 00 00 00 
[/HEADER]
[code]
//null op for optional parameter
0B 
	
//m_kSoldier.iRank = 7
0F 35 DF FA FF FF C1 F9 FF FF 00 01 01 B2 52 00 00 2C 07 

//if(bPsi)
07 6B 00 2D 00 2D 53 00 00 

	//m_kSoldier.iPsiRank = 4
	0F 35 E1 FA FF FF C1 F9 FF FF 00 01 01 B2 52 00 00 2C 04 

	//m_kChar.bHasPsiGift = true
	14 2D 35 2F FF FF FF CB F9 FF FF 00 01 01 B3 52 00 00 27 

//SetSoldierClass(eClass)
1B BC 2A 00 00 00 00 00 00 00 2E 53 00 00 16 

//GiveTopTreePerks(true)
1B E1 13 00 00 00 00 00 00 27 16 

//GiveBottomTreePerks(true)
1B D1 13 00 00 00 00 00 00 27 16 

//if(bPsi)
07 AB 00 2D 00 2D 53 00 00 

	//GivePsiPerks()
	1B DE 13 00 00 00 00 00 00 16 

//return
04 0B 

//EOS
53 	
[/CODE]
[/BEFORE_HEX]


[AFTER_HEX]
[HEADER]
9E 00 00 00 7E 00 00 00 
[/HEADER]
[code]
//if (bPsi)
07 5D 00 2D 00 2D 53 00 00 

	//m_kChar.aStats[7] -= 6
	A2 1A 2C 07 35 17 FB FF FF CB F9 FF FF 00 01 01 B3 52 00 00 2C 06 16 

	//m_kChar.aStats[1] -= 3
	A2 1A 2C 01 35 17 FB FF FF CB F9 FF FF 00 01 01 B3 52 00 00 2C 03 16 

	//AssignRandomPerks()
	1B 1B 03 00 00 00 00 00 00 16 

//ClearPerks(false)
1B B6 06 00 00 00 00 00 00 28 16 

//m_iTurnsOut = 14*24
0F 01 AC 52 00 00 90 2C 0E 2C 18 16 

//SetStatus(6)
1B C1 2A 00 00 00 00 00 00 24 06 4A 16 

//null-ops
0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 

//return
04 0B 

//EOS
53 

[/CODE]
[/AFTER_HEX] 
MODFILEVERSION=4
UPKFILE=XComStrategyGame.upk
GUID=31 9C 3B 3F 9C 5D E4 40 AB AF 92 8E 25 65 74 F2 // XComStrategyGame_EW_patch1.upk
FUNCTION=DismissSoldier@XGFacility_Barracks

[BEFORE_HEX]
[HEADER]
A2 00 00 00 76 00 00 00 
[/HEADER]
[code]
//if(kSoldier == none)
07 11 00 72 00 73 31 00 00 2A 16 

	//return
	04 0B 

//if(kSoldier.m_kSoldier.iPsiRank == 4)
07 4B 00 9A 35 E1 FA FF FF C1 F9 FF FF 00 00 19 00 73 31 00 00 09 00 B2 52 00 00 00 01 B2 52 00 00 2C 04 16 

	//return
	04 0B 

//RemoveSoldier(kSoldier)
1B 35 29 00 00 00 00 00 00 00 73 31 00 00 16 

//STORAGE().ReleaseLoadout(kSoldier)
19 1B 9A 2E 00 00 00 00 00 00 16 13 00 00 00 00 00 00 1B 10 29 00 00 00 00 00 00 00 73 31 00 00 16 

//kSoldier.Destroy()
19 00 73 31 00 00 03 00 B2 FF FF FF 00 61 17 16 

//return
04 0B 

//EOS
53 	
[/CODE]
[/BEFORE_HEX]


[AFTER_HEX]
[HEADER]
92 00 00 00 76 00 00 00 
[/HEADER]
[code]
//if (kSoldier.GetStatus() == 0)
07 66 00 9A 38 3A 19 00 73 31 00 00 0A 00 7D 53 00 00 00 1B 51 13 00 00 00 00 00 00 16 38 3A 24 00 16 

	//RemoveSoldier(kSoldier)
	1B 35 29 00 00 00 00 00 00 00 73 31 00 00 16 

	//STORAGE().ReleaseLoadout(kSoldier)
	19 1B 9A 2E 00 00 00 00 00 00 16 13 00 00 00 00 00 00 1B 10 29 00 00 00 00 00 00 00 73 31 00 00 16 

//kSoldier.GiveBottomTreePerks()
19 00 73 31 00 00 0B 00 D3 52 00 00 00 1B D1 13 00 00 00 00 00 00 27 16 

//null ops
0B 0B 0B 0B 0B 0B 0B 0B 0B 

//return
04 0B 

// EOS
53 

[/CODE]
[/AFTER_HEX] 

 

 

 

Clearly your mod works just fine, but I figured I would point out a couple of potential trouble-spots (which have caused me a lot of headaches in the past) that could cause issues in other situations. I'm not trying to make you feel bad, but I thought I would point these out in a public forum in case others can learn from them :smile:

 

 

1) Return Values in context statements

 

This error is really common because UE Explorer doesn't really label or display these.

 

In XGFacility_Barracks.DismissSoldier you constructed the line:

//kSoldier.GiveBottomTreePerks()
19 00 73 31 00 00 0B 00 D3 52 00 00 00 1B D1 13 00 00 00 00 00 00 27 16 

which UE Explorer decompiles just fine. However there is a secret bug lurking (in this case it doesn't matter because you aren't using the return value -- in fact, GiveBottomTreePerks doesn't even have a return value!).

 

The trick is in the D3 52 00 00 references, whose resolved name is : "XGStrategySoldier.HasPsiGift.ReturnValue". If you were actually trying to assign and use the results of the return value this would be a crashing bug. And it's really sneaky / hard to find-or-fix. For this particular example technically you'd set those byte to 00 00 00 00 as that's the value used for a null return. But it runs just fine without it.

 

I'm sure the real Unreal Compiler automatically inserts the correct ReturnValue reference into the context statement, but since we're modding we have to do it manually. The easiest way to find the ReturnValue of a function is by using the UE Explorer "View Managed Properties" Dialogue for the function. The ReturnValue will be given as a Decimal integer value (which you have to convert to LITTLE_ENDIAN hex).

 

 

2) Differences in types of == operators

 

In XGSoldierUI.DismissSoldierActionCallback you disabled a conditional

//if((GENELABS()) != none)
07 9C 00 77 1B F2 10 00 00 00 00 00 00 16 2A 16 

by changing the 77 token into a 9A token. However 77 is the != operator token for class comparisons, while 9A is the == operator for integer comparisons. So this effectively works because the comparison of the GENELABS() class object (as an implicit cast to integer) doesn't equal none (it can't), which results in false, skipping the conditional.

 

The == operator for classes is actually 72, while the != operator for integers is 9B. Floats, strings, vectors all have their own == and != operators. Bytes don't have comparison operators, which is why you'll always see them cast to integers before doing a comparison (using 38 3A).

 

Another effective (if somewhat messy) way to skip conditional is to change the 07 into a 06, changing it from a conditional to an unconditional jump. I use this method all the time when debugging.

 

 

3) Removing error checking statement

 

This one isn't really a byte code error, but I saw that in XGFacility_Barracks.DismissSoldier you removed the line :

//if(kSoldier == none)
07 11 00 72 00 73 31 00 00 2A 16 

in order to free up space. While it appears to work, you begin the function by using :

//if (kSoldier.GetStatus() == 0)
07 66 00 9A 38 3A 19 00 73 31 00 00 0A 00 7D 53 00 00 00 1B 51 13 00 00 00 00 00 00 16 38 3A 24 00 16 

This can be the source of maddening intermittent errors that users end up reporting. If the function is ever (for some reason) called with a null kSoldier, the game will crash.

Link to comment
Share on other sites

  • Replies 193
  • Created
  • Last Reply

Top Posters In This Topic

This can be the source of maddening intermittent errors that users end up reporting. If the function is ever (for some reason) called with a null kSoldier, the game will crash.

Actually, it is safe to use null-object references:

Variables that refer to actors always either refer to a valid actor (any actor that actually exists in the level), or they contain the value None. None is equivalent to the C/C++ NULL pointer. However, in UnrealScript, it is safe to access variables and call functions with a None reference; the result is always zero.

Here: http://udn.epicgames.com/Three/UnrealScriptVariables.html Edited by wghost81
Link to comment
Share on other sites

Good stuff! I confess I don't have any experience with the C family (I treat Xcom more like... cryptography, I guess?), so I won't catch many invisible errors.

 

I did have concerns about removing the escape clause, but I looked up the chain to the one place DismissSoldier is called and a null reference couldn't get through. "Couldn't" is such a loaded word though, it's probably more that playing so many Bethesda games has raised my threshold of acceptable crash levels : p

Edited by Peasly Wellbott
Link to comment
Share on other sites

  • 2 weeks later...

Yes it is :)

 

Sorry I haven't posted up anything recently, as I've been knee-deep in Long War coding (after finishing up work on the UPKmodder tool we're using).

 

In another related thread someone posted up the idea of being able to add specific "random perk" slots to create an option in-between the regular static perks and the very randomized SW Training Roulette option. I'm pretty sure I'm going to add it, it's just a question of when I get the time.

Link to comment
Share on other sites

I know that I speak for many of us by saying that the work that Amineri and JohnnyLump are doing for this game is just absolutely amazing. The LongWar mod is a DLC in itself and makes the game so much more fun to play.

 

So I just want to say thank you to the both of you and all others who might be helping out.

Link to comment
Share on other sites

  • 2 weeks later...

I thought I'd drop in a let everyone know that I am still working on stuff :wink:

 

Here's a screenshot for the psionics system rework that I'm doing:

http://wiki.tesnexus.com/images/6/61/Expanded_Psionic_Perks.jpg

 

As you can see I've unlocked a full 7 ranks for psionics, and will be fixing up various MEC/genemod/alien perks to fill out the psionic perk tree from the vanill 5 perks to the 12 perks shown above.

 

For testing/screenshot I enabled the display of all 7 ranks -- normally these psionic abilities aren't available this early :smile:

 

In case you are curious what the icons are:

  • Rank 1 : Psi Inspiration and MindMerge (alien perk)
  • Rank 2 : Psi Panic and Distortion Field (MEC Support ability)
  • Rank 3 : Clairaudience (bioelectric skin genemod) and Regen (EXALT medic regen pheremones)
  • Rank 4 : Mindfray and Bloodcall (alien perk)
  • Rank 5 : Greater MindMerge (alien perk) and Telekinetic Field
  • Rank 6 : Mind Control
  • Rank 7 : Rift

This is part of a general reworking of psionics that's coming in the EW Long War, which includes a bunch of other balancing factors to mitigate the power creep inherent in granting more abilities to soldiers.

 

Psionics will come earlier, but be more difficult to train. Psionics abilities have to be unlocked via research (usually interrogations), and psionic soldiers require more recovery time between missions (i.e fatigue time is longer).

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...