Amineri Posted July 8, 2013 Share Posted July 8, 2013 Definitely Firaxis is making some use of the extensibility of the Unreal Engine with the DLC content. So far all of the experimentation I've done with attempting to invoke a different asset has been unsuccessful, however. Perhaps I should try and follow the trail of how the Zhang custom head was mapped into the game and loads. Functionally the head is another object treated identically to any other attached object. The base body type is determined by gender / armor type. Then an armor kit can be overlayed (either defined by the armor / weapon, or a custom armor kit overlay). The head is attached, and the various weapons / pieces of equipment are attached. If I can understand how the two DLC armor kits, the DLC helmets / hair, or the Zhang head DLC is implemented, it might be possible to do something similar to add some additional art assets via a similar method. I noticed that some hooks for the DLC assets were programmed in from the beginning -- there are still hooks for the 2nd DLC character 'Annette', although there aren't any art assets (they'd be in the 2nd DLC folder). Link to comment Share on other sites More sharing options...
Amineri Posted July 8, 2013 Share Posted July 8, 2013 Some good news and maybe-not-so-good news. I dug a bit into some of the DLC content. By simply copying the SF upk for the kevlar armor kit 0 (file: Deco_Kevlar0_SF.upk) and the texture file (file: CharTextures_DLC_PackIn.tfc) to my umodel folder I was able to open and view the DLC kevlar armor kit 0. The upk contained both the male and female versions of the armor kit. The female version is displayed below: So the good news is that the game can load up new armor kits. ---------- So now the not-so-good news. I dug a bit into where it is that the code 'discovers' the new armor kits, etc. In XGCustomizeUI are a variety of functions relating to the soldier customization. In particular there is : function AdvanceArmorDeco(int Dir) { local int CurIdx, NewIdx, NumPresets, NewDeco; NumPresets = m_kPawn.PossibleArmorKits.Length; CurIdx = GetArmorDecoIndex(); NewIdx = CurIdx + Dir; if(NewIdx < 0) { NewIdx = NumPresets - 1; } if(NewIdx >= NumPresets) { NewIdx = 0; } NewDeco = ((m_kPawn.PossibleArmorKits.Length > 0) ? m_kPawn.PossibleArmorKits[NewIdx] : -1); m_kPawn.SetArmorDeco(NewDeco); m_kSoldier.m_kSoldier.kAppearance.iArmorDeco = NewDeco; //return; } This function sets up and cycles through the available armor kits as defined in the m_kPawn.PossibleArmorKits dynamic array. So the next question is : where is PossibleArmorKits defined? In XComHumanPawn are the following class variables, which include PossibleArmorKits: var transient int NumPossibleArmorSkins; var transient array<int> PossibleHeads; var transient array<int> PossibleHairs; var transient array<XGGameData.ECharacterVoice> PossibleVoices; var transient int NumPossibleHairColors; var transient int NumPossibleSkinColors; var transient int NumPossibleArmorTints; var transient array<int> PossibleArmorKits; So this is where the PossibleArmorKits array "lives", but how is it filled out? In XComHumanPawn.state'InHQ' is the function : function FindPossibleCustomParts(const out TCharacter inCharacter) { local array<int> GenderHeads, RaceHeads, RaceGenderHeads, CharacterHeads, GenderHairs, RaceHairs, RaceGenderHairs, CharacterHairs, PawnHairs, CharacterPawnHairs; local XGGameData.EPawnType PawnType; local XGGameData.EItemType ArmorType; PossibleHeads.Length = 0; PossibleHairs.Length = 0; PossibleArmorKits.Length = 0; XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).GetContentIdsForRace(4, byte(m_kAppearance.iRace), RaceHeads); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).GetContentIdsForGender(4, byte(m_kAppearance.iGender), GenderHeads); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).GetContentIdsForCharacter(4, byte(inCharacter.iType), CharacterHeads); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).IntersectContentIds(RaceHeads, GenderHeads, RaceGenderHeads); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).IntersectContentIds(RaceGenderHeads, CharacterHeads, PossibleHeads); PawnType = byte(class'XGBattleDesc'.static.MapSoldierToPawn(Character.kInventory.iArmor, m_kAppearance.iGender)); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).GetContentIdsForRace(6, byte(m_kAppearance.iRace), RaceHairs,, true); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).GetContentIdsForGender(6, byte(m_kAppearance.iGender), GenderHairs,, true); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).GetContentIdsForCharacter(6, byte(inCharacter.iType), CharacterHairs,, true); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).GetContentIdsForPawn(6, PawnType, PawnHairs, true); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).IntersectContentIds(RaceHairs, GenderHairs, RaceGenderHairs); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).IntersectContentIds(CharacterHairs, PawnHairs, CharacterPawnHairs); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).IntersectContentIds(RaceGenderHairs, CharacterPawnHairs, PossibleHairs); ArmorType = byte(inCharacter.kInventory.iArmor); PossibleArmorKits.AddItem(-1); XComContentManager(class'Engine'.static.GetEngine().GetContentManager()).GetContentIdsForArmor(2, ArmorType, PossibleArmorKits); PossibleVoices = ((m_kAppearance.iGender == 1) ? class'XGCharacterGenerator'.default.PossibleMaleVoices : class'XGCharacterGenerator'.default.PossibleFemaleVoices); //return; } You can see that it is invoking the XComContentManager to discover the content for a whole variety of possible customizable things, with the armor kits among them at the end, via the GenerateContentIdsForArmor call. Unfortunately when the XComContentManager is broached, the situation becomes unhappy : native function GetContentIdsForArmor(XComContentManager.EContentCategory ContentType, XGGameData.EItemType ArmorType, out array<int> Ids); native function bool GetContentInfo_Unit(int Id, out XComUnitPackageInfo UnitInfo); native function bool GetContentInfo_Weapon(int Id, out XComWeaponPackageInfo WeaponInfo); native function bool GetContentInfo_ArmorKit(int Id, out XComArmorKitPackageInfo KitInfo); native function bool GetContentInfo_Head(int Id, out XComHeadPackageInfo HeadInfo); native function bool GetContentInfo_Body(int Id, out XComBodyPackageInfo BodyInfo); native function bool GetContentInfo_Hair(int Id, out XComHairPackageInfo HairInfo); native function IntersectContentIds(const out array<int> IdsA, const out array<int> IdsB, out array<int> Intersection); native function XComLinearColorPalette GetColorPalette(XComContentManager.EColorPalette PaletteType); All of the functions that retrieve the possible content info are implemented in native code. At this point I do not know how robust the native code is about searching for new content to load. Deco_Kevlar0_SF.upk is in the DLC_PackIn/CookedPCConsole folder, whileDeco_Kevlar1_SF.upk is in the DLC_Day60/CookedPCConsole folder Perhaps the content search function scans through subfolders looking for files that begin with 'Deco_Kevlar' The XGGameData class has the EArmorKits enum which includes: eKit_Deco_Kevlar0, eKit_Deco_Kevlar1, eKit_Deco_Kevlar2, eKit_Deco_Kevlar3, eKit_Deco_Kevlar4, eKit_Deco_Kevlar5, eKit_Deco_Kevlar6, eKit_Deco_Kevlar7, eKit_Deco_Kevlar8, eKit_Deco_Kevlar9, eKit_Deco_Kevlar10, eKit_Deco_Kevlar11, 12 armor kit names which are consistent with the 2 existing Kevlar armor kits. The XComEngine.ini file in the DLC_PackIn contains the lines: [Engine.PackagesToFullyLoadForDLC] .MapName=Command1 .Package=Deco_Kevlar0 Additionally, the XComContent.ini in the DLC_PackIn contains the lines: ; Add deco kits [XComGame.XComContentManager] ; Kevlar Deco 0 +ArmorKitPackageInfo=(KitType=eKit_Deco_Kevlar0,ArchetypeName="Deco_Kevlar0.ARC_Deco_Kevlar0") ; Skeleton Deco 0 +ArmorKitPackageInfo=(KitType=eKit_Deco_Skeleton0,ArchetypeName="Deco_Skeleton0.ARC_Deco_Skeleton0") ; Carapace Deco 0 +ArmorKitPackageInfo=(KitType=eKit_Deco_Carapace0,ArchetypeName="Deco_Carapace0.ARC_Deco_Carapace0") The DLC_Day60 ini files contain similar lines. These are all merged into a single ini files in the My Games folder, which contains the XComEngine.ini and XComContent.ini loads defined in both DLC packages, as well as the core game. It may be that this is sufficient to cause the native XComContentManger.GetContentIdsForArmor function to retrieve the existing two armor kits available via released DLC. If so, creating a third armor kit as Deco_Kevlar2, and then configuring the ini files appropriately may cause the new art assets to be loaded and available in the customize menu. However, that requires someone that is familiar with creating art assets for Unreal Engine games, which certainly isn't me. Sorry for the long post, but ... well... lots of info :P Link to comment Share on other sites More sharing options...
Amineri Posted July 8, 2013 Share Posted July 8, 2013 A quick update. The good news is that the XComContent Native functions are indeed robust enough to load additional armor kits. I did the following:Created a copy of the Deco_Kevlar0_SF.upk, renamed as Deco_Kevlar2_SF.upk Hex editted the string Deco_Kevlar0 to Deco_Kevlar2 inside the new file Deco_Kevlar2_SF.upk Placed the new Deco_Kevlar2_SF file int the DLC_PackIn/CookedPCConsole folder Editted both of XComEngine.ini and XComContent.ini to include load commands for Deco_Kevlar2_SF Editted the same merged files in the My Games to include the load of Deco_Kevlar2_SFUpon game load the Kevlar now had 4 total Kevlar armor kit options (the basic 1, the one included with the Zhang DLC, and two identical copies of the version included with the DLC_PackIn). This means that if someone with the appropriate tools were to create a brand new armor kit overlay, it could be integrated into the game as a customizable option. Link to comment Share on other sites More sharing options...
dubiousintent Posted July 8, 2013 Share Posted July 8, 2013 Excellent! Just knowing that expected functionality does indeed work is hugely encouraging that we don't have to rediscover EVERYTHING from scratch. It seems to me our current major roadblock is to find a way of dealing with 'native functions'. So here are the results of some more digging on that subject.The Unreal Wiki Functions article says: "A function in UnrealScript is a subroutine associated with a specific class." (A class is essentially a blueprint for an item to be used by the engine in the game, per the Unreal Script Reference at [http://udn.epicgames.com/Three/UnrealScriptReference.html].)Simulated is a ''call modifier'' that "Marks the function as valid for execution on clients if the actor containing it was replicated to that client and the local role of that client is either ROLE_SimulatedProxy or ROLE_DumbProxy.Note: The modifier simulated does not imply any kind of replication or even broadcast! Also, this modifier is not inherited when overriding functions - every super function call evaluates that super function's simulated modifier separately, potentially breaking the chain of super calls on clients!"Further, that native is an implementation modifier that "The actual implementation of this function resides in native code in a C++ function with the name execNameOfUnrealScriptFunction. ... Similar to simulated functions, native functions can be called clientsidely on replicated actors even if the local role is ROLE_SimulatedProxy or ROLE_DumbProxy."The interesting implication to me was that it ought to be possible to overwrite the native functions. And according to this UDN article, you can. There is also a tutorial on that site specifically on Mastering Unreal Functions, which is part of the Unreal Script Programming section of that Wiki.The Unreal Development Kit Gems section has additional useful guides such as ''Adding map specific debugging options'' for the Unreal Editor, ''Adding on screen indicators'' for "How to add on screen indicators providing useful locational markers to the player", ''Creating actor selection boxes or brackets'' on "How to render selection boxes around an actor using the HUD", ''Saving and loading game states'' on "How to create a flexible save and load game state system", etc.Anyone desiring to mod or really create new material for X-Com is advised to start with the UDN and Unreal Wiki sites. -Dubious- Link to comment Share on other sites More sharing options...
Amineri Posted July 9, 2013 Share Posted July 9, 2013 I think the 'overwriting' is the class-concept of having a child class able to overwrite a function defined in the parent class. I've seen this a few times implemented in the XCOM game code. I think the method above would work for any of the "DLC type" items : armor kits, helmets, hair styles, animation, faces, voices, etc. All stuff that isn't related to actual gameplay. All of the game mechanics are derived from Weapons and Armor. Even items like Medikits and Nano-fiber vests are actually implemented as weapons. Currently all of the weapons and armors have, in addition to the extra upks containing art assets, a class defined inside the XComGame.upk. All Weapons have a name such as XGWeapon_<weaponname>. Each of these definitions doesn't define much, as they are pretty much just extensions with a few tweaks of the parent XGWeapon class. All armors have a similar class such as XGArmor_<armorname>. The way the loadout system is currently set up in XCOM, it appears that an additional class would have to be added to add any additional weapons to the game. At this point I don't know how to add new functions to the XComGame.upk, or if there is some way to add a new class that can be called from XComGame.upk but resides in a separate upk. Link to comment Share on other sites More sharing options...
dubiousintent Posted July 9, 2013 Share Posted July 9, 2013 I've updated the 'Modding XCOM:EU 2012' article on the Wiki with the links to the major elements of the UE3 articles on the UDN site, and a link to the Unreal Wiki site. Hopefully that will help people zero-in on the modding material they are interested in rather than have to drill down from the very top. -Dubious- Link to comment Share on other sites More sharing options...
Amineri Posted July 9, 2013 Share Posted July 9, 2013 I just tried making some of the weapon/armor kits into "customizable kits", but it didn't seem to quite work. I took the Kevlar + LMG (Ballistic) kit and changed the file name and hex editted the internal name to "ARC_Deco_Kevlar2". Upon loading the game did present a 4th Kevlar deco option, but instead of the Heavy kit all extra were removed -- the shoulder, elbow and knee pads. This isn't a bad outfit for a scout, actually, but wasn't what I was shooting for. Hopefully I'll puzzle out why that weapon-armor kit appears to be broken as a deco armor kit. Link to comment Share on other sites More sharing options...
Amineri Posted July 10, 2013 Share Posted July 10, 2013 This should probably go into a new thread, but here's the last gasp in this thread. My experiment to display the level1 Kevlar + LMG armor kit didn't work as planned, but it did end up creating a somewhat unique look that I use on my Scout : Basically it removes the shoulder guards, elbow pads and knee pads that constitute the "Kevlar + Assault Rifle" armor kit, leaving just the bare armor model. I think it looks great for the Scout class in Long War. Link to comment Share on other sites More sharing options...
Amineri Posted July 16, 2013 Share Posted July 16, 2013 I've had a few questions recently regarding IEEE floating point values that UE uses to store floats. Floats are designated via the 0x1E token. I'm currently using this webtool to convert float values to IEEE standard representation : http://babbage.cs.qc.cuny.edu/IEEE-754.old/Decimal.html There a lot of such tools around, though. The second question I had was about how to understand enumerated values, which I do using Bertilsson's excellent enum reverse-lookup tool : http://hem.bredband.net/bertrich/XCOM/ReverseLookup.htm Information on both of these should probably be added to the earlier "how to hex edit" tutorials. I tried to add it to the wiki myself but I'm having issues accessing the wiki right now :( Link to comment Share on other sites More sharing options...
dubiousintent Posted July 17, 2013 Share Posted July 17, 2013 I've had a few questions recently regarding IEEE floating point values that UE uses to store floats. Floats are designated via the 0x1E token. I'm currently using this webtool to convert float values to IEEE standard representation : http://babbage.cs.qc.cuny.edu/IEEE-754.old/Decimal.html There a lot of such tools around, though. The second question I had was about how to understand enumerated values, which I do using Bertilsson's excellent enum reverse-lookup tool : http://hem.bredband.net/bertrich/XCOM/ReverseLookup.htm Information on both of these should probably be added to the earlier "how to hex edit" tutorials. I tried to add it to the wiki myself but I'm having issues accessing the wiki right now :(References added to the 'Data Types' section of 'Hex Editting UPK files' Wiki article. -Dubious- Link to comment Share on other sites More sharing options...
Recommended Posts