A Simple Day And Night SystemNote: There is no alpha blending or actual graphic change involved in this tutorial. This is just the actual time engine. You have to figure out the graphical aspect yourself So this is going to be a fairly simple, copy and paste tutorial. If you have any questions or problems doing the tutorial, feel free to ask
The system will work based on minutes, hours and days. I did not add in seconds because I always felt in-game time should be faster. This system will basically make every second count as a minute, so a complete 24 hour game cycle would last 24 minutes in real life.
Server SideOpen up modTypesOpen up modGlobalsOpen up modDatabaseNow we will make the saving and loading functions so that the date remains the same when we close the server. Add these subs at the bottom:
Code:
Public Sub SaveTime()
Dim FileName As String
FileName = App.Path & "\data\time.ini"
With GameTime
PutVar FileName, "TIME", "DAY", CStr(.Day)
PutVar FileName, "TIME", "MONTH", CStr(.Month)
PutVar FileName, "TIME", "HOUR", CStr(.Hour)
PutVar FileName, "TIME", "YEAR", CStr(.Year)
PutVar FileName, "TIME", "MINUTE", CStr(.Minute
End With
End Sub
Public Sub LoadTime()
Dim FileName As String
FileName = App.Path & "\data\time.ini"
With GameTime
If FileExist(FileName,True) Then
.Day = Val(GetVar(FileName, "TIME", "DAY"))
.Hour = Val(GetVar(FileName, "TIME", "HOUR"))
.Year = Val(GetVar(FileName, "TIME", "YEAR"))
.Month = Val(GetVar(FileName, "TIME", "MONTH"))
.Minute = Val(GetVar(FileName, "TIME", "MINUTE"))
Else
.Day = 1
.Month = 1
.Year = 1
SaveTime
End If
End With
End Sub
Now we have to actually use the saving and loading functions, so we will load the time when we are starting up the server, and save it when we are closing the server.
Open up modGeneralIn sub InitServer, under the following line :
Code:
Call SetStatus("Spawning map Npcs...")
Call SpawnAllMapNpcs
Add :
Code:
Call SetStatus("Loading time engine...")
Call LoadTime
In sub DestroyServer, under the following line:
Code:
Call SetStatus("Destroying System Tray...")
Call DestroySystemTray
Add :
Code:
Call SetStatus("Saving time values...")
Call SaveTime
We now have a working time engine which saves and loads the values. However, we have no actual code which makes the time change, so let's add that in now
Open up modServerLoopLook through the sub until you get to :
Code:
If Tick > tmr1000 Then
This is basically the code that will run every second. Since we want every second to be a minute, we're going to add in our code here. However, to keep the server loop clean, we're going to make our own sub for processing the time. So let's add our call to that sub. Under
Code:
' Handle shutting down server
If isShuttingDown Then
Call HandleShutdown
End If
Add :
Code:
' A second has passed, so process the time
Call ProcessTime
Open up modGameLogicAdd the following at the bottom. This is the sub that handles the time as well as a function the returns the last length of each month (For example February has 28 days, December has 12, etc..). Note : I did not add leap years, you can do that yourself if you really want to .
Code:
Public Sub ProcessTime()
With GameTime
.Minute = .Minute + 1
If .Minute >= 60 Then
.Hour = .Hour + 1
.Minute = 0
If .Hour >= 24 Then
.Day = .Day + 1
.Hour = 0
If .Day > GetMonthMax Then
.Month = .Month + 1
.Day = 1
If .Month > 12 Then
.Year = .Year + 1
.Month = 1
End If
End If
End If
End If
End With
End Sub
Public Function GetMonthMax() As Byte
Dim M As Byte
M = GameTime.Month
If M = 1 Or M = 3 Or M = 5 Or M = 7 Or M = 8 Or M = 10 Or M = 12 Then
GetMonthMax = 31
ElseIf M = 4 Or M = 6 Or M = 9 Or M = 11 Then
GetMonthMax = 30
ElseIf M = 2 Then
GetMonthMax = 28
End If
End Function
So there you have it. That is the base for the time engine. I will add extras as time goes on. Enjoy
Time Events:As an optional add-on, I will show you how to add time events. This add-on is entirely server side.
Open up modGameLogic
Replace the old ProcessTime sub with the following :
Code:
Public Sub ProcessTime()
With GameTime
.Minute = .Minute + 1
If .Minute >= 60 Then
.Hour = .Hour + 1
.Minute = 0
If .Hour >= 24 Then
.Day = .Day + 1
.Hour = 0
ChangeDayEvents
If .Day > GetMonthMax Then
.Month = .Month + 1
.Day = 1
ChangeMonthEvents
If .Month > 12 Then
.Year = .Year + 1
.Month = 1
ChangeYearEvents
End If
End If
End If
End If
End With
End Sub
Note : I did not add in a handler for events when the minute change, as I feel this would be called too often for no reason. You can add it in if you really want to have an event based on the minute, it's fairly simple, just add in a ChangeMinuteEvents call in the ProcessTime sub and make a ChangeMinuteEvents sub
Now, under that sub, add the following :
Code:
Public Sub ChangeDayEvents()
End Sub
Public Sub ChangeMonthEvents()
End Sub
Public Sub ChangeYearEvents()
End Sub
These are your event subs. They are called every time the day/month/year changes. For example, if you wanted to make an event that wished everyone happy new year, you would change the ChangeYearEvents sub to look like this :
Code:
Public Sub ChangeYearEvents()
Call GlobalMsg("All the villagers of Gameland are celebrating the new game year!", Cyan)
End Sub
You could easily add, for example, an automated event that would give every player that is online at the time experience. For another example, let's make an event for when it is Valentine's Day in-game. This would be in the ChangeDayEvents :
Code:
Public Sub ChangeDayEvents()
Dim I As Long
If GameTime.Day = 14 And GameTime.Month = 2 Then
For I = 1 To High_Index
If IsPlaying(I) Then
Call PlayerMsg(I, "An anonymous villager has sent you a chocolate as a present for Valentine's Day!", Red)
Call GiveItem(I, 1, 1)
End If
Next I
End If
End Sub
This would, for example, message every player and give them an item.
So as you can see, time events are incredibly versitile and can be used to make many automated events to add a sense of realism and time to your game.
/time Command:Adding a /time command to find out the time on the client side will be a fairly easy process as long as you follow my exact directions. This will give you a peek at creating and sending a packet.
On both the Client Side and Server Side, go to modEnumerationsLook for the following :
Code:
'The following enum member automatically stores the number of messages,
'since it is last. Any new messages must be placed above this entry.
CMSG_COUNT
End Enum
It will be in the 'enum' :
Code:
Public Enum ClientPackets
Right above the comment, add :
Code:
CTimeRequest
.
Make sure you add this to both the Client modEnumerations and the Server modEnumerations.
On the Client Side, open up modInputGo to sub HandleKeyPresses and look for the following code:
Code:
' Request stats
Case "/stats"
Set Buffer = New clsBuffer
Buffer.PreAllocate 2
Buffer.WriteInteger CGetStats
SendData Buffer.ToArray()
and under it, add :
Code:
' Request time
Case "/time"
Set Buffer = New clsBuffer
Buffer.PreAllocate 2
Buffer.WriteInteger CTimeRequest
SendData Buffer.ToArray()
Very good! Now for the
server side, open up
modHandleDataAt the bottom of the sub InitMessages, add the following line :
Code:
HandleDataSub(CTimeRequest) = GetAddress(AddressOf HandleTimeRequest)
And now add the following sub anywhere in that module :
Code:
' :::::::::::::::::::::::::
' :: Time Request packet ::
' :::::::::::::::::::::::::
Private Sub HandleTimeRequest(ByVal Index As Long, ByRef Data() As Byte, ByVal StartAddr As Long, ByVal ExtraVar As Long)
Dim TimeString As String, DateString As String, MinuteString As String
With GameTime
MinuteString = .Minute
If Len(MinuteString) = 1 Then MinuteString = "0" & MinuteString
If .Hour > 12 Then
TimeString = (.Hour - 12) & ":" & MinuteString & " PM"
ElseIf .Hour = 12 Then
TimeString = .Hour & ":" & MinuteString & " PM"
Else
TimeString = .Hour & ":" & MinuteString & " AM"
End If
DateString = Right(.Day, 1)
If DateString = 1 Then
DateString = .Day & "st"
ElseIf DateString = 2 Then
DateString = .Day & "nd"
ElseIf DateString = 3 Then
DateString = .Day & "rd"
Else
DateString = .Day & "th"
End If
Call PlayerMsg(Index, "The current game time is " & TimeString & ". The date is currently the " & DateString & " of " & MonthName(.Month) & ", Year " & .Year & ".", White)
End With
End Sub
Note that we use the built-in VB6 function MonthName to get the name of the Month. If you wish to have your own month names, or you modify the code to have a smaller amount of months with custom names, you will have to make a function that will give you the name of the month.
You now have a fully working /time function!