Amineri Posted December 28, 2013 Author Share Posted December 28, 2013 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 More sharing options...
dubiousintent Posted December 28, 2013 Share Posted December 28, 2013 Dang! That's some good stuff to know, but darned if I can see quite where to put it on the wiki. Any suggestions other than 'Hex editing UPK files' under the 'Rewriting functions' section? -Dubious- Link to comment Share on other sites More sharing options...
wghost81 Posted December 28, 2013 Share Posted December 28, 2013 (edited) 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 December 28, 2013 by wghost81 Link to comment Share on other sites More sharing options...
Amineri Posted December 28, 2013 Author Share Posted December 28, 2013 Well, that's interesting... I thought sure that I'd had some crashing bugs as a result of trying to access a member of a none reference. I guess it must have been something else? Link to comment Share on other sites More sharing options...
wghost81 Posted December 28, 2013 Share Posted December 28, 2013 I don't know, as being a C++ programmer, I always check for null-references myself. :smile: But discovering that UE script null-objects are safe (presumably) was interesting. Link to comment Share on other sites More sharing options...
PeaslyWellbott Posted December 28, 2013 Share Posted December 28, 2013 (edited) 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 December 28, 2013 by Peasly Wellbott Link to comment Share on other sites More sharing options...
EpicBlu Posted January 8, 2014 Share Posted January 8, 2014 Maybe unnecessary bump, but is this mod still being worked on? Link to comment Share on other sites More sharing options...
Amineri Posted January 8, 2014 Author Share Posted January 8, 2014 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 More sharing options...
drake8888 Posted January 8, 2014 Share Posted January 8, 2014 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 More sharing options...
Amineri Posted January 17, 2014 Author Share Posted January 17, 2014 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 FieldRank 6 : Mind ControlRank 7 : RiftThis 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 More sharing options...
Recommended Posts