Galcyon Posted June 8, 2019 Share Posted June 8, 2019 (edited) So, after combing through the mods on Nexus and the Internet, I found something. Nobody (seems to) have made a mod that makes the Necromage Vampire build easier to make/handle. And by that, I mean nobody seems to have tried to create a mod that will apply bonuses granted by Necromage retroactively to any perks/permanent magic effects granted by quests that were taken before the player both became a vampire and took Necromage. I think you all see where this is going. The idea is that the script will run in the background. Once it detects that the player both has vampirism and has taken the Necromage perk, it will then run through the list of perks and permanent magic effects that can be boosted by Necromage, and will subsequently remove and replace any of these that the player may have already acquired. Ideally, there will be a second or so of delay between removal and replacement, so the script doesn't accidentally do something stupid and, say, duplicate a perk, then remove the duplicate. Now, here's where I need help. I haven't got a clue as to how to script in Papyrus. I have worked with scripting in prior Bethesda Software titles before, but it was circa Fallout 3/New Vegas and Oblivion. So, unless someone wants to provide me with the framework where I can then plug in the perks/abilities (I'm not asking for this), I need to know how to do the following, or what the following commands are: 1. Adding, removing, and detecting a specific magical effect on a player.2. Setting up a timer as insurance to prevent the script from doing things out of order.3. How to either run the script in the background, or (at the absolute worst), attach it to a lesser ability that the player can cast at their convenience. Edited June 8, 2019 by Galcyon Link to comment Share on other sites More sharing options...
ReDragon2013 Posted June 10, 2019 Share Posted June 10, 2019 (edited) You are able to add/remove spells, it is nearly impossible to remove a magic effect which is hanging on the player, because effects are stacking. Next two script could be a base for you. GalcNecromage_MGEFScript Scriptname GalcNecromage_MGEFScript extends ActiveMagicEffect ; Adding and removing specific magical effect on a player. ; https://forums.nexusmods.com/index.php?/topic/7715458-retroactive-necromage-need-scripting-help/ MagicEffect PROPERTY myEffect auto ; fill with the effect you want to observe ; -- EVENTs -- EVENT OnEffectStart(Actor akTarget, Actor akCaster) ; received when this effect is first started (OnInit may not have been run yet!) Debug.Trace("OnEffectStart() - target = " +akTarget+ ", caster = " +akCaster+ " " +self) ; info only IF akTarget == Game.GetPlayer() ELSE self.Dispel() RETURN ; - STOP - player is different to target ENDIF ;--------------------- IF akTarget.HasMagicEffect(myEffect) ; effect should be removed self.Dispel() ELSE ; effect will be added ENDIF ENDEVENT EVENT OnEffectFinish(Actor akTarget, Actor akCaster) ; received when this effect is finished (effect may already be deleted, calling functions on this effect will fail) Debug.Trace("OnEffectFinish() - target = " +akTarget+ ", caster = " +akCaster+ " " +self) ; info only ENDEVENT For next script you have to create a new quest, add an Alias use "unique player" to fill the alias, and attach the script.GalcNecromage_PlayerAliasScript Scriptname GalcNecromage_PlayerAliasScript extends ReferenceAlias ; https://forums.nexusmods.com/index.php?/topic/7715458-retroactive-necromage-need-scripting-help/ MagicEffect PROPERTY myEffect auto ; fill with the effect you want to observe ; -- EVENTs -- ; switch on cookies in your browser to show next ; https://www.creationkit.com/index.php?title=GetOwningQuest_-_Alias ; https://www.creationkit.com/index.php?title=IsInFaction_-_Actor ; https://www.creationkit.com/index.php?title=HasMagicEffect_-_Actor EVENT OnInit() ; received first time by adding this RefAlias to a new or loaded game (can be run twice depends on quest flag) Debug.Trace("OnInit() - has been triggered.. " +self) ENDEVENT EVENT OnPlayerLoadGame() ; received immediately after the player has loaded a save game. A good time to check for additional content. Debug.Trace("OnPlayerLoadGame() - has been triggered.. playerRace is " + Game.GetPlayer().GetRace() + ", " +self) ENDEVENT EVENT OnDying(Actor akKiller) ; triggered when this actor begins dying (only if this alias points at an actor) UnRegisterForUpdate() quest q = self.GetOwningQuest() q.Stop() ; stop my quest, player will dying! ENDEVENT EVENT OnRaceSwitchComplete() ; received when this actor finishes changing its race myF_Test() ENDEVENT EVENT OnMagicEffectApply(ObjectReference akCaster, MagicEffect akEffect) ; received when a magic affect is being applied to this object or actor IF (akEffect == myEffect) ; player got the special effect gotoState("Waiting") ; ### STATE ### now test: Is the effect still running? RegisterForSingleUpdate(1.0) ENDIF ENDEVENT ;======================================= state Waiting ;============ EVENT OnUpdate() IF Game.GetPlayer().HasMagicEffect(myEffect) RegisterForSingleUpdate(5.0) ; check every 5 seconds again ELSE ; player has lost the special effect gotoState("") ; ### STATE ### ENDIF ENDEVENT ;======= endState ; -- FUNCTIONs -- ;------------------ FUNCTION myF_Test() ;------------------ keyword kw = Game.GetForm(0x000A82BB) as Keyword ; Vampire [KYWD:000A82BB] IF Game.GetPlayer().HasKeyword(kw) ; player is a vampire ELSE ; player is of human race ENDIF ENDFUNCTION Edited June 10, 2019 by ReDragon2013 Link to comment Share on other sites More sharing options...
Galcyon Posted June 10, 2019 Author Share Posted June 10, 2019 (edited) You are able to add/remove spells, it is nearly impossible to remove a magic effect which is hanging on the player, because effects are stacking.Yeah, based on some independent research, it looks like I can just use the typical hasspell/addspell/removespell commands on the quest reward abilities, since the CK tags them as spells, not effects. Remove the ability should effectively remove the effect it's connected to, correct? I'm also having a hard time wrapping my head around those scripts; like I said, I have no idea how to work Papyrus, and what I need to alter isn't popping out at me. Does "myEffect" need to be changed, and if so, doesn't that mean there's going to be MASSIVE list of "MagicEffect PROPERTY myEffect auto" at the beginning of both scripts? Seems kinda excessive for what should be a "detect if two values are true, and then add/replace certain perks/spells if the player has them" script. Edited June 10, 2019 by Galcyon Link to comment Share on other sites More sharing options...
Galcyon Posted June 12, 2019 Author Share Posted June 12, 2019 (edited) Ok, so I've written up a script that I'm fairly certain will not work as-is (mostly becasue I am unfamiliar with Papyrus, and I can't understand the script ReDragon2013 posted above), and I'd like to know what I need to change to make it run. It's not the complete script, since I removed repeats of if/endif blocks that did the same thing to different perks/spells. Scriptname RetrogradeNecromage :this section checks to see if the player is eligible for the script's effects if (game.GetPlayer().hasperk(necromage)) and (PlayerRef.Haskeyword(vampire)) == True: :this section checks which quest reward abilities the player currently has and automaticlly resets them if (game.GetPlayer().hasspell(T01DibellaRewardAbility)) Game.getplayer().removespell(T01DibellaRewardAbility) Game.getplayer().addspell(T01DibellaRewardAbility, false) endif (above format is repeated for multiple abilities) ;this section checks which perks the player currently has and automatically resets them if PlayerRef.hasperk(atronach) PlayerRef.removeperk(atronach) playerRef.addperk(atronach, false) endif (above format is repeated for multiple perks) endif else end One problem I already assume will happen is that the script will continuously remove and replace listed perks/abilties once the player is eligible for the script to affect them, which is undesirable. I assume that putting in an "int RunOnce" at the top and setting the primary if block to check if RunOnce == 0 will solve it, but I'm not sure how to introduce it into the primary if block, since I'm checking if two things are true, and that RunOnce is false (0). I'm also not 100% sure that I have the script pointers to the player correct, I'm seeing multiple ways to refer to the player, and my search-fu on the CK wiki isn't working too well. And I assume that there's other errors that need to be fixed before it will run, since again I'm unfamiliar with Papyrus. -EDIT- Yeah, most of the script is rendered as purple. Pretty sure that means "this doesn't work!" Edited June 12, 2019 by Galcyon Link to comment Share on other sites More sharing options...
ReDragon2013 Posted June 12, 2019 Share Posted June 12, 2019 (edited) first you should keep in mind ; this is a papyrus comment (a leading semicolon) : this is bad (a colon only)next split your conditions like next sample and use an internal actor variable to to store the result of calling Game.GetPlayer() Perk PROPERTY necromage auto ; fill with your new created perk Perk PROPERTY atronach auto ; Spell PROPERTY T01DibellaRewardAbility auto ; https://www.creationkit.com/index.php?title=RemoveSpell_-_Actor ; https://www.creationkit.com/index.php?title=HasPerk_-_Actor ; https://www.creationkit.com/index.php?title=AddPerk_-_Actor ;------------------ FUNCTION myF_Test() ;------------------ actor player = Game.GetPlayer() ; get the internal variable once here IF player.HasKeyword(vampire) ELSE RETURN ; - STOP - player is of human race ENDIF ;--------------------- IF player.HasPerk(necromage) ELSE RETURN ; - STOP - player is a vampire, but does not have the special necromage perk ENDIF ;--------------------- ; selection 1 ; ~~~~~~~~~~~ TryToReset_Spell(player, T01DibellaRewardAbility) ; selection 2 ; ~~~~~~~~~~~ TryToReset_Perk(player, atronach) ENDFUNCTION ;------------------------------------------------ FUNCTION TryToReset_Spell(Actor player, Spell sp) ;------------------------------------------------ IF player.RemoveSpell(sp) Utility.Wait(0.1) ; wait a bit.. How long try it out? player.AddSpell(sp, False) ; reset this ability ENDIF ENDFUNCTION ;--------------------------------------------- FUNCTION TryToReset_Perk(Actor player, Perk p) ;--------------------------------------------- IF player.HasPerk(p) player.RemovePerk(p) Utility.Wait(0.1) ; wait a bit player.AddPerk(p, False) ; reset perk ENDIF ENDFUNCTIONUse this link: https://www.creationkit.com/index.php?title=Category:Scripting to get more information about native papyrus functions Edited June 12, 2019 by ReDragon2013 Link to comment Share on other sites More sharing options...
Galcyon Posted June 12, 2019 Author Share Posted June 12, 2019 Thanks. That example/base script is far more straightforward in terms of what's going on (for example, I have to define individual perks and spells as properties, so I can then use them in a function (and bypassing the need to individually code a bunch of if/endif blocks)). If I understood it right, the full script (which will includes all the various perks and abilities) should look something like this? Perk PROPERTY necromage auto ; fill with your new created perk Perk PROPERTY atronach auto ; Perk PROPERTY AvoidDeath auto ; Perk PROPERTY DualFlurry30 auto ; Spell PROPERTY T01DibellaRewardAbility auto Spell PROPERTY PerkMara auto Spell PROPERTY MS04Reward auto ; https://www.creationkit.com/index.php?title=RemoveSpell_-_Actor ; https://www.creationkit.com/index.php?title=HasPerk_-_Actor ; https://www.creationkit.com/index.php?title=AddPerk_-_Actor ;------------------ FUNCTION myF_Test() ;------------------ actor player = Game.GetPlayer() ; get the internal variable once here IF player.HasKeyword(vampire) ELSE RETURN ; - STOP - player is of human race ENDIF ;--------------------- IF player.HasPerk(necromage) ELSE RETURN ; - STOP - player is a vampire, but does not have the special necromage perk ENDIF ;--------------------- ; selection 1 ; ~~~~~~~~~~~ TryToReset_Spell(player, T01DibellaRewardAbility) TryToReset_Spell(player, PerkMara) TryToReset_Spell(player, MS04Reward) ; selection 2 ; ~~~~~~~~~~~ TryToReset_Perk(player, atronach) TryToReset_Perk(player, AvoidDeath) TryToReset_Perk(player, DualFlurry30) ENDFUNCTION ;------------------------------------------------ FUNCTION TryToReset_Spell(Actor player, Spell sp) ;------------------------------------------------ IF player.HasSpell(sp) player.RemoveSpell(sp) Utility.Wait(0.1) ; wait a bit.. How long try it out? player.AddSpell(sp, False) ; reset this ability ENDIF ENDFUNCTION ;--------------------------------------------- FUNCTION TryToReset_Perk(Actor player, Perk p) ;--------------------------------------------- IF player.HasPerk(p) player.RemovePerk(p) Utility.Wait(0.1) ; wait a bit player.AddPerk(p, False) ; reset perk ENDIF ENDFUNCTION Link to comment Share on other sites More sharing options...
Galcyon Posted June 15, 2019 Author Share Posted June 15, 2019 (edited) Ok, so assuming that the last edits are good, I've gone ahead and added in one final check for the script to make sure it runs when I want it to; adding an integer variable so that the script will not run after it has replaced everything the first time around. I'm pretty sure I can already control for that by doing something with quest advances on a trigger script, but it can't hurt to double-check to see if I understood how to set up and advance integer variables, y'know? So, the script below, once added into the game, should check for three things: if the script has already run through and replaced things, if the player is a vampire, and if the player has the Necromage perk. If the answers are NO, YES, and YES, then it will replace 7 specific perks (Atronach, Avoid Death, Dual Flurry 1 and 2, Extra Pockets, Fists of Steel, and Magic Resist 1) and 7 specific abilities (Agent of Dibella, Agent of Mara, Ancient Knowledge, Dragon Infusion, Prowler's Profit, Sailor's Repose, and Sinderon's Serendipity) if the player has them, and tick off that the script has gone through and replaced things. Perk PROPERTY necromage auto ; fill with your new created perk Perk PROPERTY atronach auto ; Perk PROPERTY AvoidDeath auto ; Perk PROPERTY DualFlurry30 auto ; Perk PROPERTY DualFlurry50 auto ; Perk PROPERTY ExtraPockets auto ; Perk PROPERTY FistsOfSteel auto ; Perk PROPERTY MagicResistance30 auto ; Spell PROPERTY T01DibellaRewardAbility auto Spell PROPERTY PerkMara auto Spell PROPERTY MS04Reward auto Spell PROPERTY BladesDragonInfusionAbility auto Spell PROPERTY TGCrownProfit auto Spell PROPERTY dunFrostflowAbyssBoon auto Spell PROPERTY NN01Spell auto int RunOnce ; this sets up a variable to prevent the script from constantly resetting perks and abilities once conditions have been met ; https://www.creationkit.com/index.php?title=RemoveSpell_-_Actor ; https://www.creationkit.com/index.php?title=HasPerk_-_Actor ; https://www.creationkit.com/index.php?title=AddPerk_-_Actor ;------------------ FUNCTION myF_Test() ;------------------ actor player = Game.GetPlayer() ; get the internal variable once here IF int RunOnce == 0 ELSE RETURN ; - STOP - player already has had their perks and abilities reset by this script ENDIF ;--------------------- IF player.HasKeyword(vampire) ELSE RETURN ; - STOP - player is of human race ENDIF ;--------------------- IF player.HasPerk(necromage) ELSE RETURN ; - STOP - player is a vampire, but does not have the special necromage perk ENDIF ;--------------------- ; selection 1 ; ~~~~~~~~~~~ TryToReset_Spell(player, T01DibellaRewardAbility) TryToReset_Spell(player, PerkMara) TryToReset_Spell(player, MS04Reward) TryToReset_Spell(player, BladesDragonInfusionAbility) TryToReset_Spell(player, TGCrownProfit) TryToReset_Spell(player, dunFrostflowAbyssBoon) TryToReset_Spell(player, NN01Spell) ; selection 2 ; ~~~~~~~~~~~ TryToReset_Perk(player, atronach) TryToReset_Perk(player, AvoidDeath) TryToReset_Perk(player, DualFlurry30) TryToReset_Perk(player, DualFlurry50) TryToReset_Perk(player, ExtraPockets) TryToReset_Perk(player, FistsOfSteel) TryToReset_Perk(player, MagicResistance30) ;--------------------- RunOnce = 1 ; This prevents the script from constantly resetting perks and abilities ENDFUNCTION ;------------------------------------------------ FUNCTION TryToReset_Spell(Actor player, Spell sp) ;------------------------------------------------ IF player.HasSpell(sp) player.RemoveSpell(sp) Utility.Wait(0.1) ; wait a bit.. How long try it out? player.AddSpell(sp, False) ; reset this ability ENDIF ENDFUNCTION ;--------------------------------------------- FUNCTION TryToReset_Perk(Actor player, Perk p) ;--------------------------------------------- IF player.HasPerk(p) player.RemovePerk(p) Utility.Wait(0.1) ; wait a bit player.AddPerk(p, False) ; reset perk ENDIF ENDFUNCTION So, did I do it right? Yes/No. Edited June 15, 2019 by Galcyon Link to comment Share on other sites More sharing options...
ReDragon2013 Posted June 15, 2019 Share Posted June 15, 2019 (edited) Keep in mind functions do not start from alone, only native events like my first posting here, have the ability to run so!Make sure you understand the different between local variable and script variable or property script code should be changed as follow: Bool RunOnce ; [default=False] ; this sets up a variable to prevent the script from constantly resetting perks and abilities once conditions have been met ;------------------ FUNCTION myF_Test() ;------------------ IF ( RunOnce ) RETURN ; - STOP - player already has had their perks and abilities reset by this script ENDIF ;--------------------- RunOnce = TRUE ; switch value of script variable (from False to TRUE) to prevent constantly resetting actor player = Game.GetPlayer() ; get the player formID and store it once here to a local variable, which have to be defined IF player.HasKeyword(vampire) ELSE RunOnce = False ; *** EDIT by future ask RETURN ; - STOP - player is of human race ENDIF ;--------------------- IF player.HasPerk(necromage) ELSE RunOnce = False ; *** EDIT by future ask RETURN ; - STOP - player is a vampire, but does not have the special necromage perk ENDIF ;--------------------- ; selections here ENDFUNCTION conditions may have different syntax, but they lead to the same result IF (RunOnce == TRUE) RETURN ; - STOP - ENDIF ;--------------------- RunOnce = TRUE IF ( RunOnce ) RETURN ; - STOP - player already has had their perks and abilities reset by this script ENDIF ;--------------------- RunOnce = TRUE IF (RunOnce as Int == 1) ; IF (RunOnce == 1) RETURN ; - STOP - ENDIF ;--------------------- RunOnce = (1 as Bool) ; RunOnce = 1conditions from above now with negative check IF (RunOnce == False) ELSE RETURN ; - STOP - ENDIF ;--------------------- RunOnce = TRUE IF ( !RunOnce ) ELSE RETURN ; - STOP - ENDIF ;--------------------- RunOnce = TRUE IF (RunOnce as Int == 0) ; IF (RunOnce == 0) ELSE ; ELSE RETURN ; - STOP - ENDIF ;--------------------- RunOnce = (1 as Bool) ; RunOnce = 1 Edited June 15, 2019 by ReDragon2013 Link to comment Share on other sites More sharing options...
Galcyon Posted June 15, 2019 Author Share Posted June 15, 2019 One question: Wouldn't the placement of RunOnce = TRUE in your changes cause the script to mark that it's reset everything before it has actually checked if the player is a vampire and has the perk? Granted, the script shouldn't be set up to fire off unless both are true anyway, but it looks like that if it encounters any issues with it firing off prematurely (say, a player has a glitch causing the script to fire off only when they have Necromage but aren't a vampire, thereby causing it to not replace anything), then once they actually do qualify for the script to run, it'll refuse to do anything because the first value it checks is being used as "did I already do this?" It seems like having it changed to Bool RunOnce ; [default=False] ; this sets up a variable to prevent the script from constantly resetting perks and abilities once conditions have been met ;------------------ FUNCTION myF_Test() ;------------------ IF ( !RunOnce ) ELSE RETURN ; - STOP - player already has had their perks and abilities reset by this script ENDIF ;--------------------- actor player = Game.GetPlayer() ; get the player formID and store it once here to a local variable, which have to be defined IF player.HasKeyword(vampire) ELSE RETURN ; - STOP - player is of human race ENDIF ;--------------------- IF player.HasPerk(necromage) ELSE RETURN ; - STOP - player is a vampire, but does not have the special necromage perk ENDIF ;--------------------- ; selections here RunOnce = TRUE ; switch value of script variable (from False to TRUE) to prevent constantly resetting ENDFUNCTION where its first check being to see if it RunOnce is False and moving the command to make it true after the selection section (thereby making it the last thing the script does before stopping instead of the first thing it does when it runs) would solve any issues if the script glitches and fires off prematurely. Link to comment Share on other sites More sharing options...
ReDragon2013 Posted June 15, 2019 Share Posted June 15, 2019 (edited) You are right, I miss that (preview post has been edited).Nevertheless I hope you found the right place to call the function myF_Test() within a papyrus event. Edited June 15, 2019 by ReDragon2013 Link to comment Share on other sites More sharing options...
Recommended Posts