Jordan Savant # Software Engineer

The Firmament Oblivion MOD - Part 1: Scripting

The Firmament Image

The goal of this mod was to detect what constellations were in the sky and bless the player with a day long boost of magical fortification relevant to the stars present. I felt like this could be a lore friendly mod that would give the player a bit of power.

You can download a the plugin directly or support it on Nexus Mod Manager

The Lore

There are 13 constellations over Tamriel each with a month that represents their zenith:

MONTH               CONSTELLATION
Morning Star        The Ritual
Sun's Dawn          The Lover
First Seed          The Lord
Rain's Hand         The Mage
Second Seed         The Shadow
Mid Year            The Steed
Sun's Height        The Apprentice
Last Seed           The Warrior
Hearthfire          The Lady
Frost Fall          The Tower
Sun's Dusk          The Atronach
Evening Star        The Thief

There is also a 13th constellation, known as The Serpent that rises in opposition against the constellation of the month. This is very interesting I would like to come back to this element as part of a random chance negative effect.

Each constellation has its own lore and representation which I believe is best referenced from the effects of the doomstone powers you can find throughout the landscape.

My idea was essentially to detect if there was a clear night sky above the player, then calculate the current game month and if a spell was active or not and then cast the relevant spell on the player. I started with The Ritual which is the first month. I also found some relevant information I could glean from the Construction Kit functions:

  • TimeScale is 30 mins in game for 1 min out of game meaning a day cycle is 48 minutes
  • There is a global variable for IsPleasant as well as some other weather detector methods
  • Game time variables including month can be pulled from global variables

First Failures

My initial idea was to create a Quest Script than ran and detected these conditions and casted a spell on the player. But I also needed a spell. So I went into Object Window and created a new spell called AAARitualFortHealth which would represent The Ritual star sign and cast a fortify health effect on the player for 48 minutes.

This produced a spell that cost over 6000 magicka to cast.

I then went into the Quest Script and attempted to cast the spell from the Player on the Player with: Player.Cast AAARitualFortHealth Player. I then attached the script to an on-running quest and loaded up. When I did it failed with Not enough magicka to cast this spell.

Well this was disconcerting. I tried to search online for how to reduce a spell cost or cast a spell from a quest without the player but since its a Quest Spell you must have an object reference variable to cast from (it is not running within the scope of an Object) and the player is the only global reference variable I could grab. Online there was little information mostly crowded out by Skyrim modding questions.

I knew from the tutorial that I could cast a spell from an object in the world OnActivate and thought perhaps I could create an object in the script and cast it from there.

ScriptName AAAConstellationPowers

Ref castItem
short state

; The Ritual = AAARitualFortHealth

Begin GameMode
    If ( state == 0 )
        Set castItem to BlankPaper
        Player.addItem BlankPaper 1
        castItem.Cast AAARitualFortHealth Player
        Player.removeItem castItem 1
        Set state to 1
    EndIf
End

In this script I was creating a blank paper object and casting from it to the player and then removing it from the player. This failed. It would add an item called Parchment into the character inventory but never cast the spell.

I assumed that it had to do with scope or something. I slept on it and had a second idea. What if I created an Object Script and which could cast the spell from within the item itself (kind of like the lectern) and place the item on the character instead?

So I created a new Object in game with the texture of a Sigil Stone and 3D model of a Flawless Pearl and called it "Constellation Stone". I adjusted the script to place the item on the player, instead of the parchment and attached the following object script to the item:

scriptname AAAConstellationStoneScr

short state

Begin GameMode

    if ( state == 0 )
        Message "Stone init"
        Cast AAARitualFortHealth Player
        Set state to 1
    ElseIf ( state == 1 )
    EndIf

End

Upon running It would add the Constellation Stone to the player inventory and also print "Stone init" to the screen but never cast the spell. I decided that perhaps it was the spell effect type. I realized the spell was still on target "self" and switched it to "target" so that the item could cast the spell on the player and tried again. But alas it still failed.

It was a nice learning experience for creating objects and adding items to inventory but I believed that it must have been either the spell cost was too high for the item's magicka (what?) or casting spell from an inventory was impossible. I leaned toward the latter explanation.

Remember, this was all because I could not cast the spell from the player because of the spell cost.

I continued to Google until I found one random comment for Skyrim on a similar problem:

Did you turn off auto-calculate for the spell?

  • Thanestus

I knew that the Skyrim Creation Kit was an evolution to the TES Construction Kit so I went back into the spell I had created and voila! There was a small check box called autocalculate. By disabling this the spell cost area became editable and I could reduce it to 0. I could also reduce the skill requirement to Novice. I adjusted my quest script to remove the Constellation Stone and cast the spell itself:

ScriptName AAAConstellationPowers

Ref castItem
short state

; The Ritual = AAARitualFortHealth

Begin GameMode
    If ( state == 0 )

        ; see if any spell is active
        Message "start"

        ;Set castItem to AAAConstellationStone
        ;Player.addItem AAAConstellationStone 1
        ;castItem.Cast AAARitualFortHealth Player
        ;Player.removeItem castItem 1

        Player.Cast AAARitualFortHealth Player
        Message "end"

        Set state to 1
    EndIf
End

Casting the Fortification Spell successfully

Success!

I could now cast spells directly on the player from a Quest Script without requiring Objects and item manipulation. I went to clean the cruft up I had created and turned toward condition detection for when to cast.

Conditions for Constellations

  1. Weather must be clear
  2. I must be outside
  3. It must be night time
  4. Get the current month / constellation to apply the effect

Detecting if Weather is Clear

There seems to be a large list of weather functions ()[https://cs.elderscrolls.com/index.php?title=Category:Weather_Functions] that could meet my needs but there is also a simple IsPleasant method I can run to determine if the weather is clear that seemed sufficient.

Testing showed this as working but if I saved it to a short such as Set pleasant to IsPleasant it seemed to always resolve to 0 even if directly using IsPleasant in an if condition afterwards equated to true.

Also of help was changing the weather with the console:

SetWeather 00038EEE ; clear
SetWeather 00038EFE ; cloudy

Chalking up that to oddity in the engine I moved on to the next condition.

Detecting if Outside

This became a bit more complicated and helped me understand the engine. The game is setup with physical locations as "cells". There can either be Interior Cells such as rooms or dungeons that have finite space and walls or Exterior Cells that span in infinite directions such as the outside world.

In the realm of Exterior Cells the game is divided into WorldSpace locations. These represent our classical game locations: Tamriel, Oblivion, Sheogorath and other quest locations such as the Painted World and Dream World. An exterior WorldSpace can be parented by another WorldSpace as well which is how cities work. SkingradWorld, BrumaWorld, AnvilWorld, KvatchEastWorld, KvatchWestWorld etc are all city locations that are parented by Tamriel.

Then there is the function GetInWorldspace that takes a WorldSpace ID and runs off of an Object reference and returns a 1 or 0. Unfortunately this does not detect if for parent world spaces so if I am walking around the outside in Skingrad and I want to know If ( Player.GetInWorldspace Tamriel ) it will be false. So for each city space we would need to check the condition.

Because of that, like the other signs I will delay adding the checks for the cities and focus on Tamriel for now.

Detecting if Night Time

There was a global variable GameHour that I was able to look into that provided the time of day. With some experimentation it was mostly dark from 21:00 to 05:00 and so a condition to that time bound was sufficient

Putting the Conditions Together

Barring the WorldSpaces for cities I was able to specify our condition detection with:

Short pleasant
Short inworld
Short atnight

if ( GameHour < 5 || GameHour > 21)
    Set atnight to 1
Else
    Set atnight to 0
EndIf
Set pleasant to IsPleasant
Set inworld to Player.GetInWorldspace Tamriel
Message  "inworld %.1f pleasant %.1f atnight %.1f" inworld pleasant atnight

if ( pleasant == 1 && inworld == 1 && atnight == 1 )
    Message "is pleasant and in tamriel at night"
EndIf

And with WorldSpace detection the script was shaping up as

ScriptName AAAConstellationPowers

;MONTH              CONSTELLATION           Guardian            Lore                                Power
;Morning Star       The Ritual                  The Mage        Abilties based on moon      Healing
;Sun's Dawn     The Lover                   The Thief       Graceful and passionate     Agility
;First Seed         The Lord                        The Warrior     Strong and healthy          RestoreHealth
;Rain's Hand        The Mage                                        Magical and arrogant            Magicka
;Second Seed        The Shadow                  The Thief       Hidden                          Invisibility
;Mid Year           The Steed                   The Warrior     Impatient and fast              Speed
;Sun's Height       The Apprentice              The Mage        Magical but weak                MagickaIncrease/MagicWeakness
;Last Seed          The Warrior                                     Warfare but ill-tempered        Attack/Strength/Endurance
;Hearthfire         The Lady                    The Warrior     Kindness and tolerance      Personality/Willpower
;Frost Fall         The Tower                   The Thief       Goldfinder and lockpicker   Open/ReflectDamage/Detect
;Sun's Dusk         The Atronach                The Mage        Magical without magicka     SpellAbsorb/FortMagicka/StuntedMagicka
;Evening Star       The Thief                                       Risky, lucky and shortlived Agility/Luck/Speed

;                       The Serpent                                     Wandering, most blessed and cursed  Dispell/Poison/Paralyze

; The Ritual = AAARitualFortHealth

short state
short pleasant
short inworld
short atnight

Begin GameMode
    If ( state == 0 )

        ; see if any spell is active
        Message "start"
        Player.Cast AAARitualFortHealth Player
        Message "end"
        Set state to 1

    ElseIf ( state == 1 )

        ; Lets test some weather conditions to see if we can get:
        ; 1. clear skies
        ; 2. month of the Ritual / Morning Star
        ; 3. outside
        ; 4. its night time

        if ( Player.GetInWorldspace Tamriel )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace SkingradWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace AnvilWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace BravilWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace BrumaWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace CheydinhalWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace ChorrolWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace ICArboretumDistrict || Player.GetInWorldspace ICArenaDistrict || Player.GetInWorldspace ICElvenGardensDistrict )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace ICImperialPalace || Player.GetInWorldspace ICImperialPalaceMQ16 || Player.GetInWorldspace ICImperialPrisonDistrict || Player.GetInWorldspace ICMarketDistrict  )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace ICMarketDistrict || Player.GetInWorldspace ICTalosPlazaDistrict || Player.GetInWorldspace ICTempleDistrict || Player.GetInWorldspace ICTheArcaneUniversity )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace KvatchEast || Player.GetInWorldspace KvatchEntrance || Player.GetInWorldspace KvatchPlaza)
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace LeyawiinWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace SkingradWorld )
            Set inworld to 1
        EndIf
        ; Message  "inworld %.0f pleasant %.0f cloudy %.0f raining %.0f snowing %.0f" inworld pleasant cloudy raining snowing

        if ( GameHour < 5 || GameHour > 21)
            Set atnight to 1
        Else
            Set atnight to 0
        EndIf

        Set pleasant to IsPleasant

        Message  "inworld %.1f pleasant %.1f atnight %.1f" inworld pleasant atnight

        if ( pleasant == 1 && inworld == 1 && atnight == 1 )
            ; Message "is pleasant and in tamriel at night"
        EndIf

    EndIf
End

Moving into the Firmament Powers

Making powers and spells was a fun and creative part. I researched the Lore by reading the in-game book "The Firmament" which describes the 13 constellations. There are three guardian constellations: The Mage, The Thief and The Warrior that take charge over another three constellations each. Then the 13th constellation is The Serpent which wars against the others. What interesting lore!

I took the spellcrafting to give bonuses and some negative effects based on each constellations lore. The spellcrafting part was straight forward once I had resolved the casting issues described earlier. I was still unable to determine how to cast a mod spell in-game with the console.

Here is a screenshot of the spells created, all prefixed with AAA:

Editor Screen of Spell

Once the spells were defined I would simply need to If ElseIf conditional the GameMonth variable into which one was cast. Apparently the state of variables for a quest are persisted with the save file so there will be some challenges are running these states on a timer which we will get to later.

Extra Conditions: Are we Looking at the Sky?

I wanted to know if the player could actually look up and us detect them admiring the sky at night? And it turns out we can with the function GetAngle. This function takes an angle argument and returns the angle of the camera: X, Y or Z

Upon playtesting the X angle represents our pitch and therefore our angle looking up or down, and anything between -90 and -55 was mostly skyward. The Y angle was our yaw, which is not something we rotate in the game, like tilting your head it would be disorienting so it is always 0 (unless staggered). The Z is our heading on the surface so it is our cardinal North, South, East and West directions.

By checking Player.GetAngle X and checking if less than -55 we could tell if we were looking at the sky.

Extra Conditions: Do we have the book "The Firmament"?

I felt like the blessing should only be bestowed on someone carrying The Firmament book, as a lover of the stars and studier of the heavens so I needed to know if the player had the book.

We could facilitate this with the function GetItemCount:

Set bookcount to Player.GetItemCount Book3ValuableTheFirmament

This gives us an inventory count for the book on the player and anything over 0 was good.

Extra Conditions: Are we affected by a spell?

This one was going to be very important to not constantly cast the same spell over and over again and honestly I should have determined this sooner as its not so much an "extra" condition but a mandatory one. Once a spell is cast its Magic Effects are applied to the character. Fortunately there appears to be a pointer back to the originating spell in-game as you can see the source of your magic effects in the magic panel in game.

We can recreate this reference with IsSpellTarget which queries active effects to see if they are the result of a spell. By doing these with each of our created spells we could determine if they had already been cast:

Set haslover to Player.IsSpellTarget AAATheLover

Assets, Textures, Icons - The BSA Extractor

I wanted to create a note to introduce this quest to the player. If the player had not received the note before to receive it in their inventory. Then to move into the primary stage of the quest where the blessings could be attained. But when I went to create the note I realized none of those assets were avilable in the Data folder. Only things in there were assets from other mods.

It appears that all of the assets for Oblivion are in compressed BSA files that require the BSA Browser to extract. Once I had it downloaded I could load up the Oblivion BSA and extra the icons and models to another folder.

From there I was able to move my icons to their Data directory location. They had to be placed in the same path as the BSA path in order for the editor to accept them. Here is a picture of the BSA explorer and icons extracted behind it:

BSA Browser with Icons

And here is an image of the letter I created in the editor:

Firmament Quest Letter


Putting it Altogether

A few more bells and whistles were added to aid the player in the mod:

  • A letter is dropped in the players inventory from the "Cult of Stars" guiding them on conditions
  • A starcounter variable will notify the player if there are ideal conditions to stargaze
  • The Serpent will either curse or bless depending on if you use his birthsign

Here is the 1.0 version of the script for the Quest:

ScriptName AAAConstellationPowers

;MONTH              CONSTELLATION           GUARDIAN        LORE                                POWER
;Morning Star       The Ritual              The Mage        Abilties based on moon              Healing
;Sun's Dawn         The Lover               The Thief       Graceful and passionate             Agility
;First Seed         The Lord                The Warrior     Strong and healthy                  RestoreHealth
;Rain's Hand        The Mage                                Magical and arrogant                Magicka
;Second Seed        The Shadow              The Thief       Hidden                              Invisibility
;Mid Year           The Steed               The Warrior     Impatient and fast                  Speed
;Sun's Height       The Apprentice          The Mage        Magical but weak                    MagickaIncrease/MagicWeakness
;Last Seed          The Warrior                             Warfare but ill-tempered            Attack/Strength/Endurance
;Hearthfire         The Lady                The Warrior     Kindness and tolerance              Personality/Willpower
;Frost Fall         The Tower               The Thief       Goldfinder and lockpicker           Open/ReflectDamage/Detect
;Sun's Dusk         The Atronach            The Mage        Magical without magicka             SpellAbsorb/FortMagicka/StuntedMagicka
;Evening Star       The Thief                               Risky, lucky and shortlived         Agility/Luck/Speed
;                   The Serpent                             Wandering, most blessed and cursed  Dispell/Poison/Paralyze

short state
short pleasant
short inworld
short atnight
short lookup
short hasbook
short hasnote
short isserpentsign

short hasritual
short haslover
short haslord
short hasmage
short hasshadow
short hassteed
short hasapprentice
short haswarrior
short haslady
short hastower
short hasatronach
short hasthief
short hasserpent
short hasserpentcurse

short rotx
short roty
short rotz
short bookcount
short dice
short notecount
short starcounter

Begin GameMode

    If ( state == 0 )

        Set notecount to Player.GetItemCount AAAFirmamentGuide
        if ( notecount == 0 )
            MessageBox "You discover a letter tucked amongst your belongings that had gone unnoticed until now."
            Player.AddItem AAAFirmamentGuide 1
            Set starcounter to 1000 ; will proc as soon as conditions are met
            Set state to 1 ; once it has been set, then move on
        EndIf

    ElseIf ( state == 1 )

        if ( Player.GetInWorldspace Tamriel )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace SkingradWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace AnvilWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace BravilWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace BrumaWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace CheydinhalWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace ChorrolWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace ICArboretumDistrict || Player.GetInWorldspace ICArenaDistrict || Player.GetInWorldspace ICElvenGardensDistrict )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace ICImperialPalace || Player.GetInWorldspace ICImperialPalaceMQ16 || Player.GetInWorldspace ICImperialPrisonDistrict || Player.GetInWorldspace ICMarketDistrict  )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace ICMarketDistrict || Player.GetInWorldspace ICTalosPlazaDistrict || Player.GetInWorldspace ICTempleDistrict || Player.GetInWorldspace ICTheArcaneUniversity )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace KvatchEast || Player.GetInWorldspace KvatchEntrance || Player.GetInWorldspace KvatchPlaza)
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace LeyawiinWorld )
            Set inworld to 1
        ElseIf ( Player.GetInWorldspace SkingradWorld )
            Set inworld to 1
        EndIf

        if ( GameHour < 5 || GameHour > 21)
            Set atnight to 1
        Else
            Set atnight to 0
        EndIf

        Set pleasant to IsPleasant

        Set rotx to Player.GetAngle X ; up and down
        Set roty to Player.GetAngle Y ; always 0
        Set rotz to Player.GetAngle Z ; NSEW heading

        if ( rotx < -55 )
            Set lookup to 1
        Else
            Set lookup to 0
        EndIf

        Set bookcount to Player.GetItemCount Book3ValuableTheFirmament
        If ( bookcount > 0 )
            Set hasbook to 1
        Else
            Set hasbook to 0
        EndIf

        Set hasritual to Player.IsSpellTarget AAATheRitual
        Set haslover to Player.IsSpellTarget AAATheLover
        Set haslord to Player.IsSpellTarget AAATheLord
        Set hasmage to Player.IsSpellTarget AAATheMage
        Set hasshadow to Player.IsSpellTarget AAATheShadow
        Set hassteed to Player.IsSpellTarget AAATheSteed
        Set hasapprentice to Player.IsSpellTarget AAATheApprentice
        Set haswarrior to Player.IsSpellTarget AAATheWarrior
        Set haslady to Player.IsSpellTarget AAATheLady
        Set hastower to Player.IsSpellTarget AAATheTower
        Set hasatronach to Player.IsSpellTarget AAATheAtronach
        Set hasthief to Player.IsSpellTarget AAATheThief
        Set hasserpent to Player.IsSpellTarget AAATheSerpent
        Set hasserpentcurse to Player.IsSpellTarget AAATheSerpentCurse

        Set isserpentsign to Player.GetIsPlayerBirthsign BirthSignSerpent
        Set dice to 1 + .5 * GetRandomPercent  ; 1 in 50 chance

        ; Message  "inworld %g pleasant %g atnight %g lookup %g hasbook %g isserpentsign  %g dice %.2f" inworld pleasant atnight lookup hasbook isserpentsign dice

        ; starcounter will notify of conditions only ever hour at most
        if ( starcounter >= 720 && inworld == 1 && atnight == 1 && pleasant == 1 && hasbook == 1)
            Message "The stars shine brightly."
            Set starcounter to 0
        Else
            Set starcounter to starcounter + 1
            ; Message "starcounter %g" starcounter
        EndIf

        if ( pleasant == 1 && inworld == 1 && atnight == 1 && lookup == 1 && hasbook == 1 )
            ; Message "is pleasant and in tamriel at night looking up with the book"

            if ( dice == 1)
                if ( isserpentsign == 1 && hasserpent == 0  )
                    MessageBox "The Serpent wanders about in the sky and has no Season, though its motions are predictable to a degree. No characteristics are common to all who are born under the sign of the Serpent. Those born under this sign are the most blessed and the most cursed. Be blessed child of the serpent."
                    Player.Cast AAATheSerpent Player
                ElseIf ( isserpentsign == 0 && hasserpentcurse == 0 )
                    MessageBox "The Serpent wanders about in the sky and has no Season, though its motions are predictable to a degree. No characteristics are common to all who are born under the sign of the Serpent. Those born under this sign are the most blessed and the most cursed. Be cursed child not mine."
                    Player.Cast AAATheSerpentCurse Player
                EndIf
            Elseif (GameMonth == 0 && hasritual == 0) 
                MessageBox "The Ritual is one of the Mage's Charges and its Season is Morning Star. Those born under this sign have a variety of abilities depending on the aspects of the moons and the Divine. Be blessed by moon and star."
                Player.Cast AAATheRitual Player
            ElseIf (GameMonth == 1 && haslover == 0)
                MessageBox "The Lover is one of the Thief's Charges and her season is Sun's Dawn. Those born under the sign of the Lover are graceful and passionate. Be blessed graceful one."
                Player.Cast AAATheLover Player
            ElseIf (GameMonth == 2 && haslord == 0)
                MessageBox "The Lord's Season is First Seed and he oversees all of Tamriel during the planting. Those born under the sign of the Lord are stronger and healthier than those born under other signs. Be blessed, hearty and strong."
                Player.Cast AAATheLord Player
            ElseIf (GameMonth == 3 && hasmage == 0)
                MessageBox "The Mage is a Guardian Constellation whose Season is Rain's Hand when magicka was first used by men. Those born under the Mage have more magicka and talent for all kinds of spellcasting, but are often arrogant and absent-minded. Be blessed arcane one."
                Player.Cast AAATheMage Player
            ElseIf (GameMonth == 4 && hasshadow == 0)
                MessageBox "The Shadow's Season is Second Seed. The Shadow grants those born under her sign the ability to hide in shadows. Be blessed night child."
                Player.Cast AAATheShadow Player
            ElseIf (GameMonth == 5 && hassteed == 0)
                MessageBox "The Steed is one of the Warrior's Charges, and her Season is Mid Year. Those born under the sign of the Steed are impatient and always hurrying from one place to another. Be blessed of hurried heart."
                Player.Cast AAATheSteed Player
            ElseIf (GameMonth == 6 && hasapprentice == 0)
                MessageBox "The Apprentice's Season is Sun's Height. Those born under the sign of the apprentice have a special affinity for magick of all kinds, but are more vulnerable to magick as well. Be blessed inquisitor."
                Player.Cast AAATheApprentice Player
            ElseIf (GameMonth == 7 && haswarrior == 0)
                MessageBox "The Warrior is the first Guardian Constellation and he protects his charges during their Seasons. The Warrior's own season is Last Seed when his Strength is needed for the harvest. Those born under the sign of the Warrior are skilled with weapons of all kinds, but prone to short tempers. Be blessed mighty one."
                Player.Cast AAATheWarrior Player
            ElseIf (GameMonth == 8 && haslady == 0)
                MessageBox "The Lady is one of the Warrior's Charges and her Season is Heartfire. Those born under the sign of the Lady are kind and tolerant. Be blessed with purity."
                Player.Cast AAATheLady Player
            ElseIf (GameMonth == 9 && hastower == 0)
                MessageBox "The Tower is one of the Thief's Charges and its Season is Frostfall. Those born under the sign of the Tower have a knack for finding gold and can open locks of all kinds. May luck find deep pockets."
                Player.Cast AAATheTower Player
            ElseIf (GameMonth == 10 && hasatronach == 0)
                MessageBox "The Atronach (often called the Golem) is one of the Mage's Charges. Its season is Sun's Dusk. Those born under this sign are natural sorcerers with deep reserves of magicka, but they cannot generate magicka of their own. Be blessed affine being."
                Player.Cast AAATheAtronach Player
            ElseIf (GameMonth == 11 && hasthief == 0)
                MessageBox "The Thief is the last Guardian Constellation, and her Season is the darkest month of Evening Star. Those born under the sign of the Thief are not typically thieves, though they take risks more often and only rarely come to harm. They will run out of luck eventually, however, and rarely live as long as those born under other signs. Be blessed nimble mind."
                Player.Cast AAATheThief Player
            EndIf

        EndIf

        Set state to 1 ; stay in this loop of blessings
    EndIf
End

Continuing

Move to the next part on questing to follow how this mod was upgraded into the Oblivion questing system to guide the player along the steps needed to become the one blessed by the stars.