Jump to content

[LE] Monitor for Quest Update or Completion


jbvertexx

Recommended Posts

I have a need to monitor when various vanilla game quests or quest stages have been completed, which then will trigger actions in a mod I am developing. My preference would be to have it event driven - I'd prefer not to use the regeisterforsingleupdate() / onupdate() sequence to check. I would also prefer to not modify the actual vanilla quest for compatibility reasons.

 

I have tried OnTrackedStatsEvent, attempting to monitor the "Quests Completed" stat. However, I've found that most of those Stats don't actually work in game (at least they do not trigger the Event). I have been able to use this method for the "Misc Objectives Completed" stat, which actually does fire the Event. However, none of the other quest type stats seem to fire the event.

 

I'm also surpised there is no Story Manager event node on quest progress & completion. I would have thought that would have been a cleaner way than the Script event node that seems to be used for most quest dependencies. Using the Script event would require me to modify the vanilla quest to fire the script event, which I want to avoid.

 

Are there any other techniques to monitor for quest and/or quest objective completion?

Link to comment
Share on other sites

Monitoring of quests (not own created) through papyrus a script is more or minor time consuming. You have to create a own quest. Create a referenceAlias filled with unique player and attach a script to that. Next script is like to show you what could be possible here. It is a raw sample.

 

jbvPlayerAliasTestScript

 

Scriptname jbvPlayerAliasTestScript extends ReferenceAlias
; https://forums.nexusmods.com/index.php?/topic/7341496-monitor-for-quest-update-or-completion/
; jbvertexx wrote: "Are there any other techniques to monitor for quest and/or quest objective completion?"

  Quest[] a                ; store all the quest we want to observe by this script

  Float fTimer
  Bool  bFirstTime


; -- EVENTs -- 4

EVENT OnInit()                                    ; if we want to start a new game or first time using on savegame
    myF_Init("OnInit")
ENDEVENT

EVENT OnPlayerLoadGame()                        ; if we have loaded a savegame and the related quest is running
    myF_Init("OnPlayerLoadGame")
ENDEVENT

EVENT OnCellLoad()
    cell c = Game.GetPlayer().GetParentCell()
    Debug.Notification("New parent cell active..")
    Debug.Trace(" Player enters " +c)
ENDEVENT

EVENT OnLocationChange(Location akOldLoc, Location akNewLoc)
    gotoState("Busy")                ; ### STATE ###

    fTimer = Utility.GetCurrentRealTime()
    Utility.Wait(0.35)

    myF_AnalyzeQuests()
    bFirstTime = False

    gotoState("")                    ; ### STATE ###
ENDEVENT


;==============================
state Busy    ; busy while doing quest analyze, it is possible location change event can be triggered more than once within a second
;=========
EVENT OnLocationChange(Location akOldLoc, Location akNewLoc)
    float f = Utility.GetCurrentRealTime() - fTimer
    Debug.Trace(self+" I'm busy.. Ooops!  " +f)        ; runtime check also
ENDEVENT
;=======
endState


; -- FUNCTIONs -- 4

;---------------------------
FUNCTION myF_AnalyzeQuests()  ; sample function here
;---------------------------
    quest q = a[0]
    int i

IF q.IsRunning()
    ; quest stored to array first position is currently running

    IF q.IsObjectiveDisplayed(15)
        ; quest objective is displayed
    ENDIF

    IF q.IsObjectiveCompleted(15)
        ; quest objective 15 is completed
    ENDIF    

    IF q.IsStageDone(5)                    ; IF q.GetStageDone(5)
        ; quest stage 5 was set
    ENDIF

    IF (q.GetCurrentStageID() == 5)        ; IF (q.GetStage() == 5)
        ; quest stage is 5
    ENDIF

ENDIF

    i = myF_Stage("MQ101", q as MQ101QuestScript, q)        ; *** sample only ***
ENDFUNCTION


;----------------------------------------------------
Int FUNCTION myF_Stage(String s, String sPS, Quest q)
;----------------------------------------------------
    bool bQuest = q.IsRunning()
    int       i = q.GetCurrentStageID()

IF (bFirstTime) || ((bQuest) && (i > 0))
    IF ( bQuest )
            s += " =  Run  "
    ELSE
        IF (i == 0)
            s += " =  -?-  "
        ELSE
            s += " = Finish"
        ENDIF
    ENDIF
;    ------------------------
    s = "- " +s+ "  stage ="
;    ------------------------
    IF (i > 999)
        s += i
    ELSE
        s += " "
        IF (i < 100)
            s += " "
        ENDIF
        s += i
        IF (i < 10)
            s += " "
        ENDIF
    ENDIF
    
    Debug.Trace(s+ " -- " +sPS)        ; first time after loading savegame or quest is running and queststage > 0
ENDIF
;------------------
IF ( bQuest )
ELSE                     ; quest is not running,
    IF (i > 0)
        RETURN -1        ; current stage is greater than Zero, stop quest observer if useful
    ENDIF
        RETURN -2        ; do not check anything related to quest
ENDIF
;------------------
IF (sPS == "None")
    RETURN -1            ; quest is running, BUT has no valid script.. Thats really bad!!!
ENDIF
;------------------
    RETURN i            ; All is fine, return quest.GetStage()
ENDFUNCTION


;--------------------------
FUNCTION myF_Init(String s)
;--------------------------
    Debug.Trace("You are running the " + Debug.GetConfigName() + " build " +Debug.GetVersionNumber() + " on platform " +Debug.GetPlatformName())
    Debug.Trace("jbvTest: " +s+ "() v1.0 jbvertexx 2019")

    bFirstTime = TRUE
    myF_InitQuestArray()
ENDFUNCTION


;----------------------------
FUNCTION myF_InitQuestArray()
;----------------------------
    int[] b = new Int[57]        ; 0..56
    a       = new Quest[57]

; main quest
b[0] = 0x0003372B     ; MQ101 [QUST:0003372B] "Unbound"
b[1] = 0x0004E50D     ; MQ102 [QUST:0004E50D] "Before the Storm", MQ102a Hadvard, MQ102b Ralof
b[2] = 0x000D0800     ; MQ103 [QUST:000D0800] "Bleak Falls Barrow"
b[3] = 0x0002610C     ; MQ104 [QUST:0002610C] "Dragon Rising"
b[4] = 0x000242BA     ; MQ105 [QUST:000242BA] "The Way of the Voice"
b[5] = 0x00032926     ; MQ106 [QUST:00032926] "A Blade in the Dark"

b[6] = 0x00035D5F     ; MQ201 [QUST:00035D5F] "Diplomatic Immunity"
b[7] = 0x00036191     ; MQ202 [QUST:00036191] "A Cornered Rat"
b[8] = 0x00036192     ; MQ203 [QUST:00036192] "Alduin's Wall"
b[9] = 0x0002D515     ; MQ204 [QUST:0002D515] "The Throat of the World"
b[10]= 0x0002D516     ; MQ205 [QUST:0002D516] "Elder Knowledge"
b[11]= 0x00036193     ; MQ206 [QUST:00036193] "Alduin's Bane"

b[12]= 0x00044C56     ; MQ301 [QUST:00044C56] "The Fallen"
b[13]= 0x00045923     ; MQ302 [QUST:00045923] "Season Unending"
b[14]= 0x00046EF0     ; MQ303 [QUST:00046EF0] "The World-Eater's Eyrie"
b[15]= 0x00046EF1     ; MQ304 [QUST:00046EF1] "Sovngarde"
b[16]= 0x00046EF2     ; MQ305 [QUST:00046EF2] "Dragonslayer"
b[17]= 0x00046EF3     ; MQ306 [QUST:00046EF3]  no name

; mage guild
b[18]= 0x0001F251     ; 1 MG01 [QUST:0001F251] "First Lessons"
b[19]= 0x0001F252     ; 2 MG02 [QUST:0001F252] "Under Saarthal"
b[20]= 0x0001F253     ; 3 MG03 [QUST:0001F253] "Hitting the Books"
b[21]= 0x0001F254     ; 4 MG04 [QUST:0001F254] "Good Intentions"
b[22]= 0x0001F255     ; 5 MG05 [QUST:0001F255] "Containment"
b[23]= 0x0001F256     ; 6 MG06 [QUST:0001F256] "Revealing the Unseen"
b[24]= 0x0001F257     ; 7 MG07 [QUST:0001F257] "The Staff of Magnus"
b[25]= 0x0001F258     ; 8 MG08 [QUST:0001F258] "The Eye of Magnus"

; thief guild
b[26]= 0x00021556     ; 0 TG00 [QUST:00021556] "A Chance Arrangement"
b[27]= 0x0001F326     ; 1 TG01 [QUST:0001F326] "Taking Care of Business"
b[28]= 0x0002154E     ; 2 TG02 [QUST:0002154E] "Loud and Clear"
b[29]= 0x0002154F     ; 3 TG03 [QUST:0002154F] "Dampened Spirits"
b[30]= 0x00021550     ; 4 TG04 [QUST:00021550] "Scoundrel's Folly"

b[31]= 0x00021551     ; 5 TG05 [QUST:00021551] "Speaking With Silence"
b[32]= 0x00021552     ; 6 TG06 [QUST:00021552] "Hard Answers"
b[33]= 0x00021553     ; 7 TG07 [QUST:00021553] "The Pursuit"
b[34]= 0x00057F99     ; 8 TG08A[QUST:00057F99] "Trinity Restored"
b[35]= 0x00021554     ; 9 TG08B[QUST:00021554] "Blindsighted"

; dark brotherhood
b[36]= 0x0001EA5C     ; 0 DarkBrotherhood [QUST:0001EA5C] "Dark Brotherhood"
b[37]= 0x0001EA50     ; 1 DB01 [QUST:0001EA50] "Innocence Lost"
b[38]= 0x0001EA51     ; 2 DB02 [QUST:0001EA51] "With Friends Like These..."
b[39]= 0x00022F47     ; 3 DB02a[QUST:00022F47] "Sanctuary"
b[40]= 0x0001EA52     ; 4 DB03 [QUST:0001EA52] "Mourning Never Comes"
b[41]= 0x0001EA53     ; 5 DB04 [QUST:0001EA53] "Whispers in the Dark"
b[42]= 0x0002AD16     ; 6 DB04a[QUST:0002AD16] "The Silence Has Been Broken"

b[43]= 0x0001EA54     ; 7 DB05 [QUST:0001EA54] "Bound Until Death"
b[44]= 0x0001EA55     ; 8 DB06 [QUST:0001EA55] "Breaching Security"
b[45]= 0x0001EA56     ; 9 DB07 [QUST:0001EA56] "The Cure for Madness"
b[46]= 0x0001EA57     ;10 DB08 [QUST:0001EA57] "Recipe for Disaster"
b[47]= 0x0001EA58     ;11 DB09 [QUST:0001EA58] "To Kill an Empire"
b[48]= 0x0003CEDA     ;12 DB10 [QUST:0003CEDA] "Death Incarnate"
b[49]= 0x0001EA59     ;13 DB11 [QUST:0001EA59] "Hail Sithis!"

; companions
b[50]= 0x0004B2D9     ; 0 C00 [QUST:0004B2D9] "Take Up Arms"         QF_C00_0004B2D9    
b[51]= 0x0006E803     ; 1 C01 [QUST:0006E803] "Proving Honour"       QF_C01_0006E803    
b[52]= 0x0006E804     ; 2 C02 [QUST:0006E804] "Brotherhood"          QF_C02_0006E804    
b[53]= 0x0001CEF4     ; 3 C03 [QUST:0001CEF4] "The Silver Hand"      QF_C01_0001CEF4        C03QuestScript
b[54]= 0x0001CEF5     ; 4 C04 [QUST:0001CEF5] "Blood's Honour"       QF_C02_0001CEF5        C04QuestScript
b[55]= 0x0006E805     ; 5 C05 [QUST:0006E805] "Purity of Revenge"    QF_C05_0006E805        C05QuestScript
b[56]= 0x0001CEF6     ; 6 C06 [QUST:0001CEF6] "Glory of the Dead"    QF_C03_0001CEF6        C06QuestScript

int i = 0
    WHILE (i < a.Length)
        a[i] = Game.GetForm( b[i] ) as Quest    ; fill our quest array
        i = i + 1
    ENDWHILE
ENDFUNCTION

 

 

Edited by ReDragon2013
Link to comment
Share on other sites

Wow - really nice scripting there! I see you are triggering off the OnLocationChange() event. I won't be able to use this because I will need to take action immediately after the quest completes, and it will impact dialogue with the NPC involved in completing the quest.

 

The closest I have come is to use an Alias on the Actor(s) involved in completing the quest (the quests I am monitoring are almost always completed through dialogue). I then use the OnActivate() event on that Alias to trigger when the player activates the Actor, so it will immediately check to see if the quest is completing. However, it will only check the next time you talk to that actor after completing the quest, so that doesn't quite work.

 

Really appreciate your sample script though, I'll be able to use the quest checking logic! If you can think of another approach for the trigger, let me know. Thanks!

Link to comment
Share on other sites

I actually figured out how it can be done by using an active magic effect on the Actor Alias. I can use a constant effect ability with conditions on the effect set to the stage (using getstage or getstagedone) or even to the quest itself. Using oneffectstart() in the magic effect script, I can send the trigger that is needed and then dispel() the effect. I have tested this and it looks like it's working - just need to work through the details of implementation.

Link to comment
Share on other sites

I suggest adding to the quest stage you are "monitoring", your code.

Otherwise your script would have to run constantly and that is not that great, it slows the game.

 

Modifying the script wont hurt anything. A new copy is generated by the CK and the old copy does not get modified.

 

some alternatives to it running all the time would be an ontravel, onwait, onlevel, onsleep, etc

 

if youre going to apply a constant effect a magic effect is the way to go. it requires a quest, a spell effect, a magic effect, and then sometimes a perk effect

If you examine the OnSleep or OnPlayerSleep quest you will see the structure needed. OnSleep of course adds XP if you are rested or well rested it is a constant effect for 8 hours.

 

most of the quests have achievements addition , that could be a way of tracking, but a better way could be the misc stats counters

 

I believe many of the mod authors for "Experience" and experience point based mods know much about this.

Edited by masternoxx
Link to comment
Share on other sites

I have a need to monitor when various vanilla game quests or quest stages have been completed, which then will trigger actions in a mod I am developing. My preference would be to have it event driven - I'd prefer not to use the regeisterforsingleupdate() / onupdate() sequence to check. I would also prefer to not modify the actual vanilla quest for compatibility reasons.

 

I have tried OnTrackedStatsEvent, attempting to monitor the "Quests Completed" stat. However, I've found that most of those Stats don't actually work in game (at least they do not trigger the Event). I have been able to use this method for the "Misc Objectives Completed" stat, which actually does fire the Event. However, none of the other quest type stats seem to fire the event.

 

I'm also surpised there is no Story Manager event node on quest progress & completion. I would have thought that would have been a cleaner way than the Script event node that seems to be used for most quest dependencies. Using the Script event would require me to modify the vanilla quest to fire the script event, which I want to avoid.

 

Are there any other techniques to monitor for quest and/or quest objective completion?

 

I don't know of a good general-case solution; guaranteeing an immediate response to any quest changing to any stage at any time as far as I know requires polling, either through registerforsingleupdate or a magic effect (which is still doing polling and can have bad interactions with brawls). However if you are listening for one specific quest to change to one specific stage, you might be able to hang your scripts off something else that it changes, or limit your polling to a brief interval.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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