prideslayer Posted March 9, 2015 Posted March 9, 2015 The UDF should *always* run synchronously, never allowing the calling script to continue until it returns (or crashes). It's not an async call like a spell. Not sure what's going on with the script issue.. maybe a buggy NVSE version? What was the calling context when it was a UDF? You can't always GetContainer or things like that -- best to make the UDF take a parameter, and pass it in from the caller.
Halstrom Posted March 9, 2015 Author Posted March 9, 2015 The UDF should *always* run synchronously, never allowing the calling script to continue until it returns (or crashes). It's not an async call like a spell. Not sure what's going on with the script issue.. maybe a buggy NVSE version? What was the calling context when it was a UDF? You can't always GetContainer or things like that -- best to make the UDF take a parameter, and pass it in from the caller. Oh sorry that was the converted version of the script, I previously had Begin Function (rActor) in the UDF version, that was the only thing I changed other than adding the female/male/creature check.
Halstrom Posted March 9, 2015 Author Posted March 9, 2015 Also I just discovered now I got this working that when my SexoutOffspring renaming activates it seems to change the players name to that of the new offspring. This script was given to me by someone else and works fine otherwise, but I really don't understand the JuJu being used to work out what I've done wrong. Scn SexoutOffSpring0RenameTokenScript ref rPlayer ref rMsgA ref rMsgB ref rTarget int bMenu int sAdded int iRemoving Begin OnAdd Set rTarget to GetContainer Set rPlayer to PlayerREF Set rMsgA to SexoutOffSpringNameMsgA Set rMsgB to SexoutOffSpringNameMsgB SetNameEx "%n" rPlayer rMsgA SetNameEx "%n" rTarget rPlayer GetPlayerName Set sAdded to 1 End Begin GameMode if (0 == sAdded) Return endif if (1 == bMenu) && iRemoving < 1 SetNameEx "%n" rPlayer rMsgB rTarget.SetActorFullName SexoutOffSpringNameMsgB PlayerREF.SetActorFullName SexoutOffSpringNameMsgA rTarget.NX_SetEVFo "SOF:rOffSpringName" SexoutOffSpringNameMsgB RemoveMe Set iRemoving to 1 endif End Begin MenuMode 1051 Set bMenu to 1 End
prideslayer Posted March 9, 2015 Posted March 9, 2015 Well this line seems suspect... PlayerREF.SetActorFullName SexoutOffSpringNameMsgA
Halstrom Posted March 9, 2015 Author Posted March 9, 2015 Well this line seems suspect... PlayerREF.SetActorFullName SexoutOffSpringNameMsgA Yeah, I suspect it's needed for the magfic to work, I'm going to try adding: PlayerREF.SetActorFullName rPlayer before: Set iRemoving to 1 and see if that sorts it.
prideslayer Posted March 10, 2015 Posted March 10, 2015 Wait, the script is supposed to change the players name? I thought that's what you said it was doing, but didn't want it to. It's doing it in two spots. That one, and in the onadd with the SetNameEx. SetActorFullName takes a message (MEGF or whatever record type -- the one used for popup messages), SetNameEx takes a string.
Halstrom Posted March 10, 2015 Author Posted March 10, 2015 Wait, the script is supposed to change the players name? I thought that's what you said it was doing, but didn't want it to. It's doing it in two spots. That one, and in the onadd with the SetNameEx. SetActorFullName takes a message (MEGF or whatever record type -- the one used for popup messages), SetNameEx takes a string.Well from what I gather it temporarily uses the option to change the Player name to change an NPC with the tokens name which works ok, but seems to not set the player name back afterwards.
prideslayer Posted March 10, 2015 Posted March 10, 2015 Oh blimey.. switch the order of these two lines, this may be the problem. RemoveMe Set iRemoving to 1 RemoveMe is a return. iRemoving isn't being set, so if the script runs again next frame (as it likely does, removeme isn't instant) then the names get swapped around a second time.. not good.
Halstrom Posted March 10, 2015 Author Posted March 10, 2015 Oh blimey.. switch the order of these two lines, this may be the problem. RemoveMe Set iRemoving to 1 RemoveMe is a return. iRemoving isn't being set, so if the script runs again next frame (as it likely does, removeme isn't instant) then the names get swapped around a second time.. not good. Aghh yeah that's a screwup of my making, I've done that one before, I only just added the RemoveMe today as it wasn't there, so unfortunately it's not the issue.
prideslayer Posted March 10, 2015 Posted March 10, 2015 hmmmmmmm Well it looks correct otherwise. As an oldschool programmer like me, you know what to do now. Start spamming debugprints. :/
Guest Posted March 10, 2015 Posted March 10, 2015 Old renaming scripts with MessageBoxEx/SetActorFullName etc. are a lil confusing. New NVSE functions allow you to do it a lil more readable with strings and Get/SetName I copy paste a scrap of code I used: ... elseif iStage == 20 ; Let iStage := 21 Let sMyName := $(Player.GetName) Let sOldAlias := $(rMarkerREF.GetMapMarkerName) Player.SetName $sOldAlias Return elseif iStage == 21 Let iStage := 22 GetPlayerName Return elseif iStage == 22 Let iStage := 0 Let sNewAlias := $(Player.GetName) rMarkerREF.SetMapMarkerName $sNewAlias Let aXMMainQuest.arObjects[2][iIndex] := $sNewAlias Player.SetName $sMyName sv_destruct sMyName sv_destruct sOldAlias sv_destruct sNewAlias ... Stage 20 gives player the name of the reference, so that when you call GetPlayerName you won't see your name but the old reference name. Stage 21 calls GetPlayerName Stage 22 gives the ref the new player name, changes the player name with the original one.
Odessa Posted March 10, 2015 Posted March 10, 2015 Aside: If called on a form, ToString ($) actually returns it's name if it has one, so using GetName (and GetMapMarkerName?) is redundant. Also, if you do use GetName, since it returns a string already, you don't need to use ToString ($) to store it in a string_var: Let sMyName := $(Player.GetName) Let sOldAlias := $(rMarkerREF.GetMapMarkerName) Player.SetName $sOldAlias Could be written more cleanly: Let sMyName := $Player ; # player name as string Let sOldAlias := rMarkerREF.GetMapMarkerName ; # map marker name as string Player.SetName $sOldAlias ; # But, SetName doesn't recognize string_VAR, so you do need $
Guest Posted March 10, 2015 Posted March 10, 2015 I always had a bit of blur on $, when it was working and when it was not. My best deal, in the end, was abounding of it. Actually your simple example just explained me when I need it... thanx
Halstrom Posted March 11, 2015 Author Posted March 11, 2015 Old renaming scripts with MessageBoxEx/SetActorFullName etc. are a lil confusing. New NVSE functions allow you to do it a lil more readable with strings and Get/SetName I copy paste a scrap of code I used: Hmm I tried this but I can't get it to compile, I seem to be missing the SetMarkerName function. Scn SexoutOffSpring0RenameTokenScriptNEW int iStage ref rCreatureOriginalName ref sOldAlias ref $sOldAlias ref sNewAlias ref $sNewAlias ref sMyName ref $sMyName ref rMarkerREF Begin GameMode if iStage == 0 ; *** Copy the Plyers name to somewhere safe and stick the offsprings name in the player pigeon hole Set iStage to 1 Set rCreatureOriginalName to GetContainer ; Set sMyName to $(Player.GetName) ; Set sOldAlias to rMarkerREF.GetMapMarkerName Player.SetName $sOldAlias Return elseif iStage == 1 ; *** Activate the popup change name thingy on screen Set iStage to 2 GetPlayerName Return elseif iStage == 2 ; *** copy the new name to the offspring and copy the original player name back to the player Set iStage to 3 ; Set sNewAlias to $(Player.GetName) ; rMarkerREF.SetMapMarkerName $sNewAlias ; Set aXMMainQuest.arObjects[2][iIndex] to $sNewAlias Player.SetName $sMyName ; sv_truct sMyName ; sv_destruct sOldAlias ; sv_destruct sNewAlias elseif iStage == 3 ; *** delete me from inventory Set iStage to 4 RemoveMe endif End I checked I'm using NVSE 4_6 beta 2 ahh just found beta 7 will try that Nope GECK still won't accept it, doesn't like the := or the sv stuff or the $() stuff Am I missing NVSE 5 or something?
Guest Posted March 11, 2015 Posted March 11, 2015 Oh, sorry SetMarkerName is not intended to be used, that was for my purpose. I wanted to show only the Set/GetNames but I guess I took the most confusing example of code... shame on me.
Halstrom Posted March 11, 2015 Author Posted March 11, 2015 Oh, sorry SetMarkerName is not intended to be used, that was for my purpose. I wanted to show only the Set/GetNames but I guess I took the most confusing example of code... shame on me.No probs, I gutted all that out, I'm getting there, I got to this now but it's not working because I think I need that $() NVSE commands to work somehow when I can get my NVSE sorted out. Scn SexoutOffSpring0RenameTokenScriptNEW int iStage ref rOriginalPlayerName ref rOriginalOffSpringName ref rNewOffSpringName Begin GameMode if iStage == 0 ; *** Copy the Plyers name to somewhere safe and stick the offsprings name in the player pigeon hole Set iStage to 1 Set rOriginalOffSpringName to GetContainer Set rOriginalPlayerName to Player.GetName Player.SetName rOriginalOffSpringName Return elseif iStage == 1 ; *** Activate the popup change name thingy on screen Set iStage to 2 GetPlayerName Return elseif iStage == 2 ; *** copy the new name to the offspring and copy the original player name back to the player Set iStage to 3 Set rNewOffSpringName to Player.GetName Player.SetName rOriginalPlayerName elseif iStage == 3 ; *** delete me from inventory Set iStage to 4 RemoveMe endif End
Odessa Posted March 11, 2015 Posted March 11, 2015 Names are strings, not forms, so you need a string_var to store them not a ref. You need to use let with string_var. string_var player_name ref rContainer int iStage if iStage == 0 let rContainer := GetContainer if rContainer let player_name := $PlayerREF ; # == PlayerREF.GetName PlayerREF.SetName $rContainer ; # SetName requires ToString ($) to accept string_var let iStage := 1 endif ... sv_Destruct player_name
Halstrom Posted March 11, 2015 Author Posted March 11, 2015 Names are strings, not forms, so you need a string_var to store them not a ref. You need to use let with string_var. string_var player_name ref rContainer int iStage if iStage == 0 let rContainer := GetContainer if rContainer let player_name := $PlayerREF ; # == PlayerREF.GetName PlayerREF.SetName $rContainer ; # SetName requires ToString ($) to accept string_var let iStage := 1 endif ... sv_Destruct player_name Thanks Odessa, PS and AJ that helped heaps, I now have: Scn SexoutOffSpring0RenameTokenScriptNEW int iStage ref rOriginalPlayerREF ref rOriginalOffSpringREF string_var sNewOffSpringName string_var sPlayerName Begin GameMode if iStage == 0 let rOriginalOffSpringREF := GetContainer if rOriginalOffSpringREF let sPlayerName := $PlayerREF ; # == PlayerREF.GetName PlayerREF.SetName $rOriginalOffSpringREF ; # SetName requires ToString ($) to accept string_var let iStage := 1 endif elseif iStage == 1 ; *** Activate the popup change name thingy on screen let iStage := 2 GetPlayerName Return elseif iStage == 2 ; *** copy the new name to the offspring and copy the original player name back to the player let iStage := 3 let sNewOffSpringName := PlayerREF.GetName rOriginalOffSpringREF.SetName $sNewOffSpringName PlayerREF.SetName $sPlayerName sv_Destruct sPlayerName sv_Destruct sNewOffSpringName elseif iStage == 3 ; *** delete me from inventory let iStage := 4 RemoveMe endif End It's a pretty simple script, I just needed to learn new language to do it It compiles fine so I'll give it a go
prideslayer Posted March 12, 2015 Posted March 12, 2015 I had a eureka moment a bit earlier to solve a puzzle that's been digging at me for quite a while but I've never really spent the time to investigate. Tested and it works fine. The puzzle is "How can you have a quest var, or something similar, that other mods can check to see if your mod is ready to proceed -- WITHOUT any potential race conditions." An example of one that doesn't work is in the current version of sexout, called "SexoutNG.bSexoutReady". This is a quest var I initially created in the hopes that other mods would check it before making sexout calls or accessing other sexout quest vars. The main quest sets it to 0 on gameloaded/restarted and then back to 1 after everything is ready. It doesn't work reliably because in a savegame, it's 1, and if a mod checks it when the game is loaded in a script that runs before sexout has a chance to set it back to 0, they erroneously think sexout is ready. This however is simple, and works reliably: let rIsReady := TempCloneForm 00SexoutActor TempCloneForms are not saved in savegames, and automatically reset to 0 whenever a game is loaded or a new game is started. So beta2 of the next sexout (v93) will have a new method to determine if sexout is ready. Instead of checking that quest var, there will be a UDF (in case I must change this AGAIN) called "fnSexoutGetReady". If it returns 1, sexout is ready. If not, it's not. For now this will be accomplished by it checking the status of that ref var. Simple, and it works! Use something similar in your own mods and put this nightmare to rest once and for all.
prideslayer Posted March 12, 2015 Posted March 12, 2015 Practical example, this is what I'm actually doing in sexout now (unreleased 93b2). In the quest script restarted/loaded block: ; this is no longer used ; set bSexoutReady to 1 let rIsReady := TempCloneForm 00SexoutActor And the UDF y'all will be calling scn fnSexoutGetReady int bReady Begin GameMode let bReady := 0 if SexoutNG.rIsReady let bReady := 1 endif SetFunctionValue bReady End That's all there is to it.
DoctaSax Posted March 12, 2015 Posted March 12, 2015 I've been thinking that maybe we need to use the potential of custom events a bit more when it comes to timing-sensitive stuff. I've been using some of it in Spunk, to notify other mods that spunk's init cycle is done, it's start & end hooks have finished, and orgasms have happened. The obvious advantage is that a mod that wishes to start something depending on such events doesn't need to periodically check anything, just register a UDF for the event and wait for it to be triggered. You'd just broadcast a "SexoutReady" event and that'd be it. People start whatever they need to in that UDF. (I know the following is probably make folks nervous about changing the interface yet again, but technically sexout itself could register a UDF for a "SexoutStart" event that modders would broadcast, specifying the parameters with the optional stringmap. You'd have everything in there that you'd need and wouldn't even need to make sexout a master.)
prideslayer Posted March 12, 2015 Posted March 12, 2015 I'll gladly do that, if it'll work. Seems like I could support both no problem (the notification and the polling), but think about this: How is your own mod going to know sexout has actually sent the notification -- or that you just think it has, because you recorded that it was sent in a quest var or something that got loaded in a save? That var that you set to 0 in your gameloaded/restarted block and then don't set to 1 until you get the event will still itself be 1 if the user saves after the event. Even if you set it to 0 again, that's not proof against your own scripts in spells and such in the save running before you've set it back to 0 on a game load. Calling the UDF in sexout everywhere you need it will ensure it's actually ready. You can even check it in the gamemode or scripteffectupdate blocks of your spells and quests that may also be present in the save game, so they 'pause' when a game is loaded until sexout is ready.
DoctaSax Posted March 12, 2015 Posted March 12, 2015 I'll gladly do that, if it'll work. Seems like I could support both no problem (the notification and the polling), but think about this: How is your own mod going to know sexout has actually sent the notification -- or that you just think it has, because you recorded that it was sent in a quest var or something that got loaded in a save? That var that you set to 0 in your gameloaded/restarted block and then don't set to 1 until you get the event will still itself be 1 if the user saves after the event. Even if you set it to 0 again, that's not proof against your own scripts in spells and such in the save running before you've set it back to 0 on a game load. Calling the UDF in sexout everywhere you need it will ensure it's actually ready. You can even check it in the gamemode or scripteffectupdate blocks of your spells and quests that may also be present in the save game, so they 'pause' when a game is loaded until sexout is ready. Well, that's just a matter of modders implementing it right and what type of mod they're making. A functionality mod that checks any act that goes on, like spunk or wear & tear, would be obvious choices to listen in on that, while mods that set off acts would probably need that UDF instead to check at the moment before the act starts. I'm just putting it out there that custom events are pretty powerful stuff, especially considering the optional stringmap. Atm, every time an orgasm happens in spunk, I broadcast a shitload of intel about it, in addition to the mere fact that it happened. If another mod picks it up, all the better, if not, nothing lost. I don't feel I strictly need to check the ready var for spunk, for instance, because my own init cycle lasts long enough for NG to do whatever it needs to. However, for prettiness' sake, I could hold off my own init cycle until such an event tells me NG is ready. They could also be a prettier solution to registering start and end hook scripts. Instead of adding spells to a formlist that you loop through & then cast, maybe just tell us "SexoutActStart" or "SexoutActEnd" and we'll be notified if we register a UDF for it and take it from there, if you provided some intel in the stringmap like the spelltarget etc, or who knows, pretty much every variable that was set could be sent along. Not saying only do it that way from on or do it rightaway. Just saying it's there to be used.
prideslayer Posted March 12, 2015 Posted March 12, 2015 I'm just putting it out there that custom events are pretty powerful stuff, especially considering the optional stringmap. Atm, every time an orgasm happens in spunk, I broadcast a shitload of intel about it, in addition to the mere fact that it happened. If another mod picks it up, all the better, if not, nothing lost. I've never used them or even looked at them, but I'm more than willing to implement a ready broadcast, as well as the global start/stop callbacks this way in addition to the existing registration system. I don't feel I strictly need to check the ready var for spunk, for instance, because my own init cycle lasts long enough for NG to do whatever it needs to. That's the idea everyone has worked under I think, but it doesn't always work. This came to me after my existing ready and init/upgrade in the current beta failed to have some quest array vars ready before Odessas ESP tried to use them via UDFs. I managed that by putting the ar_constructs and checks for ar_null inside the UDF as well as the main quest script, so if the UDF was called before sexouts update was finished, the arrays wouldn't be null. However, for prettiness' sake, I could hold off my own init cycle until such an event tells me NG is ready. They could also be a prettier solution to registering start and end hook scripts. Instead of adding spells to a formlist that you loop through & then cast, maybe just tell us "SexoutActStart" or "SexoutActEnd" and we'll be notified if we register a UDF for it and take it from there, if you provided some intel in the stringmap like the spelltarget etc, or who knows, pretty much every variable that was set could be sent along. Not saying only do it that way from on or do it rightaway. Just saying it's there to be used. Yep I like the idea of using them for the global hooks. That system is a massive hack right now. Don't go peeking into how it works, it's embarrassing but the best I could do at the time. It predates NX and all this fancy NVSE stuff. Also I know Hal has a bunch of timers in SCR that wait for things to be ready that take an indeterminate amount of time. He could use the events and/or the ready UDF as well.
DoctaSax Posted March 12, 2015 Posted March 12, 2015 Yep I like the idea of using them for the global hooks. That system is a massive hack right now. Don't go peeking into how it works, it's embarrassing but the best I could do at the time. It predates NX and all this fancy NVSE stuff. Well understood. It's just screaming for it now that you've moved toward having people start things with a UDF with a stringmap parameter too. I'd like nothing better than to have a UDF ready to be notified like array_var args ref tgt begin Function {args} let myquest.args := ar_copy args ; everything I need in one place let tgt:= args["SpellTarget"] tgt.CIOS myStartHookSpell ; looking up whatever it needs in myquest.args & erasing when done End
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