jbvertexx Posted January 24, 2019 Share Posted January 24, 2019 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 More sharing options...
ReDragon2013 Posted January 24, 2019 Share Posted January 24, 2019 (edited) 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 January 24, 2019 by ReDragon2013 Link to comment Share on other sites More sharing options...
jbvertexx Posted January 25, 2019 Author Share Posted January 25, 2019 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 More sharing options...
jbvertexx Posted January 25, 2019 Author Share Posted January 25, 2019 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 More sharing options...
masternoxx Posted January 25, 2019 Share Posted January 25, 2019 (edited) 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 effectIf 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 January 25, 2019 by masternoxx Link to comment Share on other sites More sharing options...
foamyesque Posted January 25, 2019 Share Posted January 25, 2019 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 More sharing options...
Recommended Posts