ThalmorJusticiar7th Posted February 8 Share Posted February 8 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 More sharing options...
scorrp10 Posted February 8 Share Posted February 8 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 More sharing options...
ThalmorJusticiar7th Posted February 8 Share Posted February 8 (edited) 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 February 8 by ThalmorJusticiar7th Link to comment Share on other sites More sharing options...
scorrp10 Posted February 8 Share Posted February 8 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 More sharing options...
ThalmorJusticiar7th Posted 4 hours ago Share Posted 4 hours ago (edited) 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: 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 4 hours ago by ThalmorJusticiar7th Link to comment Share on other sites More sharing options...
scorrp10 Posted 2 hours ago Share Posted 2 hours ago 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 More sharing options...
ThalmorJusticiar7th Posted 1 hour ago Share Posted 1 hour ago 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 More sharing options...
Recommended Posts