rms827 Posted June 13, 2015 Share Posted June 13, 2015 I managed to duplicate the improper attacks by invincible sentries bug that a few players have complained about relating to my Dawnguard Sentries mod. The mod is supposed to be set up so that player character vampires and their followers are ignored. Occasionally somebody reports that they ge attacked anyway, and that the Sentries cannot be killed despite switching their Essential / Respawn Only toggle (in the MCM menu). For me, this bug hit after completing the Bloodstone Chalice mission and starting the Prophecy quest. It's consistent too. I played through from a few different saves and literally spent all last night (about 6 hours) stripping mods away from my load order trying to find the cause. LONG story short and saving the detailed description of all the mod combinations I disabled and re-enabled, it *seems* like the problem is either in Skyrim / Dawnguard itself OR there is some odd bug in Dawnguard Sentries. At this point, I cut back the Sentries to a really simple AI system. They're basically town guards with Dawnguard gear that can level higher than normal city guards. No Dawnguard related factions at all. No added faction relations at all since the town guards pretty much ignore anything but crimes and dragon or vampire attacks already. Beyond that, they just walk in circles on a path of idle markers. The only scripts I use are the Disable during Civil War sieges (so they don't interfere) and the MCM Menu one. Point being I don't know what I could do to simplify and conflict proof my mod any further. Oddly enough, I figured out how to fix the bug (temporarily?), regardless of the mods I was using; do a clean save with the mod. Exit the game, disable the mod, start it back up, save and exit again, and then re-enable the mod and the Sentries go back to ignoring the vampire player & their followers. Since the problem occurred and the solution worked rather I was using 63 mods or literally just the Unofficial Patches, i'm not sure the mods had anything to do with it. Here's the only "non-vanilla" script I have for the mod: Scriptname DawnSentryMCMStates extends SKI_ConfigBase {Creates Dawnguard Sentries MCM Menu} bool ImmortalCity = Falsebool RedShirtBeGone = Falsebool ImmortalOutside = Falsebool ExteriorBeGone = Falsebool PatrolImmortal = Falsebool PatrolBeGone = Falsebool RoriksteadImmortal = Falsebool RoriksteadBeGone = Falsebool CastleVampsBeGone = False Function theInit() ModName = "Dawnguard Sentries" Pages = new String[1] Pages[0] = "Configuration"EndFunction Event OnConfigInit() theInit()EndEvent Event OnConfigOpen() theInit()EndEvent Event OnVersionUpdate(int a_version) theInit()EndEvent Event OnPageReset(string page) If (page == "") LoadCustomContent("Dawnguard Sentries/DawnguardShield.dds") Return Endif UnloadCustomContent() If (page=="Configuration") SetCursorFillMode(TOP_TO_BOTTOM) AddToggleOptionST ("MakeEssential", "Make Sentries Essential", ImmortalCity) AddEmptyOption() AddToggleOptionST ("DisableRedShirts", "Disable Dawnguard Recruits", RedShirtBeGone) AddEmptyOption() AddToggleOptionST ("ExteriorEssential", "Make Exterior Sentries Essential", ImmortalOutside) AddToggleOptionST ("ExteriorDisable", "Disable Exterior Sentries", ExteriorBeGone) AddEmptyOption() AddToggleOptionST ("PatrolEssential", "Make Sentry Road Patrol Essential", PatrolImmortal) AddToggleOptionST ("PatrolDisable", "Disable Sentry Road Patrol", PatrolBeGone) AddEmptyOption() AddToggleOptionST("RoriksteadEssential", "Set Rorikstead Sentries as Essential", RoriksteadImmortal) AddToggleOptionST("RoriksteadDisabled", "Disable Sentries in Rorikstead", RoriksteadBeGone) AddEmptyOption() AddToggleOptionST("VampireSentries", "Disable Vampires Outside Volkihar", CastleVampsBeGone) EndIfEndEvent State MakeEssential Event OnHighlightST() SetInfoText("Sets Non-Recruit Sentries in Towns & Cities as Essential") EndEvent Event OnSelectST() ImmortalCity = !ImmortalCity SetToggleOptionValueST(ImmortalCity) If ImmortalCity == True DawnSentryEssentialToggleQuest.start() ElseIf ImmortalCity == False DawnSentryEssentialToggleQuest.stop() EndIf EndEvent Event OnDefaultST() ImmortalCity = False SetToggleOptionValueST(False) DawnSentryEssentialToggleQuest.stop() EndEventEndState State DisableRedShirts Event OnHighlightST() SetInfoText("Disables the Dawnguard Recruits in Dayspring Canyon") EndEvent Event OnSelectST() RedShirtBeGone = !RedShirtBeGone SetToggleOptionValueST(RedShirtBeGone) If RedShirtBeGone == True DawnSentryRedShirtDisableXMarker.disable() ElseIf RedShirtBeGone == False DawnSentryRedShirtDisableXMarker.enable() EndIf EndEvent Event OnDefaultST() RedShirtBeGone = False SetToggleOptionValueST(False) DawnSentryRedShirtDisableXMarker.enable() EndEventEndState State ExteriorEssential Event OnHighlightST() SetInfoText("Sets Sentries Patrolling Outside Riften & Solitude as Essential") EndEvent Event OnSelectST() ImmortalOutside = !ImmortalOutside SetToggleOptionValueST(ImmortalOutside) If ImmortalOutside == True DawnSentryEssentialExteriorSentries.Start() ElseIf ImmortalOutside == False DawnSentryEssentialExteriorSentries.Stop() EndIf EndEvent Event OnDefaultST() ImmortalOutside = False SetToggleOptionValueST(False) DawnSentryEssentialExteriorSentries.Stop() EndEventEndState State ExteriorDisable Event OnHighlightST() SetInfoText("Disable the Sentries Patrolling Outside Riften & Solitude") EndEvent Event OnSelectST() ExteriorBeGone = !ExteriorBeGone SetToggleOptionValueST(ExteriorBeGone) If ExteriorBeGone == True DawnSentrySolitudeDocksDisableToggleMarker.disable() DawnSentryRiftenExteriorDisableXMarker.disable() ElseIf ExteriorBeGone == False DawnSentrySolitudeDocksDisableToggleMarker.enable() DawnSentryRiftenExteriorDisableXMarker.enable() EndIf EndEvent Event OnDefaultST() ExteriorBeGone = False SetToggleOptionValueST(False) DawnSentrySolitudeDocksDisableToggleMarker.enable() DawnSentryRiftenExteriorDisableXMarker.enable() EndEventEndState State PatrolEssential Event OnHighlightST() SetInfoText("Sets Sentries Patrolling Between Riften and Dayspring Canyon as Essential") EndEvent Event OnSelectST() PatrolImmortal = !PatrolImmortal SetToggleOptionValueST(PatrolImmortal) If PatrolImmortal == True DawnSentryEssentialPatrolQuest.Start() ElseIf PatrolImmortal == False DawnSentryEssentialPatrolQuest.Stop() EndIf EndEvent Event OnDefaultST() PatrolImmortal = False SetToggleOptionValueST(False) DawnSentryEssentialPatrolQuest.Stop() EndEventEndState State PatrolDisable Event OnHighlightST() SetInfoText("Disables Sentries Patrolling Between Riften and Dayspring Canyon") EndEvent Event OnSelectST() PatrolBeGone = !PatrolBeGone SetToggleOptionValueST(PatrolBeGone) If PatrolBeGone == True DawnSentryPatrolUnitDisableXMarker.disable() ElseIf PatrolBeGone == False DawnSentryPatrolUnitDisableXMarker.enable() EndIf EndEvent Event OnDefaultST() PatrolBeGone = False SetToggleOptionValueST(False) DawnSentryPatrolUnitDisableXMarker.enable() EndEventEndState State RoriksteadEssential Event OnHighlightST() SetInfoText("Sets Sentries Patrolling Rorikstead as Essential") EndEvent Event OnSelectST() RoriksteadImmortal = !RoriksteadImmortal SetToggleOptionValueST(RoriksteadImmortal) If RoriksteadImmortal == True DawnSentryRoriksteadToggleQuest.Start() ElseIf RoriksteadImmortal == False DawnSentryRoriksteadToggleQuest.Stop() EndIf EndEvent Event OnDefaultST() RoriksteadImmortal = False SetToggleOptionValueST(False) DawnSentryRoriksteadToggleQuest.Stop() EndEventEndState State RoriksteadDisabled Event OnHighlightST() SetInfoText("Disables Sentries Patrolling Rorikstead Village") EndEvent Event OnSelectST() RoriksteadBeGone = !RoriksteadBeGone SetToggleOptionValueST(RoriksteadBeGone) If RoriksteadBeGone == True DawnSentryRoriksteadDisableXMarker.disable() ElseIf RoriksteadBeGone == False DawnSentryRoriksteadDisableXMarker.enable() EndIf EndEvent Event OnDefaultST() RoriksteadBeGone = False SetToggleOptionValueST(False) DawnSentryRoriksteadDisableXMarker.enable() EndEventEndState State VampireSentries Event OnHighlightST() SetInfoText("Disables Vampire Guards Patrolling Outside Castle Volkihar") EndEvent Event OnSelectST() CastleVampsBeGone = !CastleVampsBeGone SetToggleOptionValueST(CastleVampsBeGone) If CastleVampsBeGone == True DawnSentryVampCastleGuardDisableXMarker.disable() ElseIf CastleVampsBeGone == False DawnSentryVampCastleGuardDisableXMarker.enable() EndIf EndEvent Event OnDefaultST() CastleVampsBeGone = False SetToggleOptionValueST(False) DawnSentryVampCastleGuardDisableXMarker.enable() EndEventEndState Quest Property DawnSentryEssentialToggleQuest AutoQuest Property DawnSentryEssentialExteriorSentries AutoQuest Property DawnSentryEssentialPatrolQuest AutoQuest Property DawnSentryRoriksteadToggleQuest AutoObjectReference Property DawnSentryPatrolUnitDisableXMarker AutoObjectReference Property DawnSentryRiftenExteriorDisableXMarker AutoObjectReference Property DawnSentryRedShirtDisableXMarker AutoObjectReference Property DawnSentrySolitudeDocksDisableToggleMarker AutoObjectReference Property DawnSentryRoriksteadDisableXMarker AutoObjectReference Property DawnSentryVampCastleGuardDisableXMarker Auto Anybody see anything I goofed? The MCM script seems to work fine and this is the only line I saw in the logs that pertained to my script: [06/13/2015 - 01:30:40AM] Error: Unable to call GetFormID - no native object bound to the script object, or object is of incorrect typestack:[None].DawnSentryMCMStates.GetFormID() - "<native>" Line ?[sKI_ConfigManagerInstance (0E000802)].SKI_ConfigManager.Cleanup() - "SKI_ConfigManager.psc" Line ?[sKI_ConfigManagerInstance (0E000802)].SKI_ConfigManager.OnGameReload() - "SKI_ConfigManager.psc" Line 82[alias PlayerRef on quest SKI_ConfigManagerInstance (0E000802)].SKI_PlayerLoadGameAlias.OnPlayerLoadGame() - "SKI_PlayerLoadGameAlias.psc" Line 6 With no specific line referenced, I'm not sure what native object to look for that it's not seeing connected to a script. ANYWAY... If anybody has any bright ideas what may be causing this, feel free to chime in. Link to comment Share on other sites More sharing options...
sLoPpYdOtBiGhOlE Posted June 15, 2015 Share Posted June 15, 2015 I'm no help with your problem.But some advice I'd offer on your calling Start() on quests from an open MCM menu or any menu, I would advise against it. A quest wont start while in a menu.Start() quest Is latent and will not return until the quest is actually started (and any start-up stage fragments run).So you end up hanging the MCM event until the menu is exited.So for example in your MCM script you have: Event OnSelectST() ImmortalCity = !ImmortalCity SetToggleOptionValueST(ImmortalCity) If ImmortalCity == True DawnSentryEssentialToggleQuest.start() ;<--- At this point the quest won't start until the menu is closed. So now you have hung this event, as the event won't continue on until the quest start returns, which it never will return while the menu is open. ElseIf ImmortalCity == False DawnSentryEssentialToggleQuest.stop() EndIf EndEventThere are ways to work around it, but it does add a little more code. I know this doesn't resolve your initial problem, but eliminating things that can break your procedures does help over all. Link to comment Share on other sites More sharing options...
rms827 Posted June 15, 2015 Author Share Posted June 15, 2015 I'm no help with your problem.But some advice I'd offer on your calling Start() on quests from an open MCM menu or any menu, I would advise against it. A quest wont start while in a menu.Start() quest Is latent and will not return until the quest is actually started (and any start-up stage fragments run).So you end up hanging the MCM event until the menu is exited.So for example in your MCM script you have: Event OnSelectST() ImmortalCity = !ImmortalCity SetToggleOptionValueST(ImmortalCity) If ImmortalCity == True DawnSentryEssentialToggleQuest.start() ;<--- At this point the quest won't start until the menu is closed. So now you have hung this event, as the event won't continue on until the quest start returns, which it never will return while the menu is open. ElseIf ImmortalCity == False DawnSentryEssentialToggleQuest.stop() EndIf EndEventThere are ways to work around it, but it does add a little more code. I know this doesn't resolve your initial problem, but eliminating things that can break your procedures does help over all. The quests you see in the MCM Menu are (more or less) empty quests, They swap out the Sentries with quest alias versions that are set as essential instead of respawn only. My little bit of studying indicated that would be the cleanest way to do that, particularly in terms of allowing for a clean uninstall of the mod if somebody didn't like it. Those quest alias options were intended to be "set it and forget it" also, as opposed to something a user might switch on the fly regularly. Like users always do what we expect them to do, lol. Explanations aside, I'm open to ideas here. The quests getting stuck in some sort of permanent hang may be at least contributing to the issue. So, is there a better way to cleanly toggle over 80 NPCs between respawn and essential? Link to comment Share on other sites More sharing options...
sLoPpYdOtBiGhOlE Posted June 15, 2015 Share Posted June 15, 2015 You can still stop and start the quest from the MCM, but you want to only call start() after you exit the menu. Link to comment Share on other sites More sharing options...
rms827 Posted June 15, 2015 Author Share Posted June 15, 2015 You can still stop and start the quest from the MCM, but you want to only call start() after you exit the menu. Gotcha. That makes sense. Since I've been up all night grinding out the Open Cities compatibllity changes for the mod, my brain is in shutdown mode now. How would I change the script wording to delay the quest start till exit? Link to comment Share on other sites More sharing options...
sLoPpYdOtBiGhOlE Posted June 15, 2015 Share Posted June 15, 2015 Well there is many ways you can do it. This is one way I tried and it appears to work ok, but there are other ways that may better suite your needs depending on what you've got setup. I add this to each quest script that MCM will be stopping and starting from a toggle.If the quest doesn't have a script then I add a script to the quest with just this in the script.(this one for example is the DawnSentryEssentialToggleQuest)ScriptName DawnSentryEssentialToggleQuestScript Extends Quest Bool bStart ;The function isn't latent and returns straight away, so your MCM script will keep doing it's thing. Function HandleQuest(Bool bState) bStart = bState RegisterForSingleUpdate(0.5) EndFunction Event OnUpdate() If bStart ;No use trying to start the quest while in Menu Mode. If Utility.IsInMenuMode() RegisterForSingleUpdate(1.0) Else ;If the quest is not running then start it. If !Self.IsRunning() Self.Start() EndIf EndIf Else ;Stopping a quest is not latent (or at least i don't think so). ;But we only need to stop it if it's running. If Self.IsRunning() Self.Stop() EndIf EndIf EndEventThen in your MCM script call the HandleQuest() function in the quest script.So in your case you have:Event OnSelectST() ImmortalCity = !ImmortalCity SetToggleOptionValueST(ImmortalCity) If ImmortalCity == True DawnSentryEssentialToggleQuest.start() ElseIf ImmortalCity == False DawnSentryEssentialToggleQuest.stop() EndIf EndEventI change it to:Event OnSelectST() ImmortalCity = !ImmortalCity SetToggleOptionValueST(ImmortalCity) (DawnSentryEssentialToggleQuest As DawnSentryEssentialToggleQuestScrit).HandleQuest(ImmortalCity) EndEventThis way the user can toggle the option all they like and it doesn't hang the MCM event.You'd do the same for the other quest Stop/Start entries you got. Link to comment Share on other sites More sharing options...
rms827 Posted June 15, 2015 Author Share Posted June 15, 2015 Nice. It's easier than I thought. Much appreciated on the help. :) Link to comment Share on other sites More sharing options...
sLoPpYdOtBiGhOlE Posted June 16, 2015 Share Posted June 16, 2015 It helped me as well.I improved my own mod by using an altered version of the above.Which has made it so my toggle quest start/stop is almost bulletproof regardless of what a user does. Prior I was using OnConfigClose() in my MCM script to toggle my quest.So when OnConfigOpen() was fired I would store the Quest state eg: Bool QuestState = False ; My toggle control uses this Bool QuestStateFlag ;I use this to see if the toggle has changed when exiting. Event OnConfigOpen() QuestStateFlag = QuestState EndEvent State myQuestToggle Event OnSelectST() QuestState = !QuestState SetToggleOptionValueST(QuestState) EndEvent EndState Event OnConfigClose() If QuestStateFlag != QuestState If QuestState If !myQuest.IsRunning() myQuest.Start() EndIf Else If myQuest.IsRunning() myQuest.Stop() EndIf EndIf EndIf EndEventThe problem I could see happening with the above is if for some reason the quest hung on start then the MCM OnConfigClose() event would hang, which in turn could affect other MCM menus. OnConfigClose() fires when the user exits my menu, but they may not exit all menus (eg: still in the game menu).So my hung OnConfigClose() could cause MCM to go screwy until the user exits all menus back to their game. So I changed it so the quest start is fired outside of MCM :smile:eg: Event OnConfigClose() If QuestStateFlag != QuestState (myQuest As myQuestScript).HandleQuest(QuestState) EndIf EndEvent Link to comment Share on other sites More sharing options...
Recommended Posts