Jump to content

[LE] Need help advancing quest stage upon item pick up


Recommended Posts

1 hour ago, scorrp10 said:

I might have made a few minor changes (i.e. moved objective handling into the quest stage fragments)  Other than that the overall structure is the same.

i see. i've checked your mod that you provided and checked the quest. its pretty much as i've set up my own quest.

 

the combat tab of the quest is pretty much the same as mine as well, except for the "specialsoultrap" and "soulcollection" conditions. i didn't know i should've add those there. goes to show how easier it is if you would've made a test mod with my requests to tell me what i should've done and where, which you did.

 

honestly, i know i jinxed this a bit, but i wanted to thank you for how far you've been willing to go to help me out and its inspiring to see veteran modders helping out newbies like me to this extent. i think i can pretty much take it from here. if there are any remaining issues, i'll be sure to check it out with you until we can brainstorm it into being functional.

 

i appreciate your help 😉

Link to comment
Share on other sites

The conditons on the dialogues are pretty self-explanatory, i.e. one for the DragonPriest:
1.  Subject's CombatTarget == Player must be 1.0 (true) - (the mob must be targeting player)
2. Subject's HasMagicEffectKeyword (aa_SpecialSoulTrapKeyword) == 0 (false)  -  (mob should have the special soultrap effect yet.  it is not truly necessary, but good for performance)
3.  GetStage of aa_SoulCollection quest must be 63  (this one is critical, since we want this happening only during this stage of the quest.)
4.  Subject's GetIsVoiceType == CrDragonPriectVoice is true (this one assures that this dialogue triggers only for Dragon Priests)

You want all those conditions to make sure we are only applying the trap spell to the mob once and only when we do need it.    We don't want repeat casts on every hit, and we don't want this triggering during other stages of the quest.

 

Link to comment
Share on other sites

37 minutes ago, scorrp10 said:

The conditons on the dialogues are pretty self-explanatory, i.e. one for the DragonPriest:
1.  Subject's CombatTarget == Player must be 1.0 (true) - (the mob must be targeting player)
2. Subject's HasMagicEffectKeyword (aa_SpecialSoulTrapKeyword) == 0 (false)  -  (mob should have the special soultrap effect yet.  it is not truly necessary, but good for performance)
3.  GetStage of aa_SoulCollection quest must be 63  (this one is critical, since we want this happening only during this stage of the quest.)
4.  Subject's GetIsVoiceType == CrDragonPriectVoice is true (this one assures that this dialogue triggers only for Dragon Priests)

You want all those conditions to make sure we are only applying the trap spell to the mob once and only when we do need it.    We don't want repeat casts on every hit, and we don't want this triggering during other stages of the quest.

 

i understand those conditions. as you said self explanatory.

 

the script attached to it is what i don't understand:

(GetOwningQuest() as SoulCollectionQuestScript).CastSoulTrap(akSpeaker)

.castsoultrap ? whouldn't that just cause the normal soultrap to be casted on the giant/drpriest/dr ? whereas we would want our special soul trap to be casted here ? i understand that this is a default script so it is necessary to use the default properties, but it just confused me. i'm guessing the condition for hasmagiceffect set to 0 means that our magic effect won't be cast on the giant, as you said, so when will the effect be cast on them ?

Edited by ThalmorJusticiar7th
Link to comment
Share on other sites

That condition checks that the monster does NOT yet have ah effect with this keyword.   Once the effect is applied, this condition will become false, and this topic will no longer trigger for this mob - the normal generic combat sound will be used instead.

Now, the script.  This dialogue is defined under SoulCollection quest, so that is what is fetched by GetOwningQuest().   (The base Quest object).  By casting it (as SoulCollectionQuestScript), I am specifically referring to the script of that type attached to the quest.   And I am their calling the CastSoulTrap function that is defined in that script.  If you look at it, you will see what that function does (casts the SpecialSoulTrap spell)

 

Link to comment
Share on other sites

  • 2 weeks later...
On 2/8/2025 at 9:04 PM, scorrp10 said:

Strange, edit did not save, I just redone it.    In any case, I just attached a working section of your quest - it is actually made for SSE, but you should be able to get the gist.

An 'empty vessel' gem is in a holder on Farengar's desk in ragonsReach.   Picking it up starts the quest, giving you "Kill a Giant" objective   (EmptyVesselPickupScript)

The quest has 'Hit' event combat dialogue defined for giant, dragon, and dragon priest, with conditions that quest needs to be on proper stage, and mob's target must be player, and  mod should not have an effect with 'SpecialSoulTrap' keyword.

When the dialogue fires, the attached script fragments (TIF_*) will cast the special soul trap spell on the target, applying the special SoulTrap effect.  (SpecialSoulTrapEffectScript)

When the target dies and effect finishes, it will call  'CompleteSoulTrap' function in the quest's  SoulCollectionQuestScript.    This function in turn will use the ApplySoulCollection function to replace the item in player's inventory and advance the quest stage.    Quest's stage fragments handle the displaying of objectives.

About your earlier question- "i.e."   stands for "in example", and is NOT part of the code.

If you say, look at Combat section of CreatureDialogueGiant  quest, you will see that its 'Hit' topic uses following condition:
image.thumb.png.3c361fd66087de5a8e05f8bd0d21e0d1.png

To make sure that this dialogue is spoken only by a creature with 'CrGiantVoice' voicetype (that is, a Giant)

SoulCollection.7z 5.47 kB · 2 downloads

so i've been studying your quest, and there are stuff that i don't understand,

 

first off why did you change the script completely from the one you gave me here ? now there are 3 functions in your attached mod, whereas in the script that you wrote for me here, there were two functions, one for applying the soultrap to the target and another for checking if a soul was collected:

 

so this is the old script you gave me:

; assuming corresponding objectives are numbered 31, 32, 33

Bool Function SpecialTrapSoul(Actor Victim, Actor Caster)
  If Caster == PlayerRef
    If GetStage() == 60 && Victim.GetRace() == GiantRace
      Return AdvanceCollectionStage(EmptySoulGem, PartialSoulGem, 63, 31)
    ElseIf GetStage() == 63 && Victim.GetRace() == DragonPriestRace
      Return AdvanceCollectionStage(PartialSoulGem, ModerateSoulGem, 66, 32)
    ElseIf GetStage() == 60 && Victim.GetRace() == DragonRace
      Return AdvanceCollectionStage(ModerateSoulGem, FilledSoulGem, 70, 33) ; <<<  THE EDITED LINE, updated gem names
    EndIf
  EndIf
  Return false
EndFunction

Bool Function AdvanceCollectionStage(MiscObject OldSoulGem, MiscObject NewSoulGem, Int NewStage, Int Objective)
  If PlayerRef.GetItemCount(OldSoulGem) > 0
    PlayerRef.RemoveItem(OldSoulGem, 1, true)
    PlayerRef.AddItem(NewSoulGem, 1)
    SetObjectiveCompleted(Objective, true)
    SetStage(NewStage)
    Return true
  EndIf
  Return false
EndFunction

 

but in your attached file, in the script files, there's a whole new script, and another function:

Scriptname SoulCollectionQuestScript extends Quest  Conditional

ReferenceAlias Property PlayerRefAlias  Auto  
Actor Property PlayerRef  Auto  
SPELL Property aa_SpecialSoulTrap  Auto  
Race Property GiantRace  Auto  
Race Property DragonPriestRace  Auto  
Race Property DragonRace  Auto  
MiscObject Property aa_EmptySoulGem  Auto  
MiscObject Property aa_PartialSoulGem  Auto  
MiscObject Property aa_ModerateSoulGem  Auto  
MiscObject Property aa_FilledSoulGem  Auto  

Function CastSoulTrap(Actor akTarget)
    if (akTarget.GetRace() == GiantRace && GetStage() == 60) || \
       (akTarget.GetRace() == DragonPriestRace && GetStage() == 63) || \
       (akTarget.GetRace() == DragonRace && GetStage() == 66)
        aa_SpecialSoulTrap.Cast(PlayerRef, akTarget)
    endif
EndFunction

Bool Function CompleteSoulTrap(Actor akCaster, Actor akTarget)
    if akCaster == PlayerRef
        if akTarget.GetRace() == GiantRace && GetStage() == 60
            return ApplySoulCollection(aa_EmptySoulGem, aa_PartialSoulGem, 63)
        elseif akTarget.GetRace() == DragonPriestRace && GetStage() == 63
            return ApplySoulCollection(aa_PartialSoulGem, aa_ModerateSoulGem, 66)
        elseif akTarget.GetRace() == DragonRace && GetStage() == 66
            return ApplySoulCollection(aa_ModerateSoulGem, aa_FilledSoulGem, 66)
        endif
    endif
    return false
EndFunction

Bool Function ApplySoulCollection(MiscObject amOldGem, MiscObject amNewGem, int aiStage)
    if PlayerRef.GetItemCount(amOldGem) > 0
        PlayerRef.RemoveItem(amOldGem, 1, true)
        PlayerRef.AddItem(amNewGem, 1)
        SetStage(aiStage)
        return true
    endif
    return false
EndFunction

 

could you elaborate why is there a third function ? because now i'm confused, also there's a reference alias property for the player in your new script, but the player Alias is not used in your new script. are you sure it should be there ?

Edited by ThalmorJusticiar7th
Link to comment
Share on other sites

One thing you have to understand is that mod development and scripting is an organic thing.  You code something one way, then realize something could be done better/easier/more efficiently - so you rewrite it.    In some cases, your script code might have no longer needed remnants from prior revisions.   Such as that PlayerRefAlias property.   I was using it in an earlier revision of the script, later switched to just using PlayerRef, but was to lazy to clean up the alias property.

The initial script was basically an example, just a piece of code not actually attached to anything, written to show how it could be done.

The new script is more of a finished product, taken from a working section of the mod.   So, to explain a few things:

The new function.   As you recall, I use 'Hit' Combat dialogue to determine when a player is in combat with a desired target.   When such dialogue triggers, I want to cast this special SoulTrap spell on the target.   For that, I need to attach a script fragment to those TopicInfos.   I could have implemented it by defining the SpecialSoulTrap spell and PlayerRef properties in EACH  TIF_   script, filling them, and making the Cast call directly from there.   

That is, EACH TIF_ script would have to be like:

Actor Property PlayerRef Auto            ; needs to be filled
SPELL Property aa_SpecialSoulTrap Auto   ; needs to be filled

Function Fragment_1(ObjectReference akSpeakerRef)
  Actor akSpeaker = akSpeakerRef as Actor
  aa_SpecialSoulTrap.Cast(PlayerRef, akSpeaker)
EndFunction

But that is just too much work.  You got only 3 kills, but what if you wanted five?  Or ten?   Whenever you see yourself doing the same piece of work more than once, you should ask yourself:  can I implement this work as a function, and call this function multiple times?   Which is exactly what I have done here.   Rather than define and fill same properties in three separate places, I define these properties ONCE in the quest script, create 'CastSoulTrap' function,  and then just call this function from the dialogue fragments, like this:   (no need to define or fill any properties there)

Function Fragment_1(ObjectReference akSpeakerRef)
  Actor akSpeaker = akSpeakerRef as Actor
  (GetOwningQuest() as SoulCollectionQuestScript).CastSoulTrap(akSpeaker)
EndFunction
Link to comment
Share on other sites

1 hour ago, scorrp10 said:

One thing you have to understand is that mod development and scripting is an organic thing.  You code something one way, then realize something could be done better/easier/more efficiently - so you rewrite it.    In some cases, your script code might have no longer needed remnants from prior revisions.   Such as that PlayerRefAlias property.   I was using it in an earlier revision of the script, later switched to just using PlayerRef, but was to lazy to clean up the alias property.

The initial script was basically an example, just a piece of code not actually attached to anything, written to show how it could be done.

The new script is more of a finished product, taken from a working section of the mod.   So, to explain a few things:

The new function.   As you recall, I use 'Hit' Combat dialogue to determine when a player is in combat with a desired target.   When such dialogue triggers, I want to cast this special SoulTrap spell on the target.   For that, I need to attach a script fragment to those TopicInfos.   I could have implemented it by defining the SpecialSoulTrap spell and PlayerRef properties in EACH  TIF_   script, filling them, and making the Cast call directly from there.   

That is, EACH TIF_ script would have to be like:

Actor Property PlayerRef Auto            ; needs to be filled
SPELL Property aa_SpecialSoulTrap Auto   ; needs to be filled

Function Fragment_1(ObjectReference akSpeakerRef)
  Actor akSpeaker = akSpeakerRef as Actor
  aa_SpecialSoulTrap.Cast(PlayerRef, akSpeaker)
EndFunction

But that is just too much work.  You got only 3 kills, but what if you wanted five?  Or ten?   Whenever you see yourself doing the same piece of work more than once, you should ask yourself:  can I implement this work as a function, and call this function multiple times?   Which is exactly what I have done here.   Rather than define and fill same properties in three separate places, I define these properties ONCE in the quest script, create 'CastSoulTrap' function,  and then just call this function from the dialogue fragments, like this:   (no need to define or fill any properties there)

Function Fragment_1(ObjectReference akSpeakerRef)
  Actor akSpeaker = akSpeakerRef as Actor
  (GetOwningQuest() as SoulCollectionQuestScript).CastSoulTrap(akSpeaker)
EndFunction

ok cool. so playerrefalias wasn't important as i guessed.

 

i understand what you mean, i just wasn't sure why you change the script, hence why i asked. as for:

(GetOwningQuest() as SoulCollectionQuestScript).CastSoulTrap(akSpeaker)

i saw that in your mock quest's dialogue and mimicked it in my own quest, but wasn't familiar why i'm doing it like this. glad that you've cleared this up. so if i am to lets say, after the quest, add the soulgem as a separate item that hosts the soul of a giant, then i can simply call on this function for the new item right ?

 

interesting stuff. thanks for clearing it up. and thank you for coming back tome pretty fast. i appreciate it.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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