Jump to content

[LE] Multiple Inheritance in Papyrus, possible solutions?


Starwhip

Recommended Posts

I have a good amount of programming experience outside of Papyrus so I have a decent idea of what I want to do, just not how I want to do it:

 

I'm looking to create a script/program/series of scripts that periodically increments a counter and causes the player to cast a spell upon reaching a threshold, resetting the counter to zero, and continuing. Certain basic items in player inventory will cause the counter to increment faster.

 

What I have working so far: While it's not super pretty and I'm sure breaks a lot of modding convention, I set up a quest to start the script on a new game. It calls RegisterForSingleUpdate, and on the OnUpdate Event, It increments the counter and then calls for another update.

 

What I haven't been able to get working: From what I can tell, I can't have this particular script check for items or cast spells because the script would have to extend ObjectReference for the former or something else I haven't researched for the latter. It currently extends Quest in order to run on game creation.

 

Basically my code looks like this right now:

ScriptName Blah extends Quest
ObjectReference Property MountainFlower01Blue auto

Event OnInit()
    RegisterForSingleUpdate(10)
EndEvent

Event OnUpdate()
    Debug.Notification(Game.GetPlayer().GetItemCount(MountainFlower01Blue))
    RegisterForSingleUpdate(10)
EndEvent

If I can do this, what am I doing wrong?

Any solutions/tips would be appreciated. Will I need several scripts managing these things somehow? Is this possible in Papyrus?

Have I just missed something glaringly obvious since it's 2am? :mellow:

Link to comment
Share on other sites

Multiple Inheritance? Most probable not :) It not Java Mate LOL It just simple coding language, each child can only extend one parent or super class.

 

For what your trying to achieve I have never done it, but what I would do is check out how the game handles step and fetch this many items quests, the best plan to not reinvent the wheel but shameless copy bethesda.

 

Cheers you made me smile. Think of Papyrus like some variant of Microsoft Code, since C# still isn't a true Object Oriented Language either.

Link to comment
Share on other sites

Multiple Inheritance? Most probable not :smile: It not Java Mate LOL It just simple coding language, each child can only extend one parent or super class.

 

For what your trying to achieve I have never done it, but what I would do is check out how the game handles step and fetch this many items quests, the best plan to not reinvent the wheel but shameless copy bethesda.

 

Cheers you made me smile. Think of Papyrus like some variant of Microsoft Code, since C# still isn't a true Object Oriented Language either.

 

Yeah, thinking it over I think I might be able to tie it to a quest or set of quests:

Counter script updates, sets quest stage. That triggers another script to read player inventory and bump quest stage further based on the result. Final quest stage will fire a script to do the desired actions before setting the quest back to the starting stage, and since the counter will keep going it should be a continuous loop.

 

EDIT: In this case, the counter script doesn't need to track anything itself, it will just act as the method of moving the whole "program" along

Edited by Starwhip
Link to comment
Share on other sites

use a player alias inside the quest, with script as follow, should be adjusted to reach your aim

 

starwPlayerAliasScript

 

Scriptname starwPlayerAliasScript extends ReferenceAlias
; https://forums.nexusmods.com/index.php?/topic/8301768-multiple-inheritance-in-papyrus-possible-solutions/

; scripts that periodically increments a counter and causes the player to cast a spell upon reaching a threshold
; resetting the counter to zero, and continuing.

  Spell   PROPERTY mySpell auto                ; the spell periodically to cast
  Keyword PROPERTY myKwd   auto                ; a keyword attached to a magiceffect of spell from above

  Flora Property MountainFlower01Blue auto

  Int PROPERTY iCounter auto Hidden            ; [default=0]
  Int iVer


; -- FUNCTIONs -- 2

;------------------
FUNCTION myF_Cast()
;------------------
    objectReference playerRef = self.GetReference()

IF playerRef.HasKeyword(myKwd) || (playerRef as Actor).IsInCombat()
    RETURN    ; - STOP -    spell is still working on player (if using a cloak spell) /or/ player is in combat
ENDIF
;---------------------
    iCounter = iCounter + 1

    int i = playerRef.GetItemCount(MountainFlower01Blue as Form)
    IF (i > 10)
        iCounter = iCounter + 5
    ENDIF

    IF (iCounter >= 50)
        mySpell.Cast(playerRef, playerRef)
        Debug.Notification("Spell casted..")
        iCounter = 0
    ENDIF
ENDFUNCTION

;------------------
FUNCTION myF_Init()
;------------------
    iVer = 1
    UnRegisterForUpdateGameTime()
    RegisterForSingleUpateGameTime(0.0)
ENDFUNCTION


; -- EVENTs --

EVENT OnInit()        ; new game or quest applies first time on saved game
; Received immediately after quest has finished initialization
    iVer = -1
    Debug.Trace(" OnInit() - has been reached.. " +self)
    myF_Init()
ENDEVENT


EVENT OnPlayerLoadGame()    ; every time saved game will be started
; Received immediately after the player has loaded a save game. A good time to check for additional content.
    Debug.Trace(" OnPlayeroadGame() - has been reached.. " +self)
    IF (iVer == -1)
    ELSE
        myF_Init()
    ENDIF
ENDEVENT


EVENT OnUpdateGameTime()
    Utility.Wait(5.0)                ; wait for 5 sec, (Do not wait to long here !!!)

    quest q = self.GetOwningQuest()
    IF (q) && q.IsRunning()
        myF_Cast()
        RegisterForSingleUpdateGameTime(0.0)
    ENDIF
ENDEVENT

; *******************************************************************************
; *** other events, which could be useful to handle ***
; *******************************************************************************

EVENT OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
; Event received when this object is hit by a source (weapon, spell, explosion) or projectile attack

ENDEVENT


EVENT OnMagicEffectApply(ObjectReference akCaster, MagicEffect akEffect)
; Event received when a magic affect is being applied to this object

ENDEVENT


EVENT OnSpellCast(Form akSpell)
; Event received when a spell is cast by this object

ENDEVENT


EVENT OnCombatStateChanged(Actor akTarget, int aeCombatState)
; Event that is triggered when this actors combat state against the target changes
; State is as follows:
; 0 - not in combat
; 1 - in combat
; 2 - searching

ENDEVENT


EVENT OnPlayerBowShot(Weapon akWeapon, Ammo akAmmo, float afPower, bool abSunGazing)
; Received when the player fires a bow. akWeapon will be a bow, akAmmo is the ammo or None,
; afPower will be 1.0 for a full-power shot, less for a dud, and abSunGazing will be true if the player is looking at the sun.

ENDEVENT

 

 

Edited by ReDragon2013
Link to comment
Share on other sites

use a player alias inside the quest, with script as follow, should be adjusted to reach your aim

 

Compiled this script to a new quest with the player alias, it runs the counter at least. The check for item count does not work, and I have the spell section commented out for the moment.

 

I've tried making the initial declaration ObjectReference instead of Flora, and I've tried removing the cast to Form in GetItemCount(). Do I need to set something up outside of the script for the item checks and spell casting to work?

Link to comment
Share on other sites

The item check in ReDragon2013's script won't work because the property for the blue mountain flower is set as Flora. The player can not pick up the plant itself. The player gets the Ingredient object instead. I'm unsure of the exact ID name for the auto fill to work, but the property should be something like:

 

Ingredient Property BlueMountainFlower Auto

 

Then later when doing the item check, there is no need to cast to Form. Just pass in the ingredient variable:

 

int i = PlayerRef.GetItemCount(BlueMountainFlower)

Link to comment
Share on other sites

The item check in ReDragon2013's script won't work because the property for the blue mountain flower is set as Flora. The player can not pick up the plant itself. The player gets the Ingredient object instead. I'm unsure of the exact ID name for the auto fill to work, but the property should be something like:

 

Ingredient Property BlueMountainFlower Auto

 

Then later when doing the item check, there is no need to cast to Form. Just pass in the ingredient variable:

 

int i = PlayerRef.GetItemCount(BlueMountainFlower)

No dice. I think the issue isn't the properties or the GetItemCount function, just that the script cannot find the player properly.

My ref alias for the player is currently set up like this, and is probably wrong.

 

m2lIS38.png

The script extends ReferenceAlias, calls objectReference playerRef = self.GetReference(), and then uses playerRef for future calls. I tested Game.GetPlayer() as a substitute but that does not work either.

Edited by Starwhip
Link to comment
Share on other sites

Be sure to test on a new game rather than one that has had your mod active. This is to rule out data that may have been baked into the save file from interfering with your testing.

 

Also, your image shows that you have not assigned any data to the properties. If you had there would be a little pencil at the bottom of the plus icon next to the script name. Highlight the script, click the properties button and assign the correct data to the properties. Otherwise, the property variables will be without a value and the script will not perform the way that you want.

Link to comment
Share on other sites

Ishara's correct. It's a classic script toubleshooting thing that catches, in particular, people coming in from a programming background: Simply compiling the code up does not necessarily create a complete script. If a script needs to interact with a game object, you need to feed it the link between that game object and whatever variable you've coded in.

There's three ways of doing that: You pull the information out of a function or event that can provide it, you attach the script to it directly or through an alias, or you fill properties. They aren't just a weird name for variables.

 

 

In your particular case I would put the script on a player alias. ReferenceAliases like that will receive basically the same events as the Actor they're attached to, as well as certain ones tied to whatever the quest the alias belongs to. This will include an OnInit() event that will fire when the quest is first loaded (which will be a double-fire for a start-game-enabled quest, but that's only relevant if your OnInit() stuff pipes information to a script outside the quest).

 

 

And no, you can only ever inherit from one line of inheritance at a time. One of the handiest references on the CK wiki is this one, which shows the inheritance tree:

 

https://www.creationkit.com/index.php?title=Category:Script_Objects

 

It's also possible your quest isn't starting or the player alias isn't being filled -- I have found using a forced reference to PlayerRef seems to behave better than the UniqueActor for making player aliases -- and to check that, you want the 'sqv QuestName' console command. It'll spit back the quest status and stage and the objectIDs of everything in aliases, among others.

 

Also as Ishara said, when testing mods, and especially when making changes to mods, start new games. I've used 'coc whiterun' so many times to start a new one for a test that I can recite Adriane's conversation with Idolaf word for word. :p

Link to comment
Share on other sites

Be sure to test on a new game rather than one that has had your mod active. This is to rule out data that may have been baked into the save file from interfering with your testing.

 

Also, your image shows that you have not assigned any data to the properties. If you had there would be a little pencil at the bottom of the plus icon next to the script name. Highlight the script, click the properties button and assign the correct data to the properties. Otherwise, the property variables will be without a value and the script will not perform the way that you want.

 

Ah, this has done it. Was missing this in the original script as well, it probably would have worked otherwise :P

 

Solution for anyone coming across this thread via Google-fu: Make sure you go to where your script is attached in the Creation Kit, select it, press Properties, and fill your script-defined properties with objects from the Creation Kit.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...