Chesed Posted January 24 Share Posted January 24 (edited) Hey hey. I have a character with several forms who swaps between different speech files when he transforms (by equpping a certain item). Originally I used a different function to replace his forms' describe strings with an assortment of generic lines, but today I removed it and started manually changing things so that they could examine certain prefabs. This is a simplified recreation of the function that handles his swaps (I believe I got it from the forums originally and did not write it myself.) Spoiler local function SwapSpeech(inst) if type(inst) == "table" and inst.prefab == "esctemplate" then if inst:HasTag("hatison") then return "esctemplateswap" end end return inst end local _GetString = GLOBAL.GetString function GLOBAL.GetString(inst, stringtype, modifier) return _GetString(SwapSpeech(inst), stringtype, modifier) end local _GetDescription = GLOBAL.GetDescription function GLOBAL.GetDescription(inst, item, modifier) return _GetDescription(SwapSpeech(inst), item, modifier) end local _GetActionFailString = GLOBAL.GetActionFailString function GLOBAL.GetActionFailString(inst, action, reason) return _GetActionFailString(SwapSpeech(inst), action, reason) end In doing this I noticed that his strings aren't being swapped properly when he examines another player. Examining other things uses speech file 2, but examining players uses speech file 1. This kind of sucks because the entire reason I started doing the lines manually is specifically so that my character could still examine other players, but in my limited testing it's the only thing that doesn't work. It always defaults to the string of his default file. :| Anyone have any idea how to potentially fix it? I've included a slimmed down recreation of my mod using the Extended Character Template. Equipping the "hat" the character spawns with changes the dialogue. Thank you for reading. swapface speech.zip Edited February 1 by Chesed Link to comment Share on other sites More sharing options...
Hornete Posted January 25 Share Posted January 25 2 hours ago, Chesed said: Hey hey. I have a character with several forms who swaps between different speech files when he transforms (by equpping a certain item). Originally I used a different function to replace his forms' describe strings with an assortment of generic lines, but today I removed it and started manually changing things so that they could examine certain prefabs. This is a simplified recreation of the function that handles his swaps (I believe I got it from the forums originally and did not write it myself.) Reveal hidden contents local function SwapSpeech(inst) if type(inst) == "table" and inst.prefab == "esctemplate" then if inst:HasTag("hatison") then return "esctemplateswap" end end return inst end local _GetString = GLOBAL.GetString function GLOBAL.GetString(inst, stringtype, modifier) return _GetString(SwapSpeech(inst), stringtype, modifier) end local _GetDescription = GLOBAL.GetDescription function GLOBAL.GetDescription(inst, item, modifier) return _GetDescription(SwapSpeech(inst), item, modifier) end local _GetActionFailString = GLOBAL.GetActionFailString function GLOBAL.GetActionFailString(inst, action, reason) return _GetActionFailString(SwapSpeech(inst), action, reason) end In doing this I noticed that his strings aren't being swapped properly when he examines another player. Examining other things uses speech file 2, but examining players uses speech file 1. This kind of sucks because the entire reason I started doing the lines manually is specifically so that my character could still examine other players, but in my limited testing it's the only thing that doesn't work. It always defaults to the string of his default file. :| Anyone have any idea how to potentially fix it? I've included a slimmed down recreation of my mod using the Extended Character Template. Equipping the "hat" the character spawns with changes the dialogue. Thank you for reading. swapface speech.zip 2.83 MB · 0 downloads local function TryCharStrings(inst, charstrings, modifier) return charstrings ~= nil and ( TryDescribe(charstrings.DESCRIBE[string.upper(inst.prefab)], modifier) or TryDescribe(charstrings.DESCRIBE.PLAYER, modifier) ) or nil end local function GetDescription(inst, viewer) local modifier = inst.components.inspectable:GetStatus(viewer) or "GENERIC" return string.format( TryCharStrings(inst, STRINGS.CHARACTERS[string.upper(viewer.prefab)], modifier) or TryCharStrings(inst, STRINGS.CHARACTERS.GENERIC, modifier), inst:GetDisplayName() ) end Looking at player_common, it's hardcoded to use the players prefab when indexing the characters strings, so you'll want to wrap these functions and make sure it returns the proper result. There could possibly be other instances like this but it's just players examinations from what I see. 1 Link to comment Share on other sites More sharing options...
Chesed Posted January 25 Author Share Posted January 25 16 hours ago, Hornete said: Looking at player_common, it's hardcoded to use the players prefab when indexing the characters strings, so you'll want to wrap these functions and make sure it returns the proper result. There could possibly be other instances like this but it's just players examinations from what I see. Typical! Thank you for digging into the cause for me, I would have never been able to find this. I have never actually wrapped a function for my mod before and have no idea how to do it. I'll poke around other threads on the forums and see if I can figure something out myself in the meantime, but if someone reading this knows how and is willing to help me out, that would be great. Worst case scenario I give up after a month and throw 10 bucks at a coder friend to do it for me. Link to comment Share on other sites More sharing options...
Hornete Posted January 25 Share Posted January 25 38 minutes ago, Chesed said: I have never actually wrapped a function for my mod before and have no idea how to do it. You do, and just did! (Probably just didn't realize the name/terminology, which is OK, For a lot of things I don't recognize them by their name/term but I do know how to do it and the process.) 20 hours ago, Chesed said: local function SwapSpeech(inst) if type(inst) == "table" and inst.prefab == "esctemplate" then if inst:HasTag("hatison") then return "esctemplateswap" end end return inst end local _GetString = GLOBAL.GetString function GLOBAL.GetString(inst, stringtype, modifier) return _GetString(SwapSpeech(inst), stringtype, modifier) end local _GetDescription = GLOBAL.GetDescription function GLOBAL.GetDescription(inst, item, modifier) return _GetDescription(SwapSpeech(inst), item, modifier) end local _GetActionFailString = GLOBAL.GetActionFailString function GLOBAL.GetActionFailString(inst, action, reason) return _GetActionFailString(SwapSpeech(inst), action, reason) end You're 'wrapping' these functions here by saving the original function, overriding it and running the old function while making your own adjustments. You'll just want to do that same process for this player characters examination function: inst.components.inspectable.getspecialdescription (AddPlayerPostInit will be useful this case of hooking into every character) Happy modding! Link to comment Share on other sites More sharing options...
Chesed Posted January 26 Author Share Posted January 26 On 1/25/2024 at 6:14 PM, Hornete said: You do, and just did! (Probably just didn't realize the name/terminology, which is OK, For a lot of things I don't recognize them by their name/term but I do know how to do it and the process.) AH, I see. I took those function from another thread on the forums... I actually had no idea exactly how they worked until now. I am godawful at understanding programming but I talked it through with a friend and between that and your explanation I think understand what I have to do. Unfortunately I spent my entire evening yesterday trying to figure out how exactly to write the functions to do it and got nowhere. For a myriad of different reasons the game just crashes as soon as my character examines something no matter what I try changing, I'm guessing because I'm just altering the wrong variables when trying to wrap the function. I eventually started getting crashes linked to stringsutil and just gave up for now. The most I can figure out is that I probably have to somehow check for the hat's tag and then do this? Quote return string.format( TryCharStrings(inst, STRINGS.CHARACTERS["ESCTEMPLATESWAP"], modifier) ) But I have no idea where to put it or how to write it. Thank you for taking the time to try and explain it to me, and I'm really sorry I'm just not getting it. Unfortunately my understanding of programming is just too low and I didn't anticipate Klei hardcoding things to be the issue. If nobody gets bored enough to try and write it, I will try again when I'm not so wiped. I really didn't think tweaking the strings would take three days off of my life. Link to comment Share on other sites More sharing options...
Hornete Posted January 27 Share Posted January 27 6 hours ago, Chesed said: The most I can figure out is that I probably have to somehow check for the hat's tag and then do this? Yea! That's very good. Since you'll want to do this for every player character, you'll want to use the helpful `AddPlayerPostInit` utility function local function TryDescribe(descstrings, modifier) return descstrings ~= nil and ( type(descstrings) == "string" and descstrings or descstrings[modifier] or descstrings.GENERIC ) or nil end local function TryCharStrings(inst, charstrings, modifier) return charstrings ~= nil and ( TryDescribe(charstrings.DESCRIBE[string.upper(inst.prefab)], modifier) or TryDescribe(charstrings.DESCRIBE.PLAYER, modifier) ) or nil end AddPlayerPostInit(function(inst) if not TheWorld.ismastersim then return end --Server only past this point local _getspecialdescription = inst.components.inspectable.getspecialdescription inst.components.inspectable.getspecialdescription = function(inst, viewer, ...) if viewer:HasTag("hatison") then --The 'viewer'(person examining) is wearing the hat return string.format( TryCharStrings(inst, STRINGS.CHARACTERS["ESCTEMPLATESWAP"], modifier), inst:GetDisplayName() ) end -- return _getspecialdescription(inst, viewer, ...) end end) Something like this should work nicely. About your stringutil crashes, I'd suspect it was because you were missing the 2nd parameter to pass into string.format judging by your sample code, which is the name of the player (when gets substituted into the '%s' characters you see in the speech file strings) 1 Link to comment Share on other sites More sharing options...
Chesed Posted January 29 Author Share Posted January 29 On 1/27/2024 at 3:30 AM, Hornete said: [SNIP] Something like this should work nicely. Thank you very much! Sorry for the late reply, I've been sick the past few days. I had to change "TheWorld" to "GLOBAL.TheWorld" but other than that, it almost works perfectly! Unfortunately there's one issue. The swapped character can't tell what modifier the examined player has, for some reason? Even still, this is still a gigantic improvement over what I had to deal with before, so thank you! Link to comment Share on other sites More sharing options...
Hornete Posted January 30 Share Posted January 30 21 hours ago, Chesed said: Thank you very much! Sorry for the late reply, I've been sick the past few days. I had to change "TheWorld" to "GLOBAL.TheWorld" but other than that, it almost works perfectly! Unfortunately there's one issue. The swapped character can't tell what modifier the examined player has, for some reason? Even still, this is still a gigantic improvement over what I had to deal with before, so thank you! Ah right! We forgot to set the 'modifier' variable properly. local modifier = inst.components.inspectable:GetStatus(viewer) or "GENERIC" Sneak this in right at the beginning of the `getspecialdescription` override. 1 1 Link to comment Share on other sites More sharing options...
Chesed Posted February 1 Author Share Posted February 1 On 1/30/2024 at 3:03 PM, Hornete said: Ah right! We forgot to set the 'modifier' variable properly. local modifier = inst.components.inspectable:GetStatus(viewer) or "GENERIC" Sneak this in right at the beginning of the `getspecialdescription` override. Everything works as I want now! Thank you very much for your help and patience! Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now