The Simple Script
Okay. So let's set the stage. For our example, we'll be creating a script to take away a certain amount of money from the player depending on how many houses they own. We'll be using a few functions you might not know, so let's take a look at them now:
GetGoldAmount() - Checks how much gold the actor has. We'll be using this to do something if the player doesn't have enough gold.
QueryStat() - This allows you to check a lot of the miscellaneous stats in game. While this isn't useful for a lot of people, one of the stats that you can check is Houses Owned, so it will be useful for us.
Okay. Let's begin with a fairly simple script, just deducting the amount of gold from the player every week, if they have the proper amount of gold. I'll be attaching the script to a quest, simply because it should work pretty well for what we have in mind. If I've got a script that is constantly checking something, I usually want to attach it to a quest, since it's easier to manage. Before we begin, I'm going to note that you could change the "rent" for each house individually, but it's a bit more complex and I'd rather not get into it right now. Also, most of the variables could be substituted for simple numbers, but I prefer variables since they're more flexible.
The Script
Scriptname MyHouseRentScript Extends Quest
{Deducts a certain cost from the player depending on how many houses they own.}
Import Game; removes the Game. prefix from any functions on the Game script
Actor Property PlayerRef Auto; this should auto-fill. It's my preferred way to refer to the player
MiscObject Property Gold001 Auto; this should auto-fill
Int HouseRent; how much is the "rent" or pay for each house?
Int HousesOwned; how many houses does the player have?
Int TotalRent; this will be the total cost per week
Int PlayerGold; the amount of gold the player has
Event OnInit()
RegisterForSingleUpdateGameTime(168.0); this is 1 week in hours - 24 x 7
EndEvent
Event OnUpdateGameTime()
PlayerGold = PlayerREF.GetGoldAmount(); sets playergold to the amount of gold the player has at the end of the week
HousesOwned = QueryStat("Houses Owned")
HouseRent = 40; you could set this to whatever you want
TotalRent = HouseRent * HousesOwned; TotalRent becomes those two ints combined
If TotalRent <= PlayerGold; if the player has the same amount of gold or more than TotalRent
PlayerREF.RemoveItem(Gold001, TotalRent); remove the amount of gold TotalRent is
Else; if the player doesn't have enough gold
Debug.Messagebox("<Alias=Player>, you have failed to pay the weekly rent. Please go meet Alduin to recieve your punishment."); pop up this message
PlayerREF.Kill(); kill the playerEndIf
RegisterForSingleUpdateGameTime(168.0); creating a loop so it will calm this event in another week, and then call this function again within this event, etc.
EndEvent
Explanation
So that's the basic idea. Obviously, you could trade out the messageboxing and player-killing for something more suited to your tastes. In fact, you could switch out pretty much everything here, except for the OnInit and OnUpdateGameTime event, and the RegisterForUpdateGameTime. That's really all you need, though this example was a fairly common application of this type of script. Hopefully the example made sense to you, but we'll overview just in case.
First, you declare all your variables and properties - don't forget to fill those afterwards. Commented out (behind the colons) is an explanation of what most of those are. Then, we start the actual scripting. OnInit is an event that runs when the script initiates, which, if your quest was properly configured, would run when the script first loaded. By properly configured, I mean that the quest should have Run Once and Start Game Enabled checked in the Quest Data tab, though we may change this later.
So when our script is initiated, we want to begin the check. So we RegisterForUpdateGameTime, which is a command that begins to send an event (OnUpdateGameTime) every specified amount of hours. In our case, it's a week, which is why I used 168.0 since 24 x 7 = 168. However, you could use whatever number you wanted here. You could even use a changing variable. But we'll get to that later.
Then, we end the OnInit event and declare the OnUpdateGameTime event. This is the event that will be called every 168 in-game hours/every week. We set PlayerGold to the amount of gold the player has. If you're wondering why we didn't just declare what PlayerGold is equal to in the variables, this is why: If we had done so, then PlayerGold wouldn't equal the amount of gold the player had when the week ends. It would equal the amount of gold the player has when the script first initiates, which isn't what we want.
We set HouseRent, HousesOwned and TotalRent at the time we did for similar reasons. If we had a HouseRent that wasn't always the same, we would want it to be the current rent, not the house rent at the initialization of the script. Likewise, HousesOwned could change as the player acquires new houses, so we wouldn't want HousesOwned to equal the amount of Houses the player had at the beginning of the game. Since TotalRent is a combination of HouseRent and HousesOwned, the same idea applies (TotalRent is equal to the rent per house multiplied by the amount of houses).
Then, we check the value of TotalRent against PlayerGold. We use the <= operator, which checks if the right statement is less than or equal to the statement on the left. So if TotalRent is less than or equal to (not bigger) PlayerGold, then we remove gold from the player by the amount of TotalRent. However, if TotalRent is larger than PlayerGold, then the player clearly cannot afford the rent. So we give a pop-up messagebox to the player, and then kill them. If you're wondering what <Alias=Player> is, just check the link in the script.
Then the process repeats again the next week, until we unregister the constant checking. If I created a mod like this, I would want to provide an option to install the mod correctly. Since we use a RegisterForSingleUpdate function, and then call it again in the event it sends (OnUpdateGameTime), this creates and endless loop. However, it's far safer than the similar function RegisterForUpdate, which can ruin save games. So don't use it.
I Don't Want It Every Week!
Can I Change the Amount of Time Every Week? Or Something...
That was a badly phrased question, but yes. You'll want to use RegisterForSingleUpdateGameTime, but instead of 168, you'd use a variable. In OnUpdateGameTime, you would change the aforementioned variable as much as you want. Then, you would create another RegisterForSingleUpdateGameTime within the OnUpdateGameTime, again using the variable instead of 168. Finally, you would do the rest of the stuff we did within OnUpdateGameTime, and that's all you would need.
So for an example, we'll say I wanted to start out with a week, but then add an extra day to how long you'd need to wait every "rent day". The modified script would look like this:
The Script
Scriptname MyCoolerHouseRentScript Extends Quest
{Deducts a certain cost from the player depending on how many houses they own, and then increases the length between rent days.}
Import Game; removes the Game. prefix from any functions on the Game script
Actor Property PlayerRef Auto; this should auto-fill. It's my preferred way to refer to the player
MiscObject Property Gold001 Auto; this should auto-fill
Int HouseRent; how much is the "rent" or pay for each house?
Int HousesOwned; how many houses does the player have?
Int TotalRent; this will be the total cost per week
Int PlayerGold; the amount of gold the player has
Float RentWait = 168.0; how long between rent days? Starts out at a week.
Event OnInit()
RegisterForSingleUpdateGameTime(RentWait)
EndEvent
Event OnUpdateGameTime()
RentWait += 24.0; sets RentWait to RentWait + 24 (one more day)
PlayerGold = PlayerREF.GetGoldAmount(); sets playergold to the amount of gold the player has at the end of the week
HousesOwned = QueryStat("Houses Owned")
HouseRent = 40; you could set this to whatever you want
TotalRent = HouseRent * HousesOwned; TotalRent becomes those two ints combined
RegisterForSingleUpdateGameTime(RentWait)
If TotalRent <= PlayerGold); if the player has the same amount of gold or more than TotalRent
PlayerREF.RemoveItem(Gold001, TotalRent); remove the amount of gold TotalRent is
Else; if the player doesn't have enough gold
Debug.Messagebox("<Alias=Player>, you have failed to pay the weekly rent. Please go meet Alduin to recieve your punishment."); pop up this message
PlayerREF.Kill(); kill the player
EndEvent
Wait For Something Before Rent Starts
Scriptname MyDeedScript Extends ObjectReference
{Sets HouseRentQuest's stage when the deed has been taken by the player.}
Quest Property HouseRentQuest Auto; this would auto-fill in my case
Actor Property PlayerREF Auto; fastest way to refer to the player, should auto-fill
Event OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)
If akNewContainer == PlayerREF
HouseRentQuest.SetStage(10)
EndIf
EndEvent
"Now that I bought Breezehome, it looks like I'll have to start paying the rent for every house I own. Bummers."
Go over to the Scripts tab, and open up our first script. Change it to this:
Scriptname MyEvenCoolerHouseRentScript Extends Quest
{Deducts a certain cost from the player depending on how many houses they own and other stuff.}
Import Game; removes the Game. prefix from any functions on the Game script
Actor Property PlayerRef Auto; this should auto-fill. It's my preferred way to refer to the player
MiscObject Property Gold001 Auto; this should auto-fill
Int HouseRent; how much is the "rent" or pay for each house?
Int HousesOwned; how many houses does the player have?
Int TotalRent; this will be the total cost per week
Int PlayerGold; the amount of gold the player has
Event OnInit()
RegisterForUpdateGameTime(168.0); this is 1 week - 24 x 7
EndEvent
Event OnUpdateGameTime()
If GetOwningQuest().GetStage() != 10; if the quest that owns this script - HouseRentQuest - isn't at stage 10
; do nothing til you hear from me - comment if you got that music reference
Else; HouseRentQuest is at stage 10/started
PlayerGold = PlayerREF.GetGoldAmount(); sets playergold to the amount of gold the player has at the end of the week
HousesOwned = QueryStat("Houses Owned")
HouseRent = 40; you could set this to whatever you want
TotalRent = HouseRent * HousesOwned; TotalRent becomes those two ints combined
If TotalRent <= PlayerGold); if the player has the same amount of gold or more than TotalRent
PlayerREF.RemoveItem(Gold001, TotalRent); remove the amount of gold TotalRent is
Else; if the player doesn't have enough gold
Debug.Messagebox("<Alias=Player>, you have failed to pay the weekly rent. Please go meet Alduin to recieve your punishment."); pop up this message
PlayerREF.Kill(); kill the player
EndEvent
Hopefully this tutorial has been helpful to you. If you've got any questions, post below and I'll answer them to the best of my ability.