Jump to content

[LE] Retroactive Necromage- need scripting help!


Recommended Posts

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 by Galcyon
Link to comment
Share on other sites

  • Replies 59
  • Created
  • Last Reply

Top Posters In This Topic

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 by ReDragon2013
Link to comment
Share on other sites

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 by Galcyon
Link to comment
Share on other sites

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 by Galcyon
Link to comment
Share on other sites

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
ENDFUNCTION

Use this link: https://www.creationkit.com/index.php?title=Category:Scripting to get more information about native papyrus functions

Edited by ReDragon2013
Link to comment
Share on other sites

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

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 by Galcyon
Link to comment
Share on other sites

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 = 1

conditions 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 by ReDragon2013
Link to comment
Share on other sites

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

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...