thebigMuh Posted June 7, 2016 Share Posted June 7, 2016 (edited) Greetings! This bug has been bothering me for ages, so I finally got annoyed enough to investigate and fix it: I'll refer to the ability the crown is holding as "crown ability" and the ability the player should have even when the crown is not equipped as "player ability". * You can easily get the Aetherial Crown into a state where it will only correctly apply the crown ability every second time you equip it. * You can equally easy get three (or more) doomstone abilities to be active at the same time - one from the crown, two actually on the player. * Also, if you store the same ability in the crown as you have as a player ability, then equipping and unequipping the crown will erase the player ability, leaving you without a doomstone ability while the crown is unequipped. The annoying (and bothersome to fix) were the first two bugs, the last one was a drive-by fix. How to reproduce: * To watch what the crown is doing enter 'prid xx00cff1' in the console to pick the crown reference (0200cff1 usually). Use 'sv' along the way to print the variables on it. * currentCrownAbility is what the crown thinks it has stored * currentDoomstoneAbility is the ability the crown thinks is on the player * For easy testing, travel to the guardian stones, since you can quickly switch between three different abilities there. * Thief is ability number 11 * Mage is ability number 6 * Warrior is ability number 13 * To get into the bugged behavior, you have to get the currentCrownAbility to be 0 even tough there actually is an ability stored in the crown. * For starters, unequip the crown, pick Mage, equip the crown, then pick Warrior. This should set the crown ability to 6, and the player ability to 13, no matter which state your game was in before. If not, then continue flipping through the stones until you get to this state. You should have Warrior as player ability and Mage as crown ability at this point, ready to screw everything up. * Unequip, then reequip the crown, checking 'sv' after each equip. currentDoomstoneAbility will flip between 13 and 0 every time you equip the crown. * When it's 0, you can do various things: * Pick Thief and you will have all three guardian stones active at the same time. currentCrownAbility will be 0 (should be 13), and the Mage will be left over (should have been removed). * Pick either Warrior or Mage, and nothing will change from an ability standpoint, but the currentCrownAbility will be 0 (should still be 6), and only every second time you equip the crown will actually give you the Mage ability. * Play around some more if you want. If you play your cards right, you should be able to get all doomstone abilities at the same time by playing this game with all doomstones in reverse order from 13 up to 1, adding one leftover ability after another (Warrior->Tower->Thief->Steed->Shadow->Serpent->Ritual->Mage->Lover->Lord->Lady->Atronach->Apprentice). In any case, the logic of the crown script (DLC1LD_AetherialCrownScript.psc) is pretty screwed up. Here's my proposed fix. It makes it quite a bit larger, partly to correctly track both the player ability and the crown ability, and partly so this script can "heal" a game that already has the crown in a broken state. I heavily commented it, which together with a diff of the old script should hopefully explain everything. My comments have a space between the ';' and the text. Scriptname DLC1LD_AetherialCrownScript extends ReferenceAlias {Script for the DLC1LD Aetherial Crown reward item} ;DLC1LD Postquest quest, since we need to keep the crown on an alias. Quest property DLC1LD_Postquest Auto ;Perk that traps the Doomstone activation. Perk property DLC1LD_AetherialCrownPerk Auto ;Standard doomstone spells & perks. ;Int value used for tracking purposes. Spell property pDoomApprenticeAbility Auto ;1 Spell property pdoomApprenticeNegativeAbility Auto Spell property pDoomAtronachAbility Auto ;2 Spell property pDoomLadyAbility Auto ;3 Spell property pDoomLordAbility Auto ;4 Spell property pDoomLoverAbility Auto ;5 Spell property pDoomMageAbility Auto ;6 Spell property pDoomRitualAbility Auto ;7 Perk property pDoomRitualPerk Auto Spell property pDoomSerpentAbility Auto ;8 Spell property pDoomShadowAbility Auto ;9 Spell property pDoomSteedAbility Auto ;10 Spell property pDoomThiefAbility Auto ;11 Spell property pDoomTowerAbility Auto ;12 Spell property pDoomWarriorAbility Auto ;13 ;Standard doomstone removed messages. Message property pDoomApprenticeRemovedMSG Auto Message property pDoomAtronachRemovedMSG Auto Message property pDoomLadyRemovedMSG Auto Message property pDoomLordRemovedMSG Auto Message property pDoomLoverRemovedMSG Auto Message property pDoomMageRemovedMSG Auto Message property pDoomRitualRemovedMSG Auto Message property pDoomSerpentRemovedMSG Auto Message property pDoomShadowRemovedMSG Auto Message property pDoomSteedRemovedMSG Auto Message property pDoomThiefRemovedMSG Auto Message property pDoomTowerRemovedMSG Auto Message property pDoomWarriorRemovedMSG Auto ;Rested abilities, which have to be removed when the Lover Stone bonus is added. Spell property pRested Auto Spell property pWellRested Auto Spell property pMarriageRested Auto ;The current ability stored in the crown. int currentCrownAbility ;The current doomstone on the player. int currentDoomstoneAbility ;Internal variables for the effect stored in the crown. Spell currentSpell1 Spell currentSpell2 Perk currentPerk Message currentRemoveMessage ;Timestamp of when the player last interacted with a doomstone. float interactionTimestamp = 0.0 ;When the player equips the crown, turn on the activation-trap perk and begin tracking. Event OnEquipped(Actor akActor) if (akActor == Game.GetPlayer()) ;Debug.Trace("CROWN: Aetherial Crown equipped. Current Ability = " + currentCrownAbility) akActor.AddPerk(DLC1LD_AetherialCrownPerk) ; Update the ability in the crown. This might have been screwed up because of an old ; buggy crown script. currentCrownAbility = IdentifyCurrentCrownAbility() ; Find out what the current doomstone effect on the player is, ignoring the effect stored ; in the crown (if any). currentDoomstoneAbility = IdentifyCurrentDoomstoneEffectOnEquip() if (currentCrownAbility != currentDoomstoneAbility) ;Reinstate the last-recorded doomstone ability. ApplyCrownEffect(True) EndIf ;Debug.Trace("CROWN: Aetherial Crown equip done. Current Doomstone recorded as: " + currentDoomstoneAbility) EndIf EndEvent ;When the player removes the crown, turn off the activation-trap perk. Event OnUnequipped(Actor akActor) ;Debug.Trace("CROWN: Aetherial Crown unequipped. Current Ability = " + currentCrownAbility) akActor.RemovePerk(DLC1LD_AetherialCrownPerk) ; Again, update the ability in the crown. This might have been screwed up because of an old ; buggy crown script. currentCrownAbility = IdentifyCurrentCrownAbility() ; Only remove the crown's ability from the player if it's different from the one on the player. If (currentCrownAbility != currentDoomstoneAbility) ApplyCrownEffect(False) EndIf EndEvent ;When the perk catches a Doomstone activation, it calls this function so we can record it. ;We have to wait briefly for the player to exit the Doomstone menu so we know whether he took the new ability. Function DoomstoneActivated() ;Debug.Trace("CROWN: Doomstone activation received.") ;Debug.Trace("CROWN: Current Doomstone recorded as: " + currentDoomstoneAbility) ;Debug.Trace("CROWN: Registering for update.") interactionTimestamp = Utility.GetCurrentRealTime() UnregisterForUpdate() RegisterForSingleUpdate(0.5) EndFunction ;Check to see if the player changed doomstone abilities. If so, change the ability stored in the crown. Event OnUpdate() RunUpdate() EndEvent Function RunUpdate() ;Debug.Trace("CROWN: OnUpdate: " + currentDoomstoneAbility + " - " + IdentifyCurrentDoomstoneEffect()) ;Make sure the currentCrownAbility is correct currentCrownAbility = IdentifyCurrentCrownAbility() ;Did the player change doomstone abilities? Int identifiedEffect = IdentifyNewDoomstoneEffect(currentCrownAbility, currentDoomstoneAbility) ;Debug.Trace("CROWN: We have: " + identifiedEffect + ", " + currentDoomstoneAbility + ", " + currentCrownAbility) if (identifiedEffect > 0) if (identifiedEffect != currentDoomstoneAbility) ;Debug.Trace("CROWN: Now updating.") ApplyCrownEffect(False) ;Remove the old ability stored in the crown. ; Check for a bugged old crown. This means that currentDoomstoneAbility == 0 even though ; there might be two (or more) doomstone abilities on the player right now (the old one ; and the new one). In that case, find the next one. If (currentDoomstoneAbility == 0) Int secondEffect = IdentifyNewDoomstoneEffect(identifiedEffect, 0) If (secondEffect != 0) ; There is a second effect. Move the first one into the crown, and make the second ; one the new doomstone ability. Might be the wrong way around, but is the best we ; can do at this point. currentDoomstoneAbility = identifiedEffect identifiedEffect = secondEffect EndIf EndIf SelectNewCrownEffect(currentDoomstoneAbility) ;Determine which ability should now be stored by the crown. ApplyCrownEffect(True) ;Apply the new ability. currentDoomstoneAbility = identifiedEffect ;Record the current doomstone for reference in the next loop. ElseIf (Utility.GetCurrentRealTime() - interactionTimestamp < 60) ;Debug.Trace("CROWN: Re-Registering for Update, v1.") UnregisterForUpdate() RegisterForSingleUpdate(0.5) Else ;Debug.Trace("CROWN: Timer expired, v1.") EndIf ElseIf (Utility.GetCurrentRealTime() - interactionTimestamp < 60) ;Debug.Trace("CROWN: Re-Registering for Update, v2.") UnregisterForUpdate() RegisterForSingleUpdate(0.5) Else ;Debug.Trace("CROWN: Timer expired, v2.") EndIf EndFunction ;Add or remove the ability stored by the crown. Function ApplyCrownEffect(bool shouldAdd) if (shouldAdd) ;Debug.Trace("CROWN: Adding Crown ability." + currentCrownAbility + " " + currentSpell1) if (currentSpell1 != None) Game.GetPlayer().AddSpell(currentSpell1) if (currentSpell2 != None) Game.GetPlayer().AddSpell(currentSpell2) EndIf if (currentPerk != None) Game.GetPlayer().AddPerk(currentPerk) EndIf EndIf Else ;Debug.Trace("CROWN: Removing Crown ability for " + currentCrownAbility + " " + currentSpell1) if (currentSpell1 != None) Game.GetPlayer().RemoveSpell(currentSpell1) if (currentSpell2 != None) Game.GetPlayer().RemoveSpell(currentSpell2) EndIf if (currentPerk != None) Game.GetPlayer().RemovePerk(currentPerk) EndIf currentRemoveMessage.Show() EndIf EndIf EndFunction ;Given the int value of a new effect (see list at top, or the identifying function below), set up the new effect stored in the crown. Function SelectNewCrownEffect(int newEffect) currentCrownAbility = newEffect If (currentCrownAbility == 0) ; If we get in here, then the old currentDoomstoneAbility was 0, either because ; of an old bugged crown or because the player didn't have a previous doomstone ; ability. Clean out the currently stored ability just to be sure. currentSpell1 = None currentSpell2 = None currentPerk = None currentRemoveMessage = None ElseIf (currentCrownAbility == 1) currentSpell1 = pDoomApprenticeAbility currentSpell2 = pdoomApprenticeNegativeAbility currentPerk = None currentRemoveMessage = pDoomApprenticeRemovedMSG ElseIf (currentCrownAbility == 2) currentSpell1 = pDoomAtronachAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomAtronachRemovedMSG ElseIf (currentCrownAbility == 3) currentSpell1 = pDoomLadyAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomLadyRemovedMSG ElseIf (currentCrownAbility == 4) currentSpell1 = pDoomLordAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomLordRemovedMSG ElseIf (currentCrownAbility == 5) ;Special case - Remove Rested bonuses for Lover. Game.GetPlayer().RemoveSpell(pRested) Game.GetPlayer().RemoveSpell(pWellRested) Game.GetPlayer().RemoveSpell(pMarriageRested) currentSpell1 = pDoomLoverAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomLoverRemovedMSG ElseIf (currentCrownAbility == 6) currentSpell1 = pDoomMageAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomMageRemovedMSG ElseIf (currentCrownAbility == 7) currentSpell1 = pDoomRitualAbility currentSpell2 = None currentPerk = pDoomRitualPerk currentRemoveMessage = pDoomRitualRemovedMSG ElseIf (currentCrownAbility == 8) currentSpell1 = pDoomSerpentAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomSerpentRemovedMSG ElseIf (currentCrownAbility == 9) currentSpell1 = pDoomShadowAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomShadowRemovedMSG ElseIf (currentCrownAbility == 10) currentSpell1 = pDoomSteedAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomSteedRemovedMSG ElseIf (currentCrownAbility == 11) currentSpell1 = pDoomThiefAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomThiefRemovedMSG ElseIf (currentCrownAbility == 12) currentSpell1 = pDoomTowerAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomTowerRemovedMSG ElseIf (currentCrownAbility == 13) currentSpell1 = pDoomWarriorAbility currentSpell2 = None currentPerk = None currentRemoveMessage = pDoomWarriorRemovedMSG EndIf ;Debug.Trace("CROWN: New crown ability selected for " + currentCrownAbility) EndFunction ;Determine which doomstone ability the player has by testing the abilities on them. ; The activator script on the doomstones only removes the first ability it finds, ; which can be either the ability in the crown or the ability actually on the player. ; Therefore we have to ignore both the old crown ability as well as the old doomstone ; ability when looking for the new ability. ; If the old crown effect was screwed up (== 0) then this will find either the previous ; doomstone effect or the new one. Doesn't really matter, we just move one of them ; into the crown, and the other will be the new doomstone effect on the player. int Function IdentifyNewDoomstoneEffect(Int firstReject, Int secondReject) ;Debug.Trace("CROWN: Now Identifying: " + currentCrownAbility) if (Game.GetPlayer().HasSpell(pDoomApprenticeAbility) && firstReject != 1 && secondReject != 1) return 1 ElseIf (Game.GetPlayer().HasSpell(pDoomAtronachAbility) && firstReject != 2 && secondReject != 2) return 2 ElseIf (Game.GetPlayer().HasSpell(pDoomLadyAbility) && firstReject != 3 && secondReject != 3) return 3 ElseIf (Game.GetPlayer().HasSpell(pDoomLordAbility) && firstReject != 4 && secondReject != 4) return 4 ElseIf (Game.GetPlayer().HasSpell(pDoomLoverAbility) && firstReject != 5 && secondReject != 5) return 5 ElseIf (Game.GetPlayer().HasSpell(pDoomMageAbility) && firstReject != 6 && secondReject != 6) return 6 ElseIf (Game.GetPlayer().HasSpell(pDoomRitualAbility) && firstReject != 7 && secondReject != 7) return 7 ElseIf (Game.GetPlayer().HasSpell(pDoomSerpentAbility) && firstReject != 8 && secondReject != 8) return 8 ElseIf (Game.GetPlayer().HasSpell(pDoomShadowAbility) && firstReject != 9 && secondReject != 9) return 9 ElseIf (Game.GetPlayer().HasSpell(pDoomSteedAbility) && firstReject != 10 && secondReject != 10) return 10 ElseIf (Game.GetPlayer().HasSpell(pDoomThiefAbility) && firstReject != 11 && secondReject != 11) return 11 ElseIf (Game.GetPlayer().HasSpell(pDoomTowerAbility) && firstReject != 12 && secondReject != 12) return 12 ElseIf (Game.GetPlayer().HasSpell(pDoomWarriorAbility) && firstReject != 13 && secondReject != 13) return 13 Else return 0 EndIf EndFunction ; Identifies the doomstone effect on the player, and tries to ignore the effect ; stored in the crown. It assumes that the crown is currently NOT equipped, but ; because the player can rapidly equip and unequip items, the old effect might ; still linger. If that should be the case, the bestFit will be returned. Int Function IdentifyCurrentDoomstoneEffectOnEquip() Int bestFit = -1 If (Game.GetPlayer().HasSpell(pDoomApprenticeAbility)) If (currentCrownAbility == 1) bestFit = 1 Else return 1 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomAtronachAbility)) If (currentCrownAbility == 2) bestFit = 2 Else return 2 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomLadyAbility)) If (currentCrownAbility == 3) bestFit = 3 Else return 3 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomLordAbility)) If (currentCrownAbility == 4) bestFit = 4 Else return 4 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomLoverAbility)) If (currentCrownAbility == 5) bestFit = 5 Else return 5 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomMageAbility)) If (currentCrownAbility == 6) bestFit = 6 Else return 6 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomRitualAbility)) If (currentCrownAbility == 7) bestFit = 7 Else return 7 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomSerpentAbility)) If (currentCrownAbility == 8) bestFit = 8 Else return 8 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomShadowAbility)) If (currentCrownAbility == 9) bestFit = 9 Else return 9 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomSteedAbility)) If (currentCrownAbility == 10) bestFit = 10 Else return 10 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomThiefAbility)) If (currentCrownAbility == 11) bestFit = 11 Else return 11 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomTowerAbility)) If (currentCrownAbility == 12) bestFit = 12 Else return 12 EndIf EndIf If (Game.GetPlayer().HasSpell(pDoomWarriorAbility)) If (currentCrownAbility == 13) bestFit = 13 Else return 13 EndIf EndIf return bestFit EndFunction ; Identifies the doomstone ability currently stored in the crown based ; on the spell in currentSpell1. Shouldn't ordinarily be needed since ; currentCrownAbility already tracks this, but there's always potential ; for something to get screwed up, and this should fix it. Int Function IdentifyCurrentCrownAbility() if (currentSpell1 == pDoomApprenticeAbility) return 1 ElseIf (currentSpell1 == pDoomAtronachAbility) return 2 ElseIf (currentSpell1 == pDoomLadyAbility) return 3 ElseIf (currentSpell1 == pDoomLordAbility) return 4 ElseIf (currentSpell1 == pDoomLoverAbility) return 5 ElseIf (currentSpell1 == pDoomMageAbility) return 6 ElseIf (currentSpell1 == pDoomRitualAbility) return 7 ElseIf (currentSpell1 == pDoomSerpentAbility) return 8 ElseIf (currentSpell1 == pDoomShadowAbility) return 9 ElseIf (currentSpell1 == pDoomSteedAbility) return 10 ElseIf (currentSpell1 == pDoomThiefAbility) return 11 ElseIf (currentSpell1 == pDoomTowerAbility) return 12 ElseIf (currentSpell1 == pDoomWarriorAbility) return 13 EndIf return 0 EndFunction Alternatively/additionally you could of course also use a one time script to clean up the player's doomstone abilities, and then use a somewhat less resilient but shorter script for the crown. Ciao, Muh! Edited June 7, 2016 by thebigMuh Link to comment Share on other sites More sharing options...
Kelsenellenelvian Posted June 17, 2016 Share Posted June 17, 2016 I would like to try your proposed changes but have no idea how to generate a pex file. would you send me a compiled script with the above? Link to comment Share on other sites More sharing options...
thebigMuh Posted June 17, 2016 Author Share Posted June 17, 2016 Sure, please drop the file (DLC1LD_AetherialCrownScript.pex) into your Skyrim/Data/Scripts folder: http://filebin.ca/2kzZSnMNczpG Ciao, Muh! Link to comment Share on other sites More sharing options...
Kelsenellenelvian Posted June 17, 2016 Share Posted June 17, 2016 THANK YOU! Link to comment Share on other sites More sharing options...
Kelsenellenelvian Posted May 14, 2017 Share Posted May 14, 2017 May I include this in a mod of mine? Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now