Profane Awakening is a single player mod for The Elder Scrolls V: Skyrim that adds content to the city of Markarth, an expedition encampment outside the city, as well as a new dungeon with two new puzzles and a multi-stage boss encounter. The player must recover a Dwemer artifact from a Breton noble and then use it to open a portal to a lost Dwemer city. After entering the portal the player is stranded on the other side and must find another way out of the city. The city is home to an artifact from the realm of Apocrypha, a Black Book. In order to exit the city, the player must recover the book and defeat the spirit of a dragon held within the tome. The player’s actions in the recovery of the original artifact influence the reception she receives upon exiting the ruined city.
Skyrim is, at its core, an open world game where the player decides what she wants to do. I wanted to allow the player to experience Profane Awakening in whatever manner they chose to. The player can use all the dialogue options (Intimidate, Persuade) available in their interactions with the quest NPCs, or is free to pickpocket essential items, or simply kill them and be on their way. The player can even proceed down an avenue in order to get rewards from NPCs before deciding to double cross them if they are so inclined. The decisions of the player impact the outcome of various actors at the end of the quest and lead to a pair of distinct endings.
The player can, via an optional side quest, afflict the holder of the Dwemer artifact with a ring that turns him into a rabbit. Depending on how the player dealt with the woman that the quest giver ordered dead, she can stride in to defuse the situation, or the quest giver’s guards will attack the player. Having small permutations of player decisions matter was something I wanted to pursue when making this module.
Halfway through the module, the player opens a gateway to the dungeon section, the city of Tzalche. Considering the Dwemer are known for their technological savvy, they might have constructed some elaborate ways to travel to their far flung cities. Using existing assets, I put together a portal sequence to transport the player from Skyrim proper to the lost city. This sequence provides a memorable moment in the module’s experience, as well as setting a tone for the further Dwemer wonders to be found later in the module.
Portal Sequence – Code Sample
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
Scriptname dunTzalchePortalSequence extends ObjectReference { Sequence for opening the portal that leads to Tzalche / Back to Tamriel } Activator Property PortalFX Auto Spell Property BeamSpell Auto ; Lightning beam spell ObjectReference[] Property SpellSources Auto ; Lightning crystal sources ObjectReference Property SpellTarget Auto ; XMarker lightning fires at Float Property SpellDelay Auto ; Delay between lightning casts Float Property SteamMeterDelay Auto ; Delay between steam meter enables Float Property SteamFXDelay Auto ; Delay between steam FX enables ObjectReference Property PortalLight Auto ObjectReference[] Property BlueLights Auto ; Lights for the initial trigger sequence of the steam meters ObjectReference[] Property OrangeLights Auto ; Lights for the final trigger sequence of the steam meters dunTzalchePistonScript Property PistonOne Auto ; Scripts to activate pistons dunTzalchePistonScript Property PistonTwo Auto dunTzalchePistonScript Property PistonThree Auto dunTzalchePistonScript Property PistonFour Auto dunTzFXDustScript Property DustOne Auto ; Dust activation scripts dunTzFXDustScript Property DustTwo Auto Sound Property AMBRumbleShake Auto Sound Property AMBRumbleShakeGreybeards Auto ObjectReference Property SteamSFX01 Auto ObjectReference Property SteamSFX02 Auto ObjectReference Property SteamSFX03 Auto ObjectReference[] Property SteamFX Auto ; Misc steam particles in the room dunTzalcheSteamMeterScript[] Property SteamMeters Auto ; Links to left steam meters { References to FX locations and where the portal will appear } ObjectReference Property PortalSpawnLoc Auto ObjectReference Property PortalTrigger Auto ObjectReference Property MachinerySFX01 Auto ObjectReference Property MachinerySFX02 Auto ObjectReference Property ShockSFX Auto Explosion Property PortalExplosion Auto ImageSpaceModifier Property PortalMod Auto Bool Property PortalOpen Auto Bool CastingInterrupted = False Quest Property dunTzalcheQuest01 Auto Bool Property bSetQuestStage Auto Int Property QuestStage Auto { When the Control Cube is placed on the pedestal the sequence to open the portal to Tzalche begins. } Event OnActivate(ObjectReference akActivator) if(!PortalOpen) int i = 0; DustOne.Activate(self); PistonOne.Activate(self); Utility.Wait(SteamFXDelay); PistonTwo.Activate(self); Utility.Wait(SteamFXDelay); PistonThree.Activate(self); Utility.Wait(SteamFXDelay); PistonFour.Activate(self); MachinerySFX01.enable() AMBRumbleShakeGreybeards.Play(self); Game.ShakeCamera(none, 0.5, 2.0); SteamSFX01.enable() ; Activating steam / sounds while(i < SteamFX.Length) SteamFX[i].enable(); Game.ShakeCamera(none, 0.25, 0.5); Utility.Wait(SteamFXDelay); SteamSFX02.enable() if( i > SteamFX.Length / 2 ) DustOne.Activate(self); endif i += 1; endWhile SteamSFX03.enable() FillMeters(false); DustOne.Activate(self); Game.ShakeCamera(none, 0.5, 2.0); i = 0; MachinerySFX02.enable() ; Lightning FX while(i < SpellSources.Length) if(i == 0) ShockSFX.enable(); endif BeamSpell.Cast(SpellSources[i], SpellTarget); Utility.Wait(SpellDelay); i += 1; endWhile FillMeters(true); DustTwo.Activate(self); Game.ShakeCamera(none, 0.5, 2.0); PortalLight.enable(); Game.GetPlayer().RampRumble(0.15, 1.5, 1600) AMBRumbleShake.Play(self); PortalOpen = true; PortalTrigger.enable(); PortalMod.Apply(); if(bSetQuestStage) dunTzalcheQuest01.SetStage(QuestStage) endif SpawnPortal(); PortalSpawnLoc.PlaceAtMe(PortalExplosion); endIf EndEvent { Determines which state to set steam meters in, false sets to blue, true to orange. } Function FillMeters(bool orangeLights) int i = 0; while(i < SteamMeters.Length) SteamMeters[i].FillMeter(); if(orangeLights) BlueLights[i].disable(); OrangeLights[i].enable(); else BlueLights[i].enable(); endif Utility.Wait(SteamMeterDelay); i += 1; endWhile EndFunction ; Brings the meters back to their emptied state Function EmptyMeters() int i = 0; while(i < SteamMeters.Length) SteamMeters[i].EmptyMeter(); OrangeLights[i].disable(); SteamMeters[i].EmptyMeter(); Utility.Wait(SteamMeterDelay); i += 1; endWhile EndFunction ; Spawns the portal FX on a delay to give it the apperance of a constant portal Function SpawnPortal() ObjectReference Portal = PortalSpawnLoc.PlaceAtMe(PortalFX); Portal.SetScale(1.2); If(!CastingInterrupted) int i = 0; while(i < SpellSources.Length) SpellSources[i].InterruptCast(); i += 1; endWhile CastingInterrupted = true EndIf Utility.Wait(0.5); If(PortalOpen) SpawnPortal(); endIf EndFunction |
Skyrim has its stock puzzles, but I wanted to extend and create my own puzzles. I chose lightning as a theme within my dungeon, as a manner of power to supplement the normal steam power associated with the Dwemer. I designed two different lightning based puzzles, one which takes a mechanic in Skyrim and extends it and one that implements a new one.
The first sees the player activating terminal and spinning them in order to connect to the next terminal in order to progress along the main path of the dungeon. There is both an introductory version of this puzzle and a more advanced one, punctuated by combat, later in the dungeon.
The second has the player create a pattern of activated buttons corresponding to lit up meters on the wall. The player needs to devise the manner in which pressing the buttons affects which buttons are active in order to succeed at the puzzle. There are two stages to this puzzle, one that serves as a tutorial and then a more advanced pattern. The player can at any time switch off the puzzle in order to restart from the beginning of the current stage.
Nine Crystal Puzzle – Code Sample
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
Scriptname dunTzNineCrystalController extends ObjectReference { Control Script for Nine Plate Puzzle, Advances stages and checks to see if the player has the correct pattern Called primary by dunTzNineCrystalPlateScript which is attached the the buttons in the puzzle } dunTzNineCrystalPlateScript[] Property Plates Auto dunTzalcheSteamMeterScript[] Property PuzzleState Auto ObjectReference Property ResonatorOne Auto ObjectReference Property ResonatorTwo Auto Explosion Property SuccessExplosion Auto Explosion Property ActivationExplosion Auto Int[] Property SolutionOne Auto Int[] Property SolutionTwo Auto Int[] Property CurrentPuzzleState Auto Int Property PuzzleStage Auto Spell Property dunTzalcheLightBeam Auto ;Pistons in the Cavern ObjectReference Property NineCrystS1P01 Auto ObjectReference Property NineCrystS1P02 Auto ObjectReference Property NineCrystS2P01 Auto ObjectReference Property NineCrystS2P02 Auto ;Progress Indicators ObjectReference Property SequenceOneSource Auto ObjectReference Property SequenceOneTarget Auto ObjectReference Property SequenceTwoSource Auto ObjectReference Property SequenceTwoTarget Auto ;Sounds in Cavern ObjectReference Property MachinerySound01 Auto ObjectReference Property MachinerySound02 Auto ;Doors in Cavern References ObjectReference Property DoorSealCaster1 Auto ObjectReference Property DoorSealCaster2 Auto ObjectReference Property DoorSealCasterTarget Auto dunTzDoorSeal Property DoorSeal Auto ObjectReference Property ExploMarker Auto ObjectReference Property puzzleLightBlue Auto ObjectReference Property puzzleLightOrange Auto ;Markers for indication of where to go ObjectReference Property IntermediarySource Auto ObjectReference Property IntermediaryTarget Auto ObjectReference Property FinalSource01 Auto ObjectReference Property FinalSource02 Auto ObjectReference Property FinalTarget01 Auto dunTzActivateDoor Property SealButton Auto { Generates effects and sound as well as starting the puzzle in whatever stage the player is currently on } Function ActivatePuzzle() int i = 0; while(i < PuzzleState.Length) if(PuzzleStage == 1) if(SolutionOne[i] == 1) ExploMarker.PlaceAtMe(ActivationExplosion) PuzzleState[i].FillMeter() puzzleLightBlue.enable() endif elseif(PuzzleStage == 2) if(SolutionTwo[i] == 1) PuzzleState[i].FillMeter() puzzleLightBlue.enable() ;Plates[2].TurnOn() endif endIf Plates[i].TurnButtonOn(); i += 1; endWhile EndFunction { Moves between the two puzzle stages } Function AdvanceStage() int i = 0; while(i < Plates.Length) Plates[i].BlockProcessing = false; i += 1; endWhile PuzzleStage = 2; ActivatePuzzle(); EndFunction { Resets the puzzle to initial state when the lever in the chamber is switched off/on } Function ResetPuzzle() int i = 0; while(i < Plates.Length) if(PuzzleStage == 1) PuzzleState[i].EmptyMeter(); elseif(PuzzleStage == 2) PuzzleState[i].EmptyMeter(); endIf if(Plates[i].Activated) Plates[i].TurnOff(); endIf CurrentPuzzleState[i] = 0 Plates[i].TurnButtonOff(); i += 1; endWhile EndFunction { Evaluates the current state of the puzzle against the solution and moves onto the 2nd stage if the player has the solution, activating effects and sounds } Function TestSolution() if(PuzzleStage == 1) bool stageOneFinished = ComparePuzzleState() if(stageOneFinished) ExploMarker.PlaceAtMe(SuccessExplosion); NineCrystS1P01.Activate(self); Utility.Wait(0.5); NineCrystS1P02.Activate(self); MachinerySound01.Enable(); dunTzalcheLightBeam.Cast(SequenceOneSource, SequenceOneTarget); dunTzalcheLightBeam.Cast(FinalSource01, FinalTarget01); ResetPuzzle(); AdvanceStage(); endIf elseif(PuzzleStage == 2) bool stageTwoFinished = ComparePuzzleState() if(stageTwoFinished) ExploMarker.PlaceAtMe(SuccessExplosion); NineCrystS2P01.Activate(self); Utility.Wait(0.5); NineCrystS2P02.Activate(self); MachinerySound02.Enable(); SealButton.TurnOn(); dunTzalcheLightBeam.Cast(SequenceTwoSource, SequenceTwoTarget); dunTzalcheLightBeam.Cast(FinalSource02, FinalTarget01); ShutdownPuzzle(); endIf endIf EndFunction { Turns off the puzzle when complete and activates door/effects to guide the player to the next part of the dungeon } Function ShutdownPuzzle() DoorSeal.bPuzzleComplete = true; DoorSeal.Activate(self); dunTzalcheLightBeam.Cast(DoorSealCaster1, DoorSealCasterTarget); dunTzalcheLightBeam.Cast(DoorSealCaster2, DoorSealCasterTarget); dunTzalcheLightBeam.Cast(IntermediarySource, IntermediaryTarget); int i = 0; While(i < Plates.Length) Plates[i].TurnOn(); Plates[i].playAnimation("Close") Plates[i].goToState("Done"); PuzzleState[i].FillMeter(); PuzzleState[i].FillMeter(); i += 1; EndWhile EndFunction Bool Function ComparePuzzleState() int i = 0; int wrong = 0; int[] Solution; If(PuzzleStage == 1) Solution = SolutionOne ElseIf(PuzzleStage == 2) Solution = SolutionTwo EndIf While(i < Solution.Length) If(Solution[i] != CurrentPuzzleState[i]) wrong += 1; Endif i += 1; EndWhile If(wrong < 0) return false Else return true; Endif EndFunction |
Below you can find an HTML5 implementation of the second puzzle’s base logic:
Whenever a player finds a Black Book in Dragonborn they can choose from three unique abilities upon completing the dungeon associated with the book. So it made sense for me to create three abilities for my book as well. I decided to tailor the abilities to the three archetypal playstyles in role-playing game: Mage, Warrior, and Thief. This way each player receives something that complements their playstyle that won’t be outgrown like a weapon.
Above is of one of the abilities, the adorable fire bear named Gustave. This is a spell that summons the companion to accompany the player until he meets his demise, akin to the Spectral Assassin spell in the base game. I modified the existing bear model in Skyrim and changed the vertex coloring to get Gustave into shape.