VbGORE1.0.14ClientSource

From VbGORE Visual Basic Online RPG Engine

This will contain the complete source of vbGORE for quick access usage.

Contents

vbGORE Version 1.0.14

This is the complete source of vbGORE Version 1.0.14. For quick access and code viewing.

GameClient Source

Forms

frmConnect

<vb> Option Explicit

Private Sub Form_KeyPress(KeyAscii As Integer) '***************************************************************** 'Call for the click button to be pressed if return is pressed 'More info: http://www.vbgore.com/GameClient.frmConnect.Form_KeyPress '*****************************************************************

   If KeyAscii = Asc(vbNewLine) Then ClickConnect

End Sub

Private Sub Form_Load() '***************************************************************** 'Load the values / graphics for the connect form 'More info: http://www.vbgore.com/GameClient.frmConnect.Form_Load '*****************************************************************

   'Set the text boxes to transparent
   SetPictureTextboxes Me.hwnd
   
   'Get the username/password
   NameTxt.Text = Var_Get(DataPath & "Game.ini", "INIT", "Name")
   PasswordTxt.Text = Var_Get(DataPath & "Game.ini", "INIT", "Password")
   SavePass = CBool(Val(Var_Get(DataPath & "Game.ini", "INIT", "SavePass")) * -1)
   
   'Set the SavePass image
   SavePass = Not SavePass 'Since the routine reverses, we reverse to reverse the reverse... trust me, it just works ;)
   SavePassImg_Click
   
   'Get the background
   Me.Picture = LoadPicture(App.Path & "\Grh\Connect.bmp")

End Sub

Private Sub ClickNew() '***************************************************************** 'Click the New Account button 'More info: http://www.vbgore.com/GameClient.frmConnect.ClickNew '*****************************************************************

   'Show frmNew and hide frmConnect
   frmNew.Visible = True
   frmNew.Show
   Me.Visible = False
   

End Sub

Private Sub ClickConnect() '***************************************************************** 'Click the Connect button 'More info: http://www.vbgore.com/GameClient.frmConnect.ClickConnect '*****************************************************************

   'Store the user name and password
   UserName = NameTxt.Text
   UserPassword = PasswordTxt.Text
   
   'Validate the user data, then start the connecting sequence
   If Game_CheckUserData Then
       SendNewChar = False
       InitSocket
   End If

End Sub

Private Sub ClickExit() '***************************************************************** 'Click the Exit button 'More info: http://www.vbgore.com/GameClient.frmConnect.ClickExit '*****************************************************************

   'Save the game ini (name and password)
   Var_Write DataPath & "Game.ini", "INIT", "Name", NameTxt.Text
   Var_Write DataPath & "Game.ini", "INIT", "SavePass", -CInt(SavePass)
   If Not SavePass Then
       Var_Write DataPath & "Game.ini", "INIT", "Password", ""
   Else
       Var_Write DataPath & "Game.ini", "INIT", "Password", PasswordTxt.Text
   End If
   'End program
   IsUnloading = 1
   

End Sub

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) '***************************************************************** 'Process clicking events 'More info: http://www.vbgore.com/GameClient.frmConnect.Form_MouseDown '*****************************************************************

   'New
   If Engine_Collision_Rect(X, Y, 1, 1, 217, 149, 96, 18) Then ClickNew
   
   'Connect
   If Engine_Collision_Rect(X, Y, 1, 1, 217, 127, 96, 18) Then ClickConnect
   'Exit
   If Engine_Collision_Rect(X, Y, 1, 1, 217, 171, 96, 18) Then ClickExit
   

End Sub

Private Sub Form_Unload(Cancel As Integer) '***************************************************************** 'Disable the picture textboxes 'More info: http://www.vbgore.com/GameClient.frmConnect.Form_Unload '*****************************************************************

   FreePictureTextboxes Me.hwnd

End Sub

Private Sub NameTxt_Change() '***************************************************************** 'Validates the name textbox upon changing 'More info: http://www.vbgore.com/GameClient.frmConnect.NameTxt_Change '*****************************************************************

   'Make sure the string is legal
   If Len(NameTxt.Text) > 0 Then
       If Game_LegalString(NameTxt.Text) = False Then
           NameTxt.Text = Left$(NameTxt.Text, Len(NameTxt.Text) - 1)
           NameTxt.SelStart = Len(NameTxt.Text)
       End If
   End If

End Sub

Private Sub NameTxt_KeyPress(KeyAscii As Integer) '***************************************************************** 'Connect when return is pressed 'More info: http://www.vbgore.com/GameClient.frmConnect.NameTxt_KeyPress '*****************************************************************

   If KeyAscii = Asc(vbNewLine) Then
       KeyAscii = 0
       ClickConnect
   End If

End Sub

Private Sub PasswordTxt_Change() '***************************************************************** 'Validates the password textbox upon changing 'More info: http://www.vbgore.com/GameClient.frmConnect.PasswordTxt_Change '*****************************************************************

   If Len(PasswordTxt.Text) > 0 Then
       If Game_LegalString(PasswordTxt.Text) = False Then
           PasswordTxt.Text = Left$(PasswordTxt.Text, Len(PasswordTxt.Text) - 1)
           PasswordTxt.SelStart = Len(PasswordTxt.Text)
       End If
   End If

End Sub

Private Sub PasswordTxt_KeyPress(KeyAscii As Integer) '***************************************************************** 'Connect when return is pressed 'More info: http://www.vbgore.com/GameClient.frmConnect.PasswordTxt_KeyPress '*****************************************************************

   If KeyAscii = Asc(vbNewLine) Then
       KeyAscii = 0
       ClickConnect
   End If

End Sub

Private Sub SavePassImg_Click() '***************************************************************** 'Hide or show the Save Password image and store the value 'More info: http://www.vbgore.com/GameClient.frmConnect.SavePassImg_Click '*****************************************************************

   'Change the value
   SavePass = Not SavePass
   
   'Display the image or remove it
   If SavePass Then
       SavePassImg.Picture = LoadPicture(GrhPath & "Check.gif")
   Else
       Set SavePassImg.Picture = Nothing
   End If

End Sub </vb>

frmMain

<vb>

Option Explicit

Implements DirectXEvent8

Private Declare Function GetCursorPos Lib "user32.dll" (ByRef lpPoint As POINTAPI) As Long

Private Sub DirectXEvent8_DXCallback(ByVal EventID As Long) '***************************************************************** 'Handles mouse device events (movement, clicking, mouse wheel scrolling, etc) 'More info: http://www.vbgore.com/GameClient.frmMain.DirectXEvent8_DXCallback '***************************************************************** Dim DevData(1 To 50) As DIDEVICEOBJECTDATA Dim NumEvents As Long Dim LoopC As Long Dim Moved As Byte Dim OldMousePos As POINTAPI

   On Error GoTo ErrOut
   'Check if message is for us
   If EventID <> MouseEvent Then Exit Sub
   If GetActiveWindow = 0 Then Exit Sub
   'Retrieve data
   NumEvents = DIDevice.GetDeviceData(DevData, DIGDD_DEFAULT)
   'Loop through data
   For LoopC = 1 To NumEvents
       Select Case DevData(LoopC).lOfs
       'Move on X axis
       Case DIMOFS_X
           If Windowed Then
               OldMousePos = MousePos
               GetCursorPos MousePos
               MousePos.X = MousePos.X - (Me.Left \ Screen.TwipsPerPixelX)
               MousePos.Y = MousePos.Y - (Me.Top \ Screen.TwipsPerPixelY)
               MousePosAdd.X = -(OldMousePos.X - MousePos.X)
               MousePosAdd.Y = -(OldMousePos.Y - MousePos.Y)
           Else
               MousePosAdd.X = (DevData(LoopC).lData * MouseSpeed)
               MousePos.X = MousePos.X + MousePosAdd.X
               If MousePos.X < 0 Then MousePos.X = 0
               If MousePos.X > frmMain.ScaleWidth Then MousePos.X = frmMain.ScaleWidth
           End If
           Moved = 1
       'Move on Y axis
       Case DIMOFS_Y
           If Windowed Then
               OldMousePos = MousePos
               GetCursorPos MousePos
               MousePos.X = MousePos.X - (Me.Left \ Screen.TwipsPerPixelX)
               MousePos.Y = MousePos.Y - (Me.Top \ Screen.TwipsPerPixelY)
               MousePosAdd.X = -(OldMousePos.X - MousePos.X)
               MousePosAdd.Y = -(OldMousePos.Y - MousePos.Y)
           Else
               MousePosAdd.Y = (DevData(LoopC).lData * MouseSpeed)
               MousePos.Y = MousePos.Y + MousePosAdd.Y
               If MousePos.Y < 0 Then MousePos.Y = 0
               If MousePos.Y > ScreenHeight Then MousePos.Y = ScreenHeight
           End If
           Moved = 1
           
       'Mouse wheel is scrolled
       Case DIMOFS_Z
           
           'Scroll the chat buffer if the cursor is over the chat buffer window
           If ShowGameWindow(ChatWindow) Then
               If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, GameWindow.ChatWindow.Screen.X, GameWindow.ChatWindow.Screen.Y, GameWindow.ChatWindow.Screen.Width, GameWindow.ChatWindow.Screen.Height) Then
                   If DevData(LoopC).lData > 0 Then
                       ChatBufferChunk = ChatBufferChunk + 0.25
                   ElseIf DevData(LoopC).lData < 0 Then
                       ChatBufferChunk = ChatBufferChunk - 0.25
                   End If
                   Engine_UpdateChatArray
                   GoTo NextLoopC
               End If
           End If
           
           'Scroll the zoom if the buffer didn't scroll
           If DevData(LoopC).lData > 0 Then
               ZoomLevel = ZoomLevel + (ElapsedTime * 0.001)
               If ZoomLevel > MaxZoomLevel Then ZoomLevel = MaxZoomLevel
           ElseIf DevData(LoopC).lData < 0 Then
               ZoomLevel = ZoomLevel - (ElapsedTime * 0.001)
               If ZoomLevel < 0 Then ZoomLevel = 0
           End If
       'Left button pressed
       Case DIMOFS_BUTTON0
           If DevData(LoopC).lData = 0 Then
               MouseLeftDown = 0
               SelGameWindow = 0
           Else
               If MouseLeftDown = 0 Then   'Clicked down
                   MouseLeftDown = 1
                   Input_Mouse_LeftClick
               End If
           End If
       'Right button pressed
       Case DIMOFS_BUTTON1
           If DevData(LoopC).lData = 0 Then
               MouseRightDown = 0
               Input_Mouse_RightRelease
           Else
               If MouseRightDown = 0 Then  'Clicked down
                   MouseRightDown = 1
                   Input_Mouse_RightClick
               End If
           End If
       End Select
       'Update movement
       If Moved Then
           Input_Mouse_Move
           'Reset move variables
           Moved = 0
           MousePosAdd.X = 0
           MousePosAdd.Y = 0
       End If
       

NextLoopC:

   Next LoopC

ErrOut:

End Sub

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) '***************************************************************** 'Forwards KeyDown events to the Input_Keys_Down sub 'More info: http://www.vbgore.com/GameClient.frmMain.Form_KeyDown '*****************************************************************

   Input_Keys_Down KeyCode
   KeyCode = 0
   Shift = 0

End Sub

Private Sub Form_KeyPress(KeyAscii As Integer) '***************************************************************** 'Forwards KeyPress events to the Input_Keys_Press sub 'More info: http://www.vbgore.com/GameClient.frmMain.Form_KeyPress '*****************************************************************

   Input_Keys_Press KeyAscii
   KeyAscii = 0

End Sub

Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer) '***************************************************************** 'Clears the KeyUp keycode and shift values 'More info: http://www.vbgore.com/GameClient.frmMain.Form_KeyUp '*****************************************************************

   KeyCode = 0
   Shift = 0

End Sub

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) '***************************************************************** 'Regain focus to Direct Input mouse in case the device is lost 'More info: http://www.vbgore.com/GameClient.frmMain.Form_MouseDown '*****************************************************************

   On Error Resume Next
       DIDevice.Acquire
       MousePos.X = X
       MousePos.Y = Y
   On Error GoTo 0
   

End Sub

Private Sub Form_Resize() '***************************************************************** 'Regain focus to Direct Input mouse in case the device is lost 'Form_Resize is called when the form returns from a minimized state, which 'is why we have this here 'More info: http://www.vbgore.com/GameClient.frmMain.Form_Resize '*****************************************************************

   On Error Resume Next
       If Not DIDevice Is Nothing Then
           If Not Windowed Then DIDevice.Acquire
       End If
   On Error GoTo 0
   

End Sub

Private Sub ShutdownTimer_Timer() '***************************************************************** 'Loops through the shutdown sequence to close up the program. A loop 'is made because it can sometimes take multiple tries to successfully 'close down GOREsock. 'More info: http://www.vbgore.com/GameClient.frmMain.ShutdownTimer_Timer '***************************************************************** Static FailedUnloads As Long

   On Error Resume Next    'Who cares about an error if we are closing down
   'Quit the client - we must user a timer since DoEvents wont work (since we're not multithreaded)
   
   'Close down the socket
   If FailedUnloads > 5 Or frmMain.GOREsock.ShutDown <> soxERROR Then
       frmMain.GOREsock.UnHook
       'Unload the engine
       Engine_Init_UnloadTileEngine
       
       'Unload the forms
       Engine_UnloadAllForms
       
       'Unload everything else
       End
   Else
       
       'If the socket is making an error on the shutdown sequence for more than a second, just unload anyways
       FailedUnloads = FailedUnloads + 1
       
   End If

End Sub

Private Sub GOREsock_OnDataArrival(inSox As Long, inData() As Byte) '********************************************* 'Retrieve the CommandIDs and send to corresponding data handler 'More info: http://www.vbgore.com/GameClient.frmMain.GOREsock_OnDataArrival '********************************************* Dim rBuf As DataBuffer Dim CommandID As Byte Dim BufUBound As Long

   'Set up the data buffer
   Set rBuf = New DataBuffer
   rBuf.Set_Buffer inData
   BufUBound = UBound(inData)
   
   'Packet arrived!
   LastServerPacketTime = timeGetTime
   
   'Uncomment this to see packets going into the client
   'Dim i As Long
   'Dim s As String
   'For i = LBound(inData) To UBound(inData)
   '    If inData(i) >= 100 Then
   '        s = s & inData(i) & " "
   '    ElseIf inData(i) >= 10 Then
   '        s = s & "0" & inData(i) & " "
   '    Else
   '        s = s & "00" & inData(i) & " "
   '    End If
   'Next i
   'Debug.Print s
   
   Do
       'Get the Command ID
       CommandID = rBuf.Get_Byte
       'Make the appropriate call based on the Command ID
       With DataCode
           Select Case CommandID
           Case 0 'This often means there was an offset problem in the packet, adding too many empty values
           Case .Comm_Talk: Data_Comm_Talk rBuf
           Case .Map_LoadMap: Data_Map_LoadMap rBuf
           Case .Map_SendName:  Data_Map_SendName rBuf
           Case .Server_ChangeChar: Data_Server_ChangeChar rBuf
           Case .Server_ChangeCharType: Data_Server_ChangeCharType rBuf
           Case .Server_CharHP: Data_Server_CharHP rBuf
           Case .Server_CharMP: Data_Server_CharMP rBuf
           Case .Server_Connect: Data_Server_Connect
           Case .Server_Disconnect: Data_Server_Disconnect
           Case .Server_EraseChar: Data_Server_EraseChar rBuf
           Case .Server_EraseObject: Data_Server_EraseObject rBuf
           Case .Server_IconBlessed: Data_Server_IconBlessed rBuf
           Case .Server_IconCursed: Data_Server_IconCursed rBuf
           Case .Server_IconIronSkin: Data_Server_IconIronSkin rBuf
           Case .Server_IconProtected: Data_Server_IconProtected rBuf
           Case .Server_IconStrengthened: Data_Server_IconStrengthened rBuf
           Case .Server_IconWarCursed: Data_Server_IconWarCursed rBuf
           Case .Server_IconSpellExhaustion: Data_Server_IconSpellExhaustion rBuf
           'Case .Server_KeepAlive: Data_Server_KeepAlive - Not needed since it only confirms the connection is alive
           Case .Server_MailBox: Data_Server_Mailbox rBuf
           Case .Server_MailItemRemove: Data_Server_MailItemRemove rBuf
           Case .Server_MailMessage: Data_Server_MailMessage rBuf
           Case .Server_MailObjUpdate: Data_Server_MailObjUpdate rBuf
           Case .Server_MakeChar: Data_Server_MakeChar rBuf
           Case .Server_MakeCharCached: Data_Server_MakeCharCached rBuf
           Case .Server_MakeEffect: Data_Server_MakeEffect rBuf
           Case .Server_MakeSlash: Data_Server_MakeSlash rBuf
           Case .Server_MakeObject: Data_Server_MakeObject rBuf
           Case .Server_MakeProjectile: Data_Server_MakeProjectile rBuf
           Case .Server_Message: Data_Server_Message rBuf
           Case .Server_MoveChar: Data_Server_MoveChar rBuf
           Case .Server_PlaySound: Data_Server_PlaySound rBuf
           Case .Server_PlaySound3D: Data_Server_PlaySound3D rBuf
           Case .Server_SendQuestInfo: Data_Server_SendQuestInfo rBuf
           Case .Server_SetCharDamage: Data_Server_SetCharDamage rBuf
           Case .Server_SetCharSpeed: Data_Server_SetCharSpeed rBuf
           Case .Server_SetUserPosition: Data_Server_SetUserPosition rBuf
           Case .Server_UserCharIndex: Data_Server_UserCharIndex rBuf
           Case .User_Attack: Data_User_Attack rBuf
           Case .User_Bank_Open: Data_User_Bank_Open rBuf
           Case .User_Bank_UpdateSlot: Data_User_Bank_UpdateSlot rBuf
           Case .User_BaseStat: Data_User_BaseStat rBuf
           Case .User_Blink: Data_User_Blink rBuf
           Case .User_CastSkill: Data_User_CastSkill rBuf
           Case .User_ChangeServer: Data_User_ChangeServer rBuf
           Case .User_Emote: Data_User_Emote rBuf
           Case .User_KnownSkills: Data_User_KnownSkills rBuf
           Case .User_LookLeft: Data_User_LookLeft rBuf
           Case .User_LookRight: Data_User_LookLeft rBuf
           Case .User_ModStat: Data_User_ModStat rBuf
           Case .User_Rotate: Data_User_Rotate rBuf
           Case .User_SetInventorySlot: Data_User_SetInventorySlot rBuf
           Case .User_SetWeaponRange: Data_User_SetWeaponRange rBuf
           Case .User_Target: Data_User_Target rBuf
           Case .User_Trade_Accept: Data_User_Trade_Accept rBuf
           Case .User_Trade_Cancel: Data_User_Trade_Cancel
           Case .User_Trade_StartNPCTrade: Data_User_Trade_StartNPCTrade rBuf
           Case .User_Trade_Trade: Data_User_Trade_Trade rBuf
           Case .User_Trade_UpdateTrade: Data_User_Trade_UpdateTrade rBuf
           Case .Combo_ProjectileSoundRotateDamage: Data_Combo_ProjectileSoundRotateDamage rBuf
           Case .Combo_SlashSoundRotateDamage: Data_Combo_SlashSoundRotateDamage rBuf
           Case .Combo_SoundRotateDamage: Data_Combo_SoundRotateDamage rBuf
           Case Else
               rBuf.Overflow  'Something went wrong or we hit the end, either way, RUN!!!!
           End Select
       End With
       'Exit when the buffer runs out
       If rBuf.Get_ReadPos > BufUBound Then Exit Do
   Loop
   
   Set rBuf = Nothing

End Sub

Private Sub GOREsock_OnConnecting(inSox As Long) '********************************************* 'When the connection is made to the server, this will send 'the login packet if the user has not already logged in 'More info: http://www.vbgore.com/GameClient.frmMain.GOREsock_OnConnecting '*********************************************

   If SocketOpen = 0 Then
       
       Sleep 50
       DoEvents
       
       'Pre-saved character
       If SendNewChar = False Then
           sndBuf.Put_Byte DataCode.User_Login
           sndBuf.Put_String UserName
           sndBuf.Put_String UserPassword
       Else
           'New character
           sndBuf.Put_Byte DataCode.User_NewLogin
           sndBuf.Put_String UserName
           sndBuf.Put_String UserPassword
           sndBuf.Put_Integer UserHead
           sndBuf.Put_Integer UserBody
           sndBuf.Put_Byte UserClass
       End If
   
       'Save Game.ini
       If Not SavePass Then UserPassword = vbNullString
       Var_Write DataPath & "Game.ini", "INIT", "Name", UserName
       Var_Write DataPath & "Game.ini", "INIT", "Password", UserPassword
       
       'Send the data
       Data_Send
       DoEvents
   
   End If
   

End Sub </vb>

frmNew

<vb> Option Explicit

Private Sub ClickCancel() '***************************************************************** 'Hides frmNew and displays frmConnect 'More info: http://www.vbgore.com/GameClient.frmNew.ClickCancel '*****************************************************************

   'Show the connect screen
   frmConnect.Visible = True
   
   'Hide this screen
   Me.Visible = False

End Sub

Private Sub ClickCreate() '***************************************************************** 'Sends the packet to the server requesting to create a new user 'More info: http://www.vbgore.com/GameClient.frmNew.ClickCancel '*****************************************************************

   'Set the variables
   UserName = NameTxt.Text
   UserPassword = PasswordTxt.Text
   UserBody = BodyCmb.ListIndex
   UserHead = HeadCmb.ListIndex
   UserClass = ClassCmb.ListIndex
   
   'Convert the body by listbox index to the body number
   Select Case UserBody
       Case 0: UserBody = 1
       Case Else: UserBody = 1
   End Select
   
   'Convert the head by listbox index to the head number
   Select Case UserHead
       Case 0: UserHead = 1
       Case Else: UserHead = 1
   End Select
   
   'Convert the class by listbox index to the class number
   Select Case UserClass
       Case 0: UserClass = ClassID.Warrior
       Case 1: UserClass = ClassID.Mage
       Case 2: UserClass = ClassID.Rogue
       Case Else: UserClass = ClassID.Warrior
   End Select
   
   'Connect
   If Game_CheckUserData Then
       SendNewChar = True
       InitSocket
   End If

End Sub

Private Sub Form_Unload(Cancel As Integer) '***************************************************************** 'Unloads the picture textboxes 'More info: http://www.vbgore.com/GameClient.frmNew.Form_Unload '*****************************************************************

   FreePictureTextboxes Me.hwnd

End Sub

Private Sub NameTxt_KeyPress(KeyAscii As Integer) '***************************************************************** 'Create new character when return is pressed 'More info: http://www.vbgore.com/GameClient.frmNew.NameTxt_KeyPress '*****************************************************************

   If KeyAscii = Asc(vbNewLine) Then
       KeyAscii = 0
       ClickCreate
   End If

End Sub

Private Sub PasswordTxt_KeyPress(KeyAscii As Integer) '***************************************************************** 'Create new character when return is pressed 'More info: http://www.vbgore.com/GameClient.frmNew.PasswordTxt_KeyPress '*****************************************************************

   If KeyAscii = Asc(vbNewLine) Then
       KeyAscii = 0
       ClickCreate
   End If

End Sub

Private Sub Form_KeyPress(KeyAscii As Integer) '***************************************************************** 'Create new character when return is pressed 'More info: http://www.vbgore.com/GameClient.frmNew.Form_KeyPress '*****************************************************************

   If KeyAscii = Asc(vbNewLine) Then ClickCreate

End Sub

Private Sub Form_Load() '***************************************************************** 'Loads up the values for frmNew and creates the listbox values and pictures 'More info: http://www.vbgore.com/GameClient.frmNew.Form_Load '*****************************************************************

   'Set the background picture
   Me.Picture = LoadPicture(GrhPath & "New.bmp")
   'Set the text boxes to transparent
   SetPictureTextboxes Me.hwnd
   'Load up the head, body and class values you can select
   'For the head and body, to add more, you have to edit it accordingly in the server
   ' under User_ConnectNew on this line:
   '
   '    'Check for a valid body and head
   '    If Head <> 1 Then Exit Sub
   '    If Body <> 1 Then Exit Sub
   '
   'Or something similar. It will appear at the top of the routine, and is pretty much the
   ' only thing that makes reference to the body or head in that sub, so it is easy to find.
   'Failure to do this will make the server reject the character. This is to prevent people from
   ' editing the packets to make their body or head whatever they want it to be.
   
   'Create the heads
   With HeadCmb
       .Clear
       .AddItem "Head 1", 0
       .ListIndex = 0
   End With
   
   'Create the bodies
   With BodyCmb
       .Clear
       .AddItem "Body 1", 0
       .ListIndex = 0
   End With
   
   'Create the classes
   With ClassCmb
       .Clear
       .AddItem "Warrior", 0
       .AddItem "Mage", 1
       .AddItem "Rogue", 2
       .ListIndex = 0
   End With
   

End Sub

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) '***************************************************************** 'Check if the buttons on the form were clicked 'More info: http://www.vbgore.com/GameClient.frmNew.Form_MouseDown '*****************************************************************

   'Click on "Create"
   If Engine_Collision_Rect(X, Y, 1, 1, 5, 189, 66, 15) Then ClickCreate
   
   'Click on "Cancel"
   If Engine_Collision_Rect(X, Y, 1, 1, 118, 190, 66, 15) Then ClickCancel

End Sub

</vb>

Modules

AllFilesInFolder

<vb> Option Explicit

Private Sub AddItem2Array1D(ByRef VarArray As Variant, ByVal VarValue As Variant)

Dim i As Long Dim iVarType As Integer

   iVarType = VarType(VarArray) - 8192
   i = UBound(VarArray)
   Select Case iVarType
   Case vbInteger, vbLong, vbSingle, vbDouble, vbCurrency, vbDecimal, vbByte
       If VarArray(0) = 0 Then
           i = 0
       Else
           i = i + 1
       End If
   Case vbDate
       If VarArray(0) = "00:00:00" Then
           i = 0
       Else
           i = i + 1
       End If
   Case vbString
       If VarArray(0) = vbNullString Then
           i = 0
       Else
           i = i + 1
       End If
   Case vbBoolean
       If VarArray(0) = False Then
           i = 0
       Else
           i = i + 1
       End If
   Case Else
   End Select
   ReDim Preserve VarArray(i)
   VarArray(i) = VarValue

End Sub

Public Function AllFilesInFolders(ByVal sFolderPath As String, Optional bWithSubFolders As Boolean = False) As String()

Dim sTemp As String Dim sDirIn As String Dim i As Integer, j As Integer

   ReDim sFilelist(0) As String, sSubFolderList(0) As String, sToProcessFolderList(0) As String
   sDirIn = sFolderPath
   If Not (Right$(sDirIn, 1) = "\") Then sDirIn = sDirIn & "\"
   
   On Error Resume Next
   
       sTemp = Dir$(sDirIn & "*.*")
       Do While LenB(sTemp) <> 0
           AddItem2Array1D sFilelist(), sDirIn & sTemp
           sTemp = Dir
       Loop
       If bWithSubFolders Then
           sTemp = Dir$(sDirIn & "*.*", vbDirectory)
           Do While LenB(sTemp) <> 0
               If sTemp <> "." Then
                   If sTemp <> ".." Then
                       If (GetAttr(sDirIn & sTemp) And vbDirectory) = vbDirectory Then AddItem2Array1D sToProcessFolderList, sDirIn & sTemp
                   End If
               End If
               sTemp = Dir
               
           Loop
           If UBound(sToProcessFolderList) > 0 Or UBound(sToProcessFolderList) = 0 And LenB(sToProcessFolderList(0)) <> 0 Then
               For i = 0 To UBound(sToProcessFolderList)
                   sSubFolderList = AllFilesInFolders(sToProcessFolderList(i), bWithSubFolders)
                   If UBound(sSubFolderList) > 0 Or UBound(sSubFolderList) = 0 And LenB(sSubFolderList(0)) <> 0 Then
                       For j = 0 To UBound(sSubFolderList)
                           AddItem2Array1D sFilelist(), sSubFolderList(j)
                       Next
                   End If
               Next
           End If
       End If
       AllFilesInFolders = sFilelist
       
   On Error GoTo 0

End Function </vb>

Compressions

<vb> Option Explicit

Public Enum CompressMethods

   RLE = 1
   RLE_Loop = 2
   LZMA = 3
   PAQ8l = 4
   Deflate64 = 5
   MonkeyAudio = 6     '*.wav only

End Enum

  1. If False Then

Private RLE, RLE_Loop, LZMA, PAQ8l, Deflate64, MonkeyAudio

  1. End If

'Value between 0 and 9, higher requiring more RAM/CPU but better compression 'Keep in mind decompressing requires a lot of RAM, too, so don't go higher than 7 Private Const PAQ8l_Level As Byte = 6

Private CompressArray() As Byte Private OutStream() As Byte Private OutPos As Long Private CntPos As Long

Private Type STARTUPINFO

   cb As Long
   lpReserved As String
   lpDesktop As String
   lpTitle As String
   dwX As Long
   dwY As Long
   dwXSize As Long
   dwYSize As Long
   dwXCountChars As Long
   dwYCountChars As Long
   dwFillAttribute As Long
   dwFlags As Long
   wShowWindow As Integer
   cbReserved2 As Integer
   lpReserved2 As Long
   hStdInput As Long
   hStdOutput As Long
   hStdError As Long

End Type

Private Type PROCESS_INFORMATION

   hProcess As Long
   hThread As Long
   dwProcessID As Long
   dwThreadID As Long

End Type

Private Const QUOTE As String * 1 = """"

Private Declare Function CreateProcessA Lib "kernel32" (ByVal lpApplicationname As Long, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Private Declare Sub CopyMem Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Private Function GetRandomFileName() As String '***************************************************************** 'Generates a random file name 'More info: http://www.vbgore.com/CommonCode.Compressions.GetRandomFileName '***************************************************************** Dim i As Long

   For i = 1 To (3 + Int(Rnd * 10))
       GetRandomFileName = GetRandomFileName & GetRandomChar
   Next i
   

End Function

Private Function GetRandomChar() As String '***************************************************************** 'Generates a random character of either 0-9, a-z or A-Z 'More info: http://www.vbgore.com/CommonCode.Compressions.GetRandomChar '***************************************************************** Dim i As Long

   i = Int(Rnd * 3)
   Select Case i
   
       '0 to 9
       Case 0
           GetRandomChar = Str$(Int(Rnd * 9))
           
       'A to Z
       Case 1
           GetRandomChar = Chr$(65 + Int(Rnd * 26))
           
       'a to z
       Case 2
           GetRandomChar = Chr$(97 + Int(Rnd * 26))
           
   End Select

End Function

Private Sub CommandLine(ByVal CommandLineString As String) '***************************************************************** 'Runs a command line string and waits for the process to finish 'More info: http://www.vbgore.com/CommonCode.Compressions.CommandLine '***************************************************************** Dim Start As STARTUPINFO Dim Proc As PROCESS_INFORMATION

   Start.dwFlags = &H1
   Start.wShowWindow = 0
   CreateProcessA 0&, CommandLineString, 0&, 0&, 1&, &H20&, 0&, 0&, Start, Proc
   Do While WaitForSingleObject(Proc.hProcess, 0) = 258
       DoEvents
       Sleep 1
   Loop

End Sub

Private Sub SaveBytes(ByRef Bytes() As Byte, ByVal File As String) '***************************************************************** 'Saves a byte array to a file 'More info: http://www.vbgore.com/CommonCode.Compressions.SaveBytes '***************************************************************** Dim FileNum As Byte

   'Delete the file if it already exists
   If Dir$(File, vbNormal) <> vbNullString Then Kill File
   
   'Find a free file number
   FileNum = FreeFile
   
   'Write the bytes to the file
   Open File For Binary Access Write As #FileNum
       Put #FileNum, , Bytes()
   Close #FileNum

End Sub

Private Sub LoadBytes(ByRef Bytes() As Byte, ByVal File As String) '***************************************************************** 'Loads a byte array from a file 'More info: http://www.vbgore.com/CommonCode.Compressions.LoadBytes '***************************************************************** Dim FileNum As Byte

   'Find a free file number
   FileNum = FreeFile
   
   Open File For Binary Access Read As #FileNum
              
       'Make sure there is data in the file
       If LOF(FileNum) > 0 Then
       
           'Resize the Bytes array to fit all of the bytes in the file
           'then grab them
           ReDim Bytes(0 To LOF(FileNum) - 1)
           Get #FileNum, , Bytes()
           
       End If
       
   Close #FileNum

End Sub

Public Sub Compression_Compress_PAQ8l(ByteArray() As Byte) '***************************************************************** 'Compresses a byte array with PAQ8l compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_Compress_PAQ8l '***************************************************************** Dim FileName As String

   FileName = GetRandomFileName
   SaveBytes ByteArray(), App.Path & "\" & FileName & ".bin"
   CommandLine DataPath & "paq8l.exe -" & PAQ8l_Level & " " & QUOTE & App.Path & "\" & FileName & ".bin" & QUOTE
   If Dir$(App.Path & "\" & FileName & ".bin.paq8l") <> vbNullString Then
       LoadBytes ByteArray(), App.Path & "\" & FileName & ".bin.paq8l"
       Kill App.Path & "\" & FileName & ".bin.paq8l"
   End If
   Kill App.Path & "\" & FileName & ".bin"

End Sub

Public Sub Compression_DeCompress_PAQ8l(ByteArray() As Byte) '***************************************************************** 'Decompresses a byte array with PAQ8l compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_DeCompress_PAQ8l '***************************************************************** Dim FileName As String

   FileName = GetRandomFileName
   SaveBytes ByteArray(), App.Path & "\" & FileName & ".bin.paq8l"
   CommandLine DataPath & "paq8l.exe -d " & QUOTE & App.Path & "\" & FileName & ".bin.paq8l" & QUOTE
   If Dir$(App.Path & "\" & FileName & ".bin") <> vbNullString Then
       LoadBytes ByteArray(), App.Path & "\" & FileName & ".bin"
       Kill App.Path & "\" & FileName & ".bin"
   End If
   Kill App.Path & "\" & FileName & ".bin.paq8l"

End Sub

Private Sub Compression_Add_CharToArray(ToArray() As Byte, ToPos As Long, ByVal Char As Byte) '***************************************************************** 'Adds a char (byte) to an array at a specific location (used for RLE compression) 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_Add_CharToArray '*****************************************************************

   If ToPos > UBound(ToArray) Then
       ReDim Preserve ToArray(ToPos + 500)
   End If
   ToArray(ToPos) = Char
   ToPos = ToPos + 1

End Sub

Public Sub Compression_Compress(SrcFile As String, DestFile As String, Compression As CompressMethods) '***************************************************************** 'A wrapper for easy file-based compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_Compress '***************************************************************** Dim Dummy As Boolean

   If Compression_File_Load(SrcFile) = 0 Then Exit Sub
   Select Case Compression
       Case RLE
           Compression_Compress_RLE CompressArray(), Dummy
       Case RLE_Loop
           Compression_Compress_RLELoop CompressArray()
       Case LZMA
           Compression_Compress_LZMA CompressArray()
       Case PAQ8l
           Compression_Compress_PAQ8l CompressArray()
       Case Deflate64
           Compression_Compress_Deflate64 CompressArray()
   End Select
   Compression_File_Save DestFile
   Erase CompressArray

End Sub

Public Sub Compression_Compress_LZMA(ByteArray() As Byte) '***************************************************************** 'Compresses a byte array with LZMA compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_Compress_LZMA '***************************************************************** Dim FileName As String

   FileName = GetRandomFileName
   SaveBytes ByteArray(), App.Path & "\" & FileName & ".bin"
   CommandLine DataPath & "7za.exe a -t7z " & QUOTE & App.Path & "\" & FileName & ".bin.7z" & QUOTE & " -aoa " & QUOTE & App.Path & "\" & FileName & ".bin" & QUOTE & " -mx9 -m0=LZMA:d80m:fb273:lc5:pb1:mc10000"
   If Dir$(App.Path & "\" & FileName & ".bin.7z") <> vbNullString Then
       LoadBytes ByteArray(), App.Path & "\" & FileName & ".bin.7z"
       Kill App.Path & "\" & FileName & ".bin.7z"
   End If
   Kill App.Path & "\" & FileName & ".bin"

End Sub

Public Sub Compression_Compress_MonkeyAudio(ByteArray() As Byte) '***************************************************************** 'Compresses a byte array with MonkeyAudio (.wav files only) compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_Compress_MonkeyAudio '***************************************************************** Dim FileName As String

   '*.wav only
   FileName = GetRandomFileName
   SaveBytes ByteArray(), App.Path & "\" & FileName & ".wav"
   CommandLine DataPath & "mac.exe " & QUOTE & App.Path & "\" & FileName & ".wav" & QUOTE & " " & QUOTE & App.Path & "\" & FileName & ".wav.ape" & QUOTE & " -c5000"
   If Dir$(App.Path & "\" & FileName & ".wav.ape") <> vbNullString Then
       LoadBytes ByteArray(), App.Path & "\" & FileName & ".wav.ape"
       Kill App.Path & "\" & FileName & ".wav.ape"
   End If
   Kill App.Path & "\" & FileName & ".wav"

End Sub

Public Sub Compression_DeCompress_MonkeyAudio(ByteArray() As Byte) '***************************************************************** 'Decompresses a byte array with MonkeyAudio (.wav files only) compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_DeCompress_MonkeyAudio '***************************************************************** Dim FileName As String

   '*.wav only
   FileName = GetRandomFileName
   SaveBytes ByteArray(), App.Path & "\" & FileName & ".wav.ape"
   CommandLine DataPath & "mac.exe " & QUOTE & App.Path & "\" & FileName & ".wav.ape" & QUOTE & " " & QUOTE & App.Path & "\" & FileName & ".wav" & QUOTE & " -d"
   If Dir$(App.Path & "\" & FileName & ".wav") <> vbNullString Then
       LoadBytes ByteArray(), App.Path & "\" & FileName & ".wav"
       Kill App.Path & "\" & FileName & ".wav"
   End If
   Kill App.Path & "\" & FileName & ".wav.ape"

End Sub

Public Sub Compression_Compress_Deflate64(ByteArray() As Byte) '***************************************************************** 'Compresses a byte array with Deflate64 compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_Compress_Deflate64 '***************************************************************** Dim FileName As String

   FileName = GetRandomFileName
   SaveBytes ByteArray(), App.Path & "\" & FileName & ".bin"
   CommandLine DataPath & "7za.exe a -tzip " & QUOTE & App.Path & "\" & FileName & ".bin.7z" & QUOTE & " -aoa " & QUOTE & App.Path & "\" & FileName & ".bin" & QUOTE & " -mx9 -mm=Deflate64 -mfb=257 -mpass=15 -mmc=1000"
   If Dir$(App.Path & "\" & FileName & ".bin.7z") <> vbNullString Then
       LoadBytes ByteArray(), App.Path & "\" & FileName & ".bin.7z"
       Kill App.Path & "\" & FileName & ".bin.7z"
   End If
   Kill App.Path & "\" & FileName & ".bin"

End Sub

Public Sub Compression_DeCompress_Deflate64(ByteArray() As Byte) '***************************************************************** 'Decompresses a byte array with Deflate64 compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_DeCompress_Deflate64 '***************************************************************** Dim FileName As String

   FileName = GetRandomFileName
   SaveBytes ByteArray(), App.Path & "\" & FileName & ".bin.7z"
   CommandLine DataPath & "7za.exe e " & QUOTE & App.Path & "\" & FileName & ".bin.7z" & QUOTE
   If Dir$(App.Path & "\" & FileName & ".bin") <> vbNullString Then
       LoadBytes ByteArray(), App.Path & "\" & FileName & ".bin"
       Kill App.Path & "\" & FileName & ".bin"
   End If
   Kill App.Path & "\" & FileName & ".bin.7z"

End Sub

Public Sub Compression_Compress_RLE(ByteArray() As Byte, IsCompressed As Boolean) '***************************************************************** 'Compresses a byte array with RLE (Run-Length Encryption) compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_Compress_RLE '***************************************************************** Dim X As Long Dim Y As Long Dim ByteCount As Long Dim LastAsc As Integer Dim TelSame As Long Dim IsRun As Boolean Dim ZeroCount As Integer Dim LengthPos As Long Dim NoLength As Boolean Dim ContStream() As Byte Dim LengthStream() As Byte

   If UBound(ByteArray) = 0 Then Exit Sub
   ReDim ContStream(200)
   ReDim LengthStream(200)
   ReDim OutStream(500)
   IsCompressed = False
   CntPos = 1
   OutPos = 0
   For X = 0 To UBound(ByteArray)
       IsRun = LastAsc = ByteArray(X) And X <> 0
       If Not IsRun Then
           If TelSame = 1 Then
               TelSame = 0
               Compression_Add_CharToArray OutStream, OutPos, CByte(LastAsc)
               ByteCount = ByteCount + 1
           ElseIf TelSame > 1 Then
               For Y = 1 To Int(ByteCount / 255)
                   Compression_Add_CharToArray ContStream, CntPos, 255
               Next Y
               ByteCount = ByteCount Mod 255
               If ByteCount = 0 Then ZeroCount = ZeroCount + 1
               Compression_Add_CharToArray ContStream, CntPos, CByte(ByteCount)
               ByteCount = 0
               For Y = 1 To Int(TelSame / 255)
                   Compression_Add_CharToArray LengthStream, LengthPos, 255
               Next Y
               TelSame = TelSame Mod 255
               Compression_Add_CharToArray LengthStream, LengthPos, CByte(TelSame)
               TelSame = 0
           End If
           Compression_Add_CharToArray OutStream, OutPos, ByteArray(X)
           ByteCount = ByteCount + 1
       Else
           TelSame = TelSame + 1
       End If
       LastAsc = ByteArray(X)
   Next X
   
   If IsRun Then
       If TelSame < 2 Then
           Compression_Add_CharToArray OutStream, OutPos, CByte(LastAsc)
       Else
           For Y = 1 To Int(ByteCount / 255)
               Compression_Add_CharToArray ContStream, CntPos, 255
           Next Y
           ByteCount = ByteCount Mod 255
           Compression_Add_CharToArray ContStream, CntPos, CByte(ByteCount)
           For Y = 1 To Int(TelSame / 255)
               Compression_Add_CharToArray LengthStream, LengthPos, 255
           Next Y
           TelSame = TelSame Mod 255
           Compression_Add_CharToArray LengthStream, LengthPos, CByte(TelSame)
       End If
   End If
   
   ContStream(0) = CByte(ZeroCount)
   If CntPos > 1 Then IsCompressed = True
   Call Compression_Add_CharToArray(ContStream, CntPos, 0)  'No Run Till EOF
   ReDim Preserve ContStream(CntPos - 1) As Byte
   
   If LengthPos > 0 Then
       ReDim Preserve LengthStream(LengthPos - 1)
   Else
       NoLength = True
   End If
   
   ReDim Preserve OutStream(OutPos - 1) As Byte
   CntPos = UBound(ContStream) + 1
   LengthPos = 0
   If Not NoLength Then LengthPos = UBound(LengthStream) + 1
   OutPos = UBound(OutStream) + 1
   ReDim ByteArray(CntPos + LengthPos + OutPos - 1)
   CopyMem ByteArray(0), ContStream(0), CntPos
   If LengthPos > 0 Then CopyMem ByteArray(CntPos), LengthStream(0), LengthPos
   CopyMem ByteArray(CntPos + LengthPos), OutStream(0), OutPos

End Sub

Public Sub Compression_Compress_RLELoop(ByteArray() As Byte) '***************************************************************** 'Compresses a byte array with RLE (Run-Length Encryption) compression 'This will loop it until no more compression can be made 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_Compress_RLELoop '***************************************************************** Dim TimesRLE As Integer Dim IsCompressed As Boolean

   Do
       Compression_Compress_RLE ByteArray, IsCompressed
       TimesRLE = TimesRLE + 1
   Loop While IsCompressed
   ReDim Preserve ByteArray(UBound(ByteArray) + 1)
   ByteArray(UBound(ByteArray)) = TimesRLE

End Sub

Public Sub Compression_DeCompress(SrcFile As String, DestFile As String, Compression As CompressMethods) '***************************************************************** 'A wrapper for easy file-based decompression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_DeCompress '*****************************************************************

   If Compression_File_Load(SrcFile) = 0 Then Exit Sub
   Select Case Compression
       Case RLE
           Compression_DeCompress_RLE CompressArray()
       Case RLE_Loop
           Compression_DeCompress_RLELoop CompressArray()
       Case LZMA
           Compression_DeCompress_LZMA CompressArray()
       Case PAQ8l
           Compression_DeCompress_PAQ8l CompressArray()
       Case Deflate64
           Compression_DeCompress_Deflate64 CompressArray()
   End Select
   Compression_File_Save DestFile
   Erase CompressArray

End Sub

Public Sub Compression_DeCompress_LZMA(ByteArray() As Byte) '***************************************************************** 'Decompresses a byte array using LZMA compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_DeCompress_LZMA '***************************************************************** Dim FileName As String

   FileName = GetRandomFileName
   SaveBytes ByteArray(), App.Path & "\" & FileName & ".bin.7z"
   CommandLine DataPath & "7za.exe e " & QUOTE & App.Path & "\" & FileName & ".bin.7z" & QUOTE
   If Dir$(App.Path & "\" & FileName & ".bin") <> vbNullString Then
       LoadBytes ByteArray(), App.Path & "\" & FileName & ".bin"
       Kill App.Path & "\" & FileName & ".bin"
   End If
   Kill App.Path & "\" & FileName & ".bin.7z"

End Sub

Public Sub Compression_DeCompress_RLE(ByteArray() As Byte) '***************************************************************** 'Decompresses a byte array using RLE (Run-Length Encryption) compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_DeCompress_RLE '***************************************************************** Dim X As Long Dim CntCount As Long Dim bytLastChar As Byte Dim ByteCount As Long Dim InpPos As Long Dim ZeroCount As Integer Dim LengthPos As Long

   ZeroCount = 0
   For X = 1 To UBound(ByteArray)
       If ByteArray(X) = 0 Then
           If ZeroCount = ByteArray(0) Then Exit For
           ZeroCount = ZeroCount + 1
       End If
       If ByteArray(X) <> 255 Then
           CntCount = CntCount + 1
       End If
   Next X
   
   OutPos = 0
   CntPos = 1
   LengthPos = X + 1
   InpPos = LengthPos
   
   Do While CntCount > 0
       If ByteArray(InpPos) <> 255 Then
           CntCount = CntCount - 1
       End If
       InpPos = InpPos + 1
   Loop
   ReDim OutStream(UBound(ByteArray) - InpPos + 1)
   ByteCount = Compression_ReadFromArray_Char(ByteArray, CntPos)
   CntCount = Compression_ReadFromArray_Char(ByteArray, LengthPos)
   Do
       If ByteCount = 0 Then
           For X = 1 To UBound(ByteArray) - InpPos + 1
               bytLastChar = Compression_ReadFromArray_Char(ByteArray, InpPos)
               Compression_Add_CharToArray OutStream, OutPos, bytLastChar
           Next X
       Else
           For X = 1 To ByteCount
               bytLastChar = Compression_ReadFromArray_Char(ByteArray, InpPos)
               Compression_Add_CharToArray OutStream, OutPos, bytLastChar
           Next X
           If ByteCount = 255 Then
               Do
                   ByteCount = Compression_ReadFromArray_Char(ByteArray, CntPos)
                   For X = 1 To ByteCount
                       bytLastChar = Compression_ReadFromArray_Char(ByteArray, InpPos)
                       Compression_Add_CharToArray OutStream, OutPos, bytLastChar
                   Next X
               Loop While ByteCount = 255
               ByteCount = Compression_ReadFromArray_Char(ByteArray, CntPos)
           Else
               ByteCount = Compression_ReadFromArray_Char(ByteArray, CntPos)
           End If
           For X = 1 To CntCount
               Compression_Add_CharToArray OutStream, OutPos, bytLastChar
           Next X
           If CntCount = 255 Then
               Do
                   CntCount = Compression_ReadFromArray_Char(ByteArray, LengthPos)
                   For X = 1 To CntCount
                       Compression_Add_CharToArray OutStream, OutPos, bytLastChar
                   Next X
               Loop While CntCount = 255
               CntCount = Compression_ReadFromArray_Char(ByteArray, LengthPos)
           Else
               CntCount = Compression_ReadFromArray_Char(ByteArray, LengthPos)
           End If
       End If
   Loop While InpPos <= UBound(ByteArray)
   ReDim ByteArray(OutPos - 1) As Byte
   CopyMem ByteArray(0), OutStream(0), OutPos

End Sub

Public Sub Compression_DeCompress_RLELoop(ByteArray() As Byte) '***************************************************************** 'Decompresses a byte array using a looped RLE (Run-Length Encryption) compression 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_DeCompress_RLELoop '***************************************************************** Dim X As Integer Dim TimesRLE As Integer

   TimesRLE = ByteArray(UBound(ByteArray))
   ReDim Preserve ByteArray(UBound(ByteArray) - 1)
   
   For X = 1 To TimesRLE
       Compression_DeCompress_RLE ByteArray
   Next X

End Sub

Private Function Compression_File_Load(FilePath As String) As Byte '***************************************************************** 'Loads a file into the CompressArray() byte array 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_File_Load '***************************************************************** Dim FreeNum As Integer

   If Not Len(FilePath) = 0 Then
       FreeNum = FreeFile
       Open FilePath For Binary As #FreeNum
       If LOF(FreeNum) = 0 Then
           Close #FreeNum
           Compression_File_Load = 0
           Exit Function
       End If
       ReDim CompressArray(0 To LOF(FreeNum) - 1)
       Get #FreeNum, , CompressArray()
       Close #FreeNum
   End If
   Compression_File_Load = 1

End Function

Private Sub Compression_File_Save(FilePath As String) '***************************************************************** 'Saves a file from the CompressArray() byte array 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_File_Save '***************************************************************** Dim FreeNum As Integer

   If LenB(FilePath) <> 0 Then
       If LenB(Dir$(FilePath, vbNormal)) <> 0 Then Kill FilePath
       FreeNum = FreeFile
       Open FilePath For Binary As #FreeNum
       Put #FreeNum, , CompressArray()
       Close #FreeNum
   End If
   

End Sub

Private Function Compression_ReadFromArray_Char(FromArray() As Byte, FromPos As Long) As Byte '***************************************************************** 'Reads a char (byte) from a specific location in an array 'More info: http://www.vbgore.com/CommonCode.Compressions.Compression_ReadFromArray_Char '*****************************************************************

   Compression_ReadFromArray_Char = FromArray(FromPos)
   FromPos = FromPos + 1

End Function </vb>

DataIDs

<vb> Option Explicit

'********** Emoticons ************ Public Const NumEmotes As Byte = 10 Public Type EmoID

   Dots As Byte
   Exclimation As Byte
   Question As Byte
   Surprised As Byte
   Heart As Byte
   Hearts As Byte
   HeartBroken As Byte
   Utensils As Byte
   Meat As Byte
   ExcliQuestion As Byte

End Type Public EmoID As EmoID

'********** Classes ************ 'Classes work by using bitwise operations, so each class ID must be a power of 2 (1, 2, 4, 8, 16, 32, 64, or 128) 'If you want more clases, change the classes to "Integer" 'To set class requirements, OR the values together 'EX: 'ClassReq = Warrior OR Rogue 'This means the class must be a Warrior or Rogue 'To check the values, use AND 'EX: 'If ClassReq AND UserClass Then ' User meets requirements 'Else ' User doesn't meet requirements 'End If Public Type ClassID

   Warrior As Integer
   Mage As Integer
   Rogue As Integer
   NoReq As Integer

End Type Public ClassID As ClassID

'********** Packets ************ 'Data String Codenames (Reduces all data transfers to 1 byte tags) Public Type DataCode

   Comm_Talk As Byte
   Comm_Shout As Byte
   Comm_Emote As Byte
   Comm_Whisper As Byte
   Comm_GroupTalk As Byte
   Comm_FontType_Talk As Byte
   Comm_FontType_Fight As Byte
   Comm_FontType_Info As Byte
   Comm_FontType_Quest As Byte
   Comm_FontType_Group As Byte
   Comm_UseBubble As Byte  'Do not use this alone - OR it onto Comm_Talk!
   Server_MailMessage As Byte
   Server_MailBox As Byte
   Server_MailItemTake As Byte
   Server_MailItemRemove As Byte
   Server_MailDelete As Byte
   Server_MailCompose As Byte
   Server_UserCharIndex As Byte
   Server_SetUserPosition As Byte
   Server_MakeChar As Byte
   Server_MakeCharCached As Byte
   Server_EraseChar As Byte
   Server_MoveChar As Byte
   Server_ChangeChar As Byte
   Server_MakeObject As Byte
   Server_EraseObject As Byte
   Server_PlaySound As Byte
   Server_PlaySound3D As Byte
   Server_Who As Byte
   Server_CharHP As Byte
   Server_CharMP As Byte
   Server_IconCursed As Byte
   Server_IconWarCursed As Byte
   Server_IconBlessed As Byte
   Server_IconStrengthened As Byte
   Server_IconProtected As Byte
   Server_IconIronSkin As Byte
   Server_IconSpellExhaustion As Byte
   Server_SetCharDamage As Byte
   Server_Help As Byte
   Server_Disconnect As Byte
   Server_Connect As Byte
   Server_Message As Byte
   Server_SetCharSpeed As Byte
   Server_MakeProjectile As Byte
   Server_MakeSlash As Byte
   Server_MailObjUpdate As Byte
   Server_MakeEffect As Byte
   Server_SendQuestInfo As Byte
   Server_ChangeCharType As Byte
   Server_KeepAlive As Byte
   Map_LoadMap As Byte
   Map_DoneLoadingMap As Byte
   Map_SendName As Byte
   User_Target As Byte
   User_KnownSkills As Byte
   User_Attack As Byte
   User_SetInventorySlot As Byte
   User_Desc As Byte
   User_Login As Byte
   User_NewLogin As Byte
   User_Get As Byte
   User_Drop As Byte
   User_Use As Byte
   User_Move As Byte
   User_Rotate As Byte
   User_LeftClick As Byte
   User_RightClick As Byte
   User_LookLeft As Byte
   User_LookRight As Byte
   User_Blink As Byte
   User_Trade_StartNPCTrade As Byte
   User_Trade_BuyFromNPC As Byte
   User_Trade_SellToNPC As Byte
   User_Trade_Trade As Byte
   User_Trade_UpdateTrade As Byte
   User_Trade_Accept As Byte
   User_Trade_Finish As Byte
   User_Trade_RemoveItem As Byte
   User_Trade_Cancel As Byte
   User_Bank_Open As Byte
   User_Bank_PutItem As Byte
   User_Bank_TakeItem As Byte
   User_Bank_UpdateSlot As Byte
   User_Bank_Balance As Byte
   User_Bank_Deposit As Byte
   User_Bank_Withdraw As Byte
   User_BaseStat As Byte
   User_ModStat As Byte
   User_CastSkill As Byte
   User_ChangeInvSlot As Byte
   User_Emote As Byte
   User_StartQuest As Byte
   User_CancelQuest As Byte
   User_SetWeaponRange As Byte
   User_RequestMakeChar As Byte
   User_RequestUserCharIndex As Byte
   User_ChangeServer As Byte
   User_ConfirmPosition As Byte
   User_Group_Make As Byte
   User_Group_Join As Byte
   User_Group_Leave As Byte
   User_Group_Invite As Byte
   User_Group_Info As Byte
   GM_Approach As Byte
   GM_Summon As Byte
   GM_Kick As Byte
   GM_Raise As Byte
   GM_SetGMLevel As Byte
   GM_Thrall As Byte
   GM_DeThrall As Byte
   GM_BanIP As Byte
   GM_UnBanIP As Byte
   GM_Warp As Byte
   GM_FindItem As Byte
   GM_SQL As Byte
   GM_GiveSkill As Byte
   GM_GiveGold As Byte
   GM_GiveObject As Byte
   GM_KillMap As Byte
   GM_Kill As Byte
   GM_WarpToMap As Byte
   GM_IPInfo As Byte
   GM_BanList As Byte
   Combo_ProjectileSoundRotateDamage As Byte
   Combo_SoundRotateDamage As Byte
   Combo_SlashSoundRotateDamage As Byte

End Type Public DataCode As DataCode

'********** Character Stats/Skills ************ 'Keep in mind vbGORE does a really bad job at actually making use of these stats by default, especially for the NPCs. If you're going 'to make a game, you will definitely want to add your own stats and change how they work. Public Type StatOrder

   'These can NOT be modded (theres no ModStat())
   MinMAN As Byte
   MinHP As Byte
   MinSTA As Byte
   Gold As Byte
   Points As Byte
   EXP As Byte
   ELU As Byte
   ELV As Byte
   'These CAN be modded (ModStat() is used along with BaseStat())
   MaxHIT As Byte
   MinHIT As Byte
   DEF As Byte
   MaxHP As Byte
   MaxSTA As Byte
   MaxMAN As Byte
   Str As Byte
   Agi As Byte     'For NPCs, this is the hit rate
   Mag As Byte
   Speed As Byte   'Speed works as + (Speed / 2) on the client since just + Speed would be too drastic (8 would double the normal speed)

End Type Public SID As StatOrder 'Stat ID Public Const NumStats As Byte = 18 Public Const FirstModStat As Byte = 9 'The lowest number of the first stat that can be modded

Public Type SkillID

   Bless As Byte
   Protection As Byte
   Strengthen As Byte
   Warcry As Byte
   Heal As Byte
   IronSkin As Byte
   SpikeField As Byte
   SummonBandit As Byte

End Type Public SkID As SkillID 'Skill IDs Public Const NumSkills As Byte = 8

Public Sub InitDataCommands() '***************************************************************** 'Sets the values of IDs for emoticons, skills, packets, etc 'Every value in here must be identical on the client and server, which is why 'the same module is used for both the client and server! 'More info: http://www.vbgore.com/CommonCode.DataIDs.InitDataCommands '*****************************************************************

   'Emoticon IDs
   With EmoID
       .Dots = 1
       .Exclimation = 2
       .Question = 3
       .Surprised = 4
       .Heart = 5
       .Hearts = 6
       .HeartBroken = 7
       .Utensils = 8
       .Meat = 9
       .ExcliQuestion = 10
   End With
   'Skill IDs
   With SkID
       .Bless = 1
       .Heal = 2
       .IronSkin = 3
       .Protection = 4
       .Strengthen = 5
       .Warcry = 6
       .SpikeField = 7
       .SummonBandit = 8
   End With
   
   'Class IDs
   With ClassID
       'These values must be based off of powers of 2! (Note: The 16th bit is not 2 ^ 16, its -(2 ^ 15) because its signed)
       'If you do not set the values in powers of 2, it will screw up the classes big time.
       .Warrior = 1    '2 ^ 0
       .Mage = 2       '2 ^ 1
       .Rogue = 4      '2 ^ 2 ... etc
       
       'This sets every bit to 1, which means that it will work with every class
       .NoReq = -1 'Read up on how signed binary works if you want to figure out why this is -1
       
   End With
   'Stat IDs
   With SID
       'These can NOT be modded - they only have and need one value BaseStat()
       .MinHP = 1
       .MinMAN = 2
       .MinSTA = 3
       .Gold = 4
       .Points = 5
       .EXP = 6
       .ELU = 7
       .ELV = 8
       'These CAN be modded, whether it is by spells, items, etc - the mod value is held in ModStat()
       .MaxHIT = 9
       .MaxHP = 10
       .MaxMAN = 11
       .MaxSTA = 12
       .MinHIT = 13
       .DEF = 14
       .Agi = 15
       .Mag = 16
       .Str = 17
       .Speed = 18
   End With
   'Packet IDs
   With DataCode
       .User_RequestMakeChar = 1
       .GM_Thrall = 2
       .Server_IconSpellExhaustion = 3
       .Comm_Shout = 4
       .Server_UserCharIndex = 5
       .Comm_Emote = 6
       .Server_SetUserPosition = 7
       .Map_LoadMap = 8
       .Map_DoneLoadingMap = 9
       .GM_Raise = 10
       .GM_Kick = 11
       .Server_CharHP = 12
       .GM_Summon = 13
       .User_ChangeServer = 14
       .Map_SendName = 15
       .User_Attack = 16
       .Server_MakeChar = 17
       .Server_EraseChar = 18
       .Server_MoveChar = 19
       .Server_ChangeChar = 20
       .Server_MakeObject = 21
       .Server_EraseObject = 22
       .User_KnownSkills = 23
       .User_SetInventorySlot = 24
       .User_StartQuest = 25
       .Server_Connect = 26
       .Server_PlaySound = 27
       .User_Login = 28
       .User_NewLogin = 29
       .Comm_Whisper = 30
       .Server_Who = 31
       .User_Move = 32
       .User_Rotate = 33
       .User_LeftClick = 34
       .User_RightClick = 35
       .User_Group_Info = 36
       .User_Get = 37
       .User_Drop = 38
       .User_Use = 39
       .GM_Approach = 40
       .Comm_Talk = 41
       .Server_SetCharDamage = 42
       .User_ChangeInvSlot = 43
       .User_Emote = 44
       .Server_CharMP = 45
       .Server_Disconnect = 46
       .User_LookLeft = 47
       .User_LookRight = 48
       .User_Blink = 49
       .User_Trade_RemoveItem = 50
       .User_Trade_BuyFromNPC = 51
       .User_BaseStat = 52
       .User_ModStat = 53
       .GM_BanIP = 54
       .GM_UnBanIP = 55
       .Server_SendQuestInfo = 56
       .User_ConfirmPosition = 57
       .Server_Help = 58
       .User_Desc = 59
       .User_Trade_Cancel = 60
       .User_Target = 61
       .User_Trade_StartNPCTrade = 62
       .User_Trade_SellToNPC = 63
       .User_CastSkill = 64
       .Server_IconCursed = 65
       .Server_IconWarCursed = 66
       .Server_IconBlessed = 67
       .Server_IconStrengthened = 68
       .Server_IconProtected = 69
       .Server_IconIronSkin = 70
       .Server_MailBox = 71
       .Server_MailMessage = 72
       .User_RequestUserCharIndex = 73
       .Server_MailItemTake = 74
       .Server_MailObjUpdate = 75
       .Server_MailDelete = 76
       .Server_MailCompose = 77
       .GM_SetGMLevel = 78
       .Server_Message = 79
       .GM_DeThrall = 80
       .Server_PlaySound3D = 81
       .Server_SetCharSpeed = 82
       .User_SetWeaponRange = 83
       .Server_MakeProjectile = 84
       .Server_MakeSlash = 85
       .Server_MakeEffect = 86
       .User_Bank_Open = 87
       .User_Bank_PutItem = 88
       .User_Bank_TakeItem = 89
       .User_Bank_UpdateSlot = 90
       .User_Group_Join = 91
       .User_Group_Invite = 92
       .User_Group_Leave = 93
       .User_Group_Make = 94
       .Comm_GroupTalk = 95
       .User_Bank_Deposit = 96
       .User_Bank_Withdraw = 97
       .User_Bank_Balance = 98
       .GM_Warp = 99
       .Server_ChangeCharType = 100
       .User_Trade_Trade = 101
       .User_Trade_UpdateTrade = 102
       .User_Trade_Accept = 104
       .User_Trade_Finish = 105
       .User_CancelQuest = 106
       .Combo_ProjectileSoundRotateDamage = 107
       .Combo_SoundRotateDamage = 108
       .Combo_SlashSoundRotateDamage = 109
       .Server_MakeCharCached = 110
       .GM_FindItem = 111
       .GM_SQL = 112
       .GM_GiveSkill = 113
       .GM_GiveGold = 114
       .GM_GiveObject = 115
       .GM_KillMap = 116
       .GM_Kill = 117
       .GM_WarpToMap = 118
       .GM_IPInfo = 119
       .GM_BanList = 120
       .Server_KeepAlive = 121
       
       'This values can be used over again since they aren't used in their own packet header
       .Comm_FontType_Fight = 1
       .Comm_FontType_Info = 2
       .Comm_FontType_Quest = 3
       .Comm_FontType_Talk = 4
       .Comm_FontType_Group = 5
       'Value 128 can be used over again since this does not count as an ID in itself - just ignore this variable! ;)
       .Comm_UseBubble = 128
       
   End With

End Sub </vb>

Declares

<vb> '** ____ _________ ______ ______ ______ _______ ** '** \ \ / / \ / ____\ / \| \ | ____| ** '** \ \ / /| | / | | || |____ ** '*** \ \ / / | /| | ___ | | / | ____| *** '**** \ \/ / | \| | \ \| | _ \ | |____ **** '****** \ / | | \__| | | | \ \| | ****** '******** \____/ |_____/ \______/ \______/|__| \__\_______| ******** '******************************************************************************* '******************************************************************************* '************ vbGORE - Visual Basic 6.0 Graphical Online RPG Engine ************ '************ Official Release: Version 1.0.13 ************ '************ http://www.vbgore.com ************ '******************************************************************************* '******************************************************************************* '***** License Information For General Users: ********************************** '******************************************************************************* '** vbGORE comes completely free. You may charge for people to play your game ** '** along with you may accept donations for your game. The only rules you ** '** must follow when using vbGORE are: ** '** - You may not claim the engine as your own creation. ** '** - You may not sell the code to vbGORE in any way or form, whether it is ** '** the original vbGORE code or a modified version of it. Selling your game** '** may be an exception - if you wish to do this, you must first acquire ** '** permission from Spodi directly. ** '** - If you are distributing vbGORE or modified code of it, read the ** '** section "Source Distrubution Information" below. ** '** This license is subject to change at any time. Only the most current ** '** version of the license applies, not the copy of the license that came with** '** your copy of vbGORE. This means if rules are added for version 1.0, you ** '** can not avoid them by using a previous version such as 0.3. ** '******************************************************************************* '***** Source Distribution Information: **************************************** '******************************************************************************* '** If you wish to distribute this source code, you must distribute as-is ** '** from the vbGORE website unless permission is given to do otherwise. This ** '** comment block must remain in-tact in the distribution. If you wish to ** '** distribute modified versions of vbGORE, please contact Spodi (info below) ** '** before distributing the source code. You may never label the source code ** '** as the "Official Release" or similar unless the code and content remains ** '** unmodified from the version downloaded from the official website. ** '** You may also never sale the source code without permission first. If you ** '** want to sell the code, please contact Spodi (below). This is to prevent ** '** people from ripping off other people by selling an insignificantly ** '** modified version of open-source code just to make a few quick bucks. ** '******************************************************************************* '***** Creating Engines With vbGORE: ******************************************* '******************************************************************************* '** If you plan to create an engine with vbGORE that, please contact Spodi ** '** before doing so. You may not sell the engine unless told elsewise (the ** '** engine must has substantial modifications), and you may not claim it as ** '** all your own work - credit must be given to vbGORE, along with a link to ** '** the vbGORE homepage. Failure to gain approval from Spodi directly to ** '** make a new engine with vbGORE will result in first a friendly reminder, ** '** followed by much more drastic measures. ** '******************************************************************************* '***** Helping Out vbGORE: ***************************************************** '******************************************************************************* '** If you want to help out with vbGORE's progress, theres a few things you ** '** can do: ** '** *Donate - Great way to keep a free project going. :) Info and benifits ** '** for donating can be found at: ** '** http://www.vbgore.com/index.php?title=Donate ** '** *Contribute - Check out our forums, contribute ideas, report bugs, or ** '** help expend the wiki pages! ** '** *Link To Us - Creating a link to vbGORE, whether it is on your own web ** '** page or a link to vbGORE in a forum you visit, every link helps ** '** spread the word of vbGORE's existance! Buttons and banners for ** '** linking to vbGORE can be found on the following page: ** '** http://www.vbgore.com/index.php?title=Buttons_and_Banners ** '** *Spread The Word - The more people who know about vbGORE, the more people** '** there is to report bugs and suggestions to improve the engine! ** '******************************************************************************* '***** Conact Information: ***************************************************** '******************************************************************************* '** Please contact the creator of vbGORE (Spodi) directly with any questions: ** '** AIM: Spodii Yahoo: Spodii ** '** MSN: Spodii@hotmail.com Email: spodi@vbgore.com ** '** 2nd Email: spodii@hotmail.com Website: http://www.vbgore.com ** '******************************************************************************* '***** Credits: **************************************************************** '******************************************************************************* '** Below are credits to those who have helped with the project or who have ** '** distributed source code which has help this project's creation. The below ** '** is listed in no particular order of significance: ** '** ** '** Chase: Help with programming, bug reports, and adding the trading system ** '** Nex666: Help with mapping, graphics, bug reports, hosting, etc ** '** Graphics (Avatar): Supplied the character sprite graphics, + a few more ** '** http://www.zidev.com/ ** '** Map tiles: ** '** http://lostgarden.com/2006/07/more-free-game-graphics.html ** '** ORE (Aaron Perkins): Used as base engine and for learning experience ** '** http://www.baronsoft.com/ ** '** SOX (Trevor Herselman): Used for all the networking ** '** http://pscode.com/vb/scripts/ShowCode.asp?txtCodeId=35239&lngWId=1 ** '** Compression Methods (Marco v/d Berg): Provided compression algorithms ** '** http://pscode.com/vb/scripts/ShowCode.asp?txtCodeId=37867&lngWId=1 ** '** All Files In Folder (Jorge Colaccini): Algorithm implimented into engine ** '** http://pscode.com/vb/scripts/ShowCode.asp?txtCodeId=51435&lngWId=1 ** '** ** '** Also, all the members of the vbGORE community who have submitted ** '** tutorials, bugs, suggestions, criticism and have just stuck around!! ** '** ** '** If you feel you belong in these credits, please contact Spodi (above). ** '******************************************************************************* '*******************************************************************************

Option Explicit

'********** Debug/Display Settings ************ 'These are your key constants - reccomended you turn off ALL debug constants before ' compiling your code for public usage just speed reasons

'Set this to true to force updater check Public Const ForceUpdateCheck As Boolean = False

'Running speed - make sure you have the same value on the server! Public Const RunningSpeed As Byte = 5 Public Const RunningCost As Long = 1 'How much stamina it cost to run

'Max chat bubble width Public Const BubbleMaxWidth As Long = 140

'Word filter - use by "word-filterto,nextword-nextfilterto"... etc Public Const FilterString As String = "fuck-****,shit-****,ass-***,bitch-*****" Public FilterFind() As String Public FilterReplace() As String

'********** NPC chat info ************ Public Type NPCChatLineCondition

   Condition As Byte           'The condition used (see NPCCHAT_COND_)
   Value As Long               'Used to hold a numeric condition value
   ValueStr As String          'Used to hold a value for SAY conditions

End Type Public Type NPCChatLine

   NumConditions As Byte       'Total number of conditions
   Conditions() As NPCChatLineCondition
   Text As String              'The text that will be said
   Style As Byte               'The style used for the text (see NPCCHAT_STYLE_)
   Delay As Integer            'The delay time applied after saying this line

End Type Public Type NPCChatAskAnswer 'The individual chat input answers

   Text As String              'The answer string
   GotoID As Byte              'ID the answer will move to

End Type Public Type NPCChatAskLine 'Individual chat input lines

   Question As String          'The question text
   NumAnswers As Byte          'Number of answers that can be used
   Answer() As NPCChatAskAnswer

End Type Public Type NPCChatAsk 'Chat input information (ASK parameters)

   StartAsk As Byte            'ID to start the asking on
   Ask() As NPCChatAskLine     'Holds all the ASK questions/responses

End Type Public Type NPCChat

   Format As Byte              'Format of the chat (see NPCCHAT_FORMAT_)
   ChatLine() As NPCChatLine   'The information on the chat line
   NumLines As Byte            'The number of chat lines
   Distance As Long            'The distance the user must be from the NPC to activate the chat
   Ask As NPCChatAsk           'All the ASK information

End Type Public NPCChat() As NPCChat

'Conditions (this are used as bit-flags, so only use powers of 2!) Public Const NPCCHAT_COND_LEVELLESSTHAN As Long = 2 ^ 0 Public Const NPCCHAT_COND_LEVELMORETHAN As Long = 2 ^ 1 Public Const NPCCHAT_COND_HPLESSTHAN As Long = 2 ^ 2 Public Const NPCCHAT_COND_HPMORETHAN As Long = 2 ^ 3 Public Const NPCCHAT_COND_KNOWSKILL As Long = 2 ^ 4 Public Const NPCCHAT_COND_DONTKNOWSKILL As Long = 2 ^ 5 Public Const NPCCHAT_COND_SAY As Long = 2 ^ 6

'Chat formats Public Const NPCCHAT_FORMAT_RANDOM As Byte = 0 Public Const NPCCHAT_FORMAT_LINEAR As Byte = 1

'Chat sytles Public Const NPCCHAT_STYLE_BOTH As Byte = 0 Public Const NPCCHAT_STYLE_BOX As Byte = 1 Public Const NPCCHAT_STYLE_BUBBLE As Byte = 2

'Client character types Public Const ClientCharType_PC As Byte = 1 Public Const ClientCharType_NPC As Byte = 2 Public Const ClientCharType_Grouped As Byte = 3 Public Const ClientCharType_Slave As Byte = 4

'********** Trade table ************ Public Type TradeObj

   Name As String
   Grh As Long
   Amount As Integer
   Value As Long

End Type Public Type TradeTable

   User1Name As String              'The name of the table
   User2Name As String
   User1Accepted As Byte
   User2Accepted As Byte
   Trade1(1 To 9) As TradeObj  'The objects both indexes have entered
   Trade2(1 To 9) As TradeObj
   Gold1 As Long               'The gold both indexes have entered
   Gold2 As Long
   MyIndex As Byte             'States whether this client is index 1 or 2

End Type Public TradeTable As TradeTable

'********** Other stuff ************ Public BaseStats(1 To NumStats) As Long Public ModStats(FirstModStat To NumStats) As Long

'Delay timers for packet-related actions (so to not spam the server) Public Const AttackDelay As Long = 200 'These constants are client-side only Public Const LootDelay As Long = 500 ' - changing these lower wont make it faster server-side! Public LastAttackTime As Long Public LastLootTime As Long

'Cached packets Type Cache_Server_MakeChar

   Body As Integer
   Head As Integer
   Heading As Byte
   X As Byte
   Y As Byte
   Speed As Byte
   Name As String
   Weapon As Integer
   Hair As Integer
   Wings As Integer
   HP As Byte
   MP As Byte
   ChatID As Byte
   CharType As Byte

End Type Type PacketCache

   Server_MakeChar As Cache_Server_MakeChar

End Type Public PacketCache As PacketCache

'Item description variables Public ItemDescWidth As Long Public ItemDescLine(1 To 10) As String 'Allow 10 lines maximum Public ItemDescLines As Byte

'Object constants Public Const MAX_INVENTORY_SLOTS As Byte = 49

'Active ASK information Public Type ActiveAsk

   AskName As String
   AskIndex As Byte
   ChatIndex As Byte
   QuestionTxt As String

End Type Public ActiveAsk As ActiveAsk

'User's inventory Type Inventory

   ObjIndex As Long
   Name As String
   GrhIndex As Long
   Amount As Integer
   Equipped As Boolean
   Value As Long

End Type

'Quest information Type QuestInfo

   Name As String
   Desc As String

End Type Public QuestInfo() As QuestInfo Public QuestInfoUBound As Byte

'Messages Public NumMessages As Byte Public Message() As String

'Signs Public Signs() As String

'Known user skills/spells Public UserKnowSkill(1 To NumSkills) As Byte

'Attack range Public UserAttackRange As Byte

'User status vars Public UserInventory(1 To MAX_INVENTORY_SLOTS) As Inventory Public UserBank(1 To MAX_INVENTORY_SLOTS) As Inventory

'The time the last packet from the server arrived Public LastServerPacketTime As Long

'If there is a clear path to the target (if any) Public ClearPathToTarget As Byte

'Used during login Public SendNewChar As Boolean

Public sndBuf As DataBuffer Public ChatBufferChunk As Single Public SoxID As Long Public SocketMoveToIP As String Public SocketMoveToPort As Integer Public SocketOpen As Byte Public TargetCharIndex As Integer Public Const DegreeToRadian As Single = 0.01745329251994 'Pi / 180 Public Const RadianToDegree As Single = 57.2958279087977 '180 / Pi

'Mail sending spam prevention Public LastMailSendTime As Long

'Holds the skin the user is using at the time Public CurrentSkin As String

'Blocked directions - take the blocked byte and OR these values (If Blocked OR <Direction> Then...) Public Const BlockedNorth As Byte = 1 Public Const BlockedEast As Byte = 2 Public Const BlockedSouth As Byte = 4 Public Const BlockedWest As Byte = 8 Public Const BlockedAll As Byte = 15

Public UseSfx As Byte Public UseMusic As Byte

'States if the project is unloading (has to give Sox time to unload) Public IsUnloading As Byte

'User login information Public UserPassword As String Public UserName As String Public UserClass As Byte Public UserBody As Byte Public UserHead As Byte

'Holds the name of the last person to whisper to the client Public LastWhisperName As String

'Zoom level - 0 = No Zoom, > 0 = Zoomed Public ZoomLevel As Single Public Const MaxZoomLevel As Single = 0.3

'Cursor flash rate Public Const CursorFlashRate As Long = 450

'If click-warping is on or not (can only be used by GMs) Public UseClickWarp As Byte

'Emoticon delay Public EmoticonDelay As Long

'How long char remains aggressive-faced after being attacked Public Const AGGRESSIVEFACETIME = 4000

'Save password check Public SavePass As Boolean

'Maximum variable sizes Public Const MAXLONG As Long = (2 ^ 31) - 1 Public Const MAXINT As Integer = (2 ^ 15) - 1 Public Const MAXBYTE As Byte = (2 ^ 8) - 1

'********** DLL CALLS *********** Public Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer Public Declare Function writeprivateprofilestring Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationname As String, ByVal lpKeyname As Any, ByVal lpString As String, ByVal lpfilename As String) As Long Public Declare Function getprivateprofilestring Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationname As String, ByVal lpKeyname As Any, ByVal lpdefault As String, ByVal lpreturnedstring As String, ByVal nsize As Long, ByVal lpfilename As String) As Long Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) Public Declare Sub ZeroMemory Lib "kernel32.dll" Alias "RtlZeroMemory" (ByRef Destination As Any, ByVal Length As Long) Public Declare Function GetActiveWindow Lib "user32" () As Long Public Declare Function timeGetTime Lib "winmm.dll" () As Long Public Declare Function timeBeginPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long </vb>

Encryptions

<vb> Option Explicit

'Credits goes to Fredrik Qvarfort for writing the algorithms in Visual Basic!

'***** Packet encryption options ***** Public Const PacketEncTypeNone As Byte = 0 'Use no encryption Public Const PacketEncTypeRC4 As Byte = 1 'Use RC4 encryption Public Const PacketEncTypeXOR As Byte = 2 'Use XOR encryption Public Const PacketEncTypeServerIn As Byte = PacketEncTypeNone 'Encryption for server in (or client out) packets Public Const PacketEncTypeServerOut As Byte = PacketEncTypeNone 'Encryption for server out (or client in) packets

'These are only used if the PacketEncType is not PacketEncTypeNone Private Const PacketEncKey1 As String = "al123vcAM !$@(2!@_#;241234vzxv!@$(*_DSZVc2123" 'First encryption key (any string works) Private Const PacketEncKey2 As String = "t123409-nsad DS:!$N$MN!U_AKLJ!1240naga!@$)ZZV" 'Second encryption key (any string works) Public Const PacketEncSeed As Long = 214 'The number to start from (any random value works) Public Const PacketEncKeys As Byte = 40 'Number of packet encryption keys

'***** RC4 ***** Private m_sBoxRC4(0 To 255) As Integer

'***** SIMPLE XOR ***** Private m_XORKey() As Byte Private m_XORKeyLen As Long Private m_XORKeyValue As String

'***** MISC *****

'Key-dependant Private m_KeyS As String

Private Declare Sub CopyMem Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)

Sub GenerateEncryptionKeys(ByRef PacketKeys() As String) '***************************************************************** 'Generates a series of unique keys based off the parameters 'It is recommended you change this routine a bit for better safety for public games 'Do NOT use random (Rnd) values since the server and client must make identical keys 'More info: http://www.vbgore.com/CommonCode.Encryptions.GenerateEncryptionKeys '***************************************************************** Dim Seed As Long Dim Key1 As String Dim Key2 As String Dim B2() As Byte Dim b() As Byte Dim i As Long Dim j As Long

   'Set the start values
   Seed = PacketEncSeed
   Key1 = PacketEncKey1
   Key2 = PacketEncKey2
   
   'Set the number of keys
   ReDim PacketKeys(0 To PacketEncKeys - 1)
   
   'Crop down the keys if needed
   If Len(Key2) > 32 Then Key2 = Left$(Key2, 32)
   If Len(Key1) > 32 Then Key1 = Left$(Key1, 32)
   
   'Loop through the needed keys
   For i = 0 To PacketEncKeys - 1
   
       'Generate a new seed
       Seed = (i * Seed) - 1
   
       'Jumble up the keys through XOR randomization
       b = StrConv(Key1, vbFromUnicode) 'Convert to a byte array
       B2 = StrConv(Key2, vbFromUnicode)
       For j = 0 To Len(Key1) - 1
           Seed = Seed + j + 1         'Modify the seed based on the placement of the character
           Do While Seed > 255         'Keep it in the byte range
               Seed = Seed - 255
           Loop
           b(j) = b(j) Xor Seed        'XOR the character by the seed
           B2(j) = B2(j) Xor CByte(Seed \ 2)
       Next j
       Key1 = StrConv(b, vbUnicode)     'Convert back to a string
       Key2 = StrConv(B2, vbUnicode)
           
       'Jumble up the keys through encryption
       Key2 = Encryption_RC4_EncryptString(Key2, Key1)
       Key1 = Encryption_RC4_EncryptString(Key1, Key2)
       
       'Store the key
       PacketKeys(i) = Key1
       
   Next i

End Sub

Private Function Encryption_Misc_FileExist(FileName As String) As Boolean '***************************************************************** 'Checks if a file exists 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_Misc_FileExist '*****************************************************************

   On Error GoTo NotExist
   Call FileLen(FileName)
   Encryption_Misc_FileExist = True
   
   On Error GoTo 0

NotExist:

End Function

Public Sub Encryption_RC4_DecryptByte(ByteArray() As Byte, Optional Key As String) '***************************************************************** 'Decryptes a byte array with RC4 encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_RC4_DecryptByte '*****************************************************************

   Call Encryption_RC4_EncryptByte(ByteArray(), Key)

End Sub

Public Sub Encryption_RC4_DecryptFile(SourceFile As String, DestFile As String, Optional Key As String) '***************************************************************** 'Decrypts a file with RC4 encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_RC4_DecryptFile '***************************************************************** Dim Filenr As Integer Dim ByteArray() As Byte

   'Make sure the source file do exist
   If (Not Encryption_Misc_FileExist(SourceFile)) Then
       Call Err.Raise(vbObjectError, , "Error in Skipjack Encryption_RC4_EncryptFile procedure (Source file does not exist).")
       Exit Sub
   End If
   'Open the source file and read the content
   'into a bytearray to decrypt
   Filenr = FreeFile
   Open SourceFile For Binary Access Read As #Filenr
   ReDim ByteArray(0 To LOF(Filenr) - 1)
   Get #Filenr, , ByteArray()
   Close #Filenr
   'Decrypt the bytearray
   Call Encryption_RC4_DecryptByte(ByteArray(), Key)
   'If the destination file already exist we need
   'to delete it since opening it for binary use
   'will preserve it if it already exist
   If (Encryption_Misc_FileExist(DestFile)) Then Kill DestFile
   'Store the decrypted data in the destination file
   Filenr = FreeFile
   Open DestFile For Binary Access Write As #Filenr
   Put #Filenr, , ByteArray()
   Close #Filenr

End Sub

Public Function Encryption_RC4_DecryptString(Text As String, Optional Key As String) As String '***************************************************************** 'Decrypts a string array with RC4 encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_RC4_DecryptString '***************************************************************** Dim ByteArray() As Byte

'Convert the data into a byte array

   ByteArray() = StrConv(Text, vbFromUnicode)
   'Decrypt the byte array
   Call Encryption_RC4_DecryptByte(ByteArray(), Key)
   'Convert the byte array back into a string
   Encryption_RC4_DecryptString = StrConv(ByteArray(), vbUnicode)

End Function

Public Sub Encryption_RC4_EncryptByte(ByteArray() As Byte, Optional Key As String) '***************************************************************** 'Encrypts a byte array with RC4 encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_RC4_EncryptByte '***************************************************************** Dim i As Long Dim j As Long Dim Temp As Byte Dim Offset As Long Dim OrigLen As Long Dim sBox(0 To 255) As Integer

   'Set the new key (optional)
   If (Len(Key) > 0) Then Encryption_RC4_SetKey Key
   'Create a local copy of the sboxes, this
   'is much more elegant than recreating
   'before encrypting/decrypting anything
   Call CopyMem(sBox(0), m_sBoxRC4(0), 512)
   'Get the size of the source array
   OrigLen = UBound(ByteArray) + 1
   'Encrypt the data
   For Offset = 0 To (OrigLen - 1)
       i = (i + 1) Mod 256
       j = (j + sBox(i)) Mod 256
       Temp = sBox(i)
       sBox(i) = sBox(j)
       sBox(j) = Temp
       ByteArray(Offset) = ByteArray(Offset) Xor (sBox((sBox(i) + sBox(j)) Mod 256))
   Next

End Sub

Public Sub Encryption_RC4_EncryptFile(SourceFile As String, DestFile As String, Optional Key As String) '***************************************************************** 'Encrypts a file with RC4 encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_RC4_EncryptFile '***************************************************************** Dim Filenr As Integer Dim ByteArray() As Byte

   'Make sure the source file do exist
   If (Not Encryption_Misc_FileExist(SourceFile)) Then
       Call Err.Raise(vbObjectError, , "Error in Skipjack Encryption_RC4_EncryptFile procedure (Source file does not exist).")
       Exit Sub
   End If
   'Open the source file and read the content
   'into a bytearray to pass onto encryption
   Filenr = FreeFile
   Open SourceFile For Binary Access Read As #Filenr
   ReDim ByteArray(0 To LOF(Filenr) - 1)
   Get #Filenr, , ByteArray()
   Close #Filenr
   'Encrypt the bytearray
   Call Encryption_RC4_EncryptByte(ByteArray(), Key)
   'If the destination file already exist we need
   'to delete it since opening it for binary use
   'will preserve it if it already exist
   If (Encryption_Misc_FileExist(DestFile)) Then Kill DestFile
   'Store the encrypted data in the destination file
   Filenr = FreeFile
   Open DestFile For Binary Access Write As #Filenr
   Put #Filenr, , ByteArray()
   Close #Filenr

End Sub

Public Function Encryption_RC4_EncryptString(Text As String, Optional Key As String) As String '***************************************************************** 'Encrypts a string with RC4 encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_RC4_EncryptString '***************************************************************** Dim ByteArray() As Byte

   'Convert the data into a byte array
   ByteArray() = StrConv(Text, vbFromUnicode)
   'Encrypt the byte array
   Call Encryption_RC4_EncryptByte(ByteArray(), Key)
   'Convert the byte array back into a string
   Encryption_RC4_EncryptString = StrConv(ByteArray(), vbUnicode)

End Function

Public Sub Encryption_RC4_SetKey(New_Value As String) '***************************************************************** 'Sets the encryption key for RC4 encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_RC4_SetKey '***************************************************************** Dim a As Long Dim b As Long Dim Temp As Byte Dim Key() As Byte Dim KeyLen As Long

   'Do nothing if the key is buffered
   If (m_KeyS = New_Value) Then Exit Sub
   'Set the new key
   m_KeyS = New_Value
   'Save the password in a byte array
   Key() = StrConv(m_KeyS, vbFromUnicode)
   KeyLen = Len(m_KeyS)
   'Initialize s-boxes
   For a = 0 To 255
       m_sBoxRC4(a) = a
   Next a
   For a = 0 To 255
       b = (b + m_sBoxRC4(a) + Key(a Mod KeyLen)) Mod 256
       Temp = m_sBoxRC4(a)
       m_sBoxRC4(a) = m_sBoxRC4(b)
       m_sBoxRC4(b) = Temp
   Next

End Sub

Public Sub Encryption_XOR_DecryptByte(ByteArray() As Byte, Optional Key As String) '***************************************************************** 'Decrypts a byte array with XOR encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_XOR_DecryptByte '*****************************************************************

   Call Encryption_XOR_EncryptByte(ByteArray(), Key)

End Sub

Public Sub Encryption_XOR_DecryptFile(SourceFile As String, DestFile As String, Optional Key As String) '***************************************************************** 'Decrypts a file with XOR encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_XOR_DecryptFile '***************************************************************** Dim Filenr As Integer Dim ByteArray() As Byte

   'Make sure the source file do exist
   If (Not Encryption_Misc_FileExist(SourceFile)) Then
       Call Err.Raise(vbObjectError, , "Error in Skipjack Encryption_XOR_EncryptFile procedure (Source file does not exist).")
       Exit Sub
   End If
   'Open the source file and read the content
   'into a bytearray to decrypt
   Filenr = FreeFile
   Open SourceFile For Binary Access Read As #Filenr
   ReDim ByteArray(0 To LOF(Filenr) - 1)
   Get #Filenr, , ByteArray()
   Close #Filenr
   'Decrypt the bytearray
   Call Encryption_XOR_DecryptByte(ByteArray(), Key)
   'If the destination file already exist we need
   'to delete it since opening it for binary use
   'will preserve it if it already exist
   If (Encryption_Misc_FileExist(DestFile)) Then Kill DestFile
   'Store the decrypted data in the destination file
   Filenr = FreeFile
   Open DestFile For Binary Access Write As #Filenr
   Put #Filenr, , ByteArray()
   Close #Filenr

End Sub

Public Function Encryption_XOR_DecryptString(Text As String, Optional Key As String) As String '***************************************************************** 'Decrypts a string with XOR encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_XOR_DecryptString '***************************************************************** Dim ByteArray() As Byte

   'Convert the source string into a byte array
   ByteArray() = StrConv(Text, vbFromUnicode)
   'Encrypt the byte array
   Call Encryption_XOR_DecryptByte(ByteArray(), Key)
   'Return the encrypted data as a string
   Encryption_XOR_DecryptString = StrConv(ByteArray(), vbUnicode)

End Function

Public Sub Encryption_XOR_EncryptByte(ByteArray() As Byte, Optional Key As String) '***************************************************************** 'Encrypts a byte array with XOR encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_XOR_EncryptByte '***************************************************************** Dim Offset As Long Dim ByteLen As Long

   'Set the new key if one was provided
   If (Len(Key) > 0) Then Encryption_XOR_SetKey Key
   'Get the size of the source array
   ByteLen = UBound(ByteArray) + 1
   'Loop thru the data encrypting it with simply XOR´ing with the key
   For Offset = 0 To (ByteLen - 1)
       ByteArray(Offset) = ByteArray(Offset) Xor m_XORKey(Offset Mod m_XORKeyLen)
   Next

End Sub

Public Sub Encryption_XOR_EncryptFile(SourceFile As String, DestFile As String, Optional Key As String) '***************************************************************** 'Encrypts a file with XOR encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_XOR_EncryptFile '***************************************************************** Dim Filenr As Integer Dim ByteArray() As Byte

   'Make sure the source file do exist
   If (Not Encryption_Misc_FileExist(SourceFile)) Then
       Call Err.Raise(vbObjectError, , "Error in Skipjack Encryption_XOR_EncryptFile procedure (Source file does not exist).")
       Exit Sub
   End If
   'Open the source file and read the content
   'into a bytearray to pass onto encryption
   Filenr = FreeFile
   Open SourceFile For Binary Access Read As #Filenr
   ReDim ByteArray(0 To LOF(Filenr) - 1)
   Get #Filenr, , ByteArray()
   Close #Filenr
   'Encrypt the bytearray
   Call Encryption_XOR_EncryptByte(ByteArray(), Key)
   'If the destination file already exist we need
   'to delete it since opening it for binary use
   'will preserve it if it already exist
   If (Encryption_Misc_FileExist(DestFile)) Then Kill DestFile
   'Store the encrypted data in the destination file
   Filenr = FreeFile
   Open DestFile For Binary Access Write As #Filenr
   Put #Filenr, , ByteArray()
   Close #Filenr

End Sub

Public Function Encryption_XOR_EncryptString(Text As String, Optional Key As String) As String '***************************************************************** 'Encrypts a string with XOR encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_XOR_EncryptString '***************************************************************** Dim ByteArray() As Byte

   'Convert the source string into a byte array
   ByteArray() = StrConv(Text, vbFromUnicode)
   'Encrypt the byte array
   Call Encryption_XOR_EncryptByte(ByteArray(), Key)
   'Return the encrypted data as a string
   Encryption_XOR_EncryptString = StrConv(ByteArray(), vbUnicode)

End Function

Public Sub Encryption_XOR_SetKey(New_Value As String) '***************************************************************** 'Sets the encryption key for XOR encryption 'More info: http://www.vbgore.com/CommonCode.Encryptions.Encryption_XOR_SetKey '*****************************************************************

   'Do nothing if the key is buffered
   If (m_XORKeyValue = New_Value) Then Exit Sub
   'Set the new key and convert it to a
   'byte array for faster accessing later
   m_XORKeyValue = New_Value
   m_XORKeyLen = Len(New_Value)
   m_XORKey() = StrConv(m_XORKeyValue, vbFromUnicode)

End Sub </vb>

FilePaths

<vb> Option Explicit

Public DataPath As String Public Data2Path As String Public GrhPath As String Public GrhMapPath As String Public MapPath As String Public MapEXPath As String Public MusicPath As String Public ServerDataPath As String Public SfxPath As String Public MessagePath As String Public LogPath As String Public ServerTempPath As String Public SignsPath As String

Public Sub InitFilePaths() '***************************************************************** 'Set the common file paths 'More info: http://www.vbgore.com/CommonCode.FilePaths.InitFilePaths '*****************************************************************

   DataPath = App.Path & "\Data\"
   Data2Path = App.Path & "\Data2\"
   GrhPath = App.Path & "\Grh\"
   GrhMapPath = App.Path & "\GrhMapEditor\"
   MapPath = App.Path & "\Maps\"
   MapEXPath = App.Path & "\MapsEX\"
   MusicPath = App.Path & "\Music\"
   ServerDataPath = App.Path & "\ServerData\"
   SfxPath = App.Path & "\Sfx\"
   MessagePath = DataPath & "Messages\"
   SignsPath = DataPath & "Signs\"
   LogPath = App.Path & "\Logs\"
   ServerTempPath = ServerDataPath & "_temp\"

End Sub </vb>

General

<vb> Option Explicit

Public Enum LogType

   General = 0
   CodeTracker = 1
   PacketIn = 2
   PacketOut = 3
   CriticalError = 4
   InvalidPacketData = 5

End Enum

Public Type NPCTradeItems

   Name As String
   Value As Long
   GrhIndex As Long

End Type

Public NumBytesForSkills As Long

Public NPCTradeItems() As NPCTradeItems Public NPCTradeItemArraySize As Byte

Public FPSCap As Long 'The FPS cap the user defined to use (in milliseconds, not FPS)

'Used for the 64-bit timer Private GetSystemTimeOffset As Currency Private Declare Sub GetSystemTime Lib "kernel32.dll" Alias "GetSystemTimeAsFileTime" (ByRef lpSystemTimeAsFileTime As Currency)

'Sleep API - used to put a process into "idle" for X milliseconds Public Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)

'Like the Shell function, but more powerful - used to call another application to load it Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long

Public Sub Log(ByVal DummyT As String, ByVal DummyB As LogType)

'*************************************************** 'Dummy routine for logs from the server since some files are shared between multiple projects '***************************************************

End Sub

Public Function Engine_ValidChar(ByVal CharIndex As Integer) As Boolean '*************************************************** 'Checks for a valid character index - if no valid index 'is found a packet will be sent to the server requesting information 'on the character CharIndex in case they were not made for whatever reason 'More info: http://www.vbgore.com/GameClient.General.Engine_ValidChar '***************************************************

   If CharIndex <= 0 Then GoTo InvalidChar
   If CharIndex > LastChar Then GoTo InvalidChar
   If CharList(CharIndex).Active = 0 Then GoTo InvalidChar
   
   Engine_ValidChar = True
   Exit Function
   

InvalidChar:

   sndBuf.Allocate 3
   sndBuf.Put_Byte DataCode.User_RequestMakeChar
   sndBuf.Put_Integer CharIndex
   Engine_ValidChar = False
   

End Function

Public Function Engine_BuildSkinsList() As String '*************************************************** 'Returns the list of all the skins in a format for the chatbox 'More info: http://www.vbgore.com/GameClient.General.Engine_BuildSkinsList '*************************************************** Dim TempSplit() As String Dim Files() As String Dim i As Long

   'Get the list of files
   Files() = AllFilesInFolders(DataPath & "Skins\", False)
   
   'Show the header message
   Engine_AddToChatTextBuffer "The following skins are available:", FontColor_Info
   
   'Look for files ending with ".ini" only
   For i = LBound(Files) To UBound(Files)
       If Right$(Files(i), 4) = ".ini" Then
           
           'Crop out the skin name and add it to the function
           TempSplit() = Split(Files(i), "\")
           If LenB(Engine_BuildSkinsList) <> 0 Then Engine_BuildSkinsList = Engine_BuildSkinsList & vbCrLf
           Engine_BuildSkinsList = Engine_BuildSkinsList & " * |" & Left$(TempSplit(UBound(TempSplit)), Len(TempSplit(UBound(TempSplit))) - 4) & "|"
       End If
   Next i
   

End Function

Sub Game_BuildFilter() '***************************************************************** 'Creates the filtering strings from the const FilterString 'More info: http://www.vbgore.com/GameClient.General.Game_BuildFilter '***************************************************************** Dim sGroup() As String Dim sSplit() As String Dim i As Long

   'Check if we even have filtered words
   If LenB(FilterString) = 0 Then Exit Sub
   'Split up the word groups
   sGroup() = Split(FilterString, ",")
   ReDim FilterFind(0 To UBound(sGroup()))
   ReDim FilterReplace(0 To UBound(sGroup()))
   For i = 0 To UBound(sGroup())
       
       'Split up the group to get the word to search for, and the word to replace it with
       sSplit() = Split(sGroup(i), "-")
       
       'Store the values
       FilterFind(i) = Trim$(sSplit(0))
       FilterReplace(i) = Trim$(sSplit(1))
       
   Next i
   

End Sub

Function Game_FilterString(ByVal s As String) As String '***************************************************************** 'Filters a string from all illegal characters and words specified in 'the const FilterString 'More info: http://www.vbgore.com/GameClient.General.Game_FilterString '***************************************************************** Dim i As Long Dim a As Integer Dim t As String

   'Check for a legal string
   If LenB(s) = 0 Then
       Game_FilterString = s
       Exit Function
   End If
   'Filter illegal character
   For i = 1 To Len(s) - 1
       a = Asc(Mid$(s, i, 1))
       If Not Game_ValidCharacter(a) Then
           t = vbNullString
           If i > 1 Then t = t & Left$(s, i - 1)
           t = t & "X"
           If i < Len(s) - 1 Then t = t & Right$(s, Len(s) - i)
           s = t
       End If
   Next i
   'Call the swear filter
   s = Game_SwearFilterString(s)
   
   'Return the string
   Game_FilterString = s

End Function

Function Game_ClosestTargetNPC() As Integer '***************************************************************** 'Find the closest NPC to target based on the user's heading and NPC's location 'More info: http://www.vbgore.com/GameClient.General.Game_ClosestTargetNPC '***************************************************************** Dim CharValue() As Long Dim LowestValue As Long Dim LowestValueChar As Long Dim UserAngleMod As Long Dim TempAngle As Long Dim TempValue As Long Dim j As Long

   'Check for characters
   If LastChar <= 1 Then Exit Function  'If theres only one character, its probably the user
   
   'Get the initial size of the chars array
   ReDim CharValue(1 To LastChar)
   
   'Calculate the modifier of the user's heading
   Select Case CharList(UserCharIndex).Heading
       Case NORTH: UserAngleMod = 0 * 45
       Case NORTHEAST: UserAngleMod = 1 * 45
       Case EAST: UserAngleMod = 2 * 45
       Case SOUTHEAST: UserAngleMod = 3 * 45
       Case SOUTH: UserAngleMod = 4 * 45
       Case SOUTHWEST: UserAngleMod = 5 * 45
       Case WEST: UserAngleMod = 6 * 45
       Case NORTHWEST: UserAngleMod = 7 * 45
   End Select
   
   'Loop through all the characters
   For j = 1 To LastChar
   
       'Make sure the character is used
       If CharList(j).Active Then
           If j <> UserCharIndex Then
               If j <> TargetCharIndex Then
                   If CharList(j).CharType = ClientCharType_NPC Then
                       
                       'Check that the character is in the screen
                       If CharList(j).Pos.X > ScreenMinX Then
                           If CharList(j).Pos.X < ScreenMaxX Then
                               If CharList(j).Pos.Y > ScreenMinY Then
                                   If CharList(j).Pos.Y < ScreenMaxY Then
                                       
                                       'Get the angle between the user and the NPC
                                       TempAngle = -UserAngleMod + Engine_GetAngle(CharList(UserCharIndex).Pos.X, CharList(UserCharIndex).Pos.Y, CharList(j).Pos.X, CharList(j).Pos.Y)
                                       
                                       'Make sure the angle is between 0 and 360
                                       Do While TempAngle >= 360
                                           TempAngle = TempAngle - 360
                                       Loop
                                       Do While TempAngle < 0
                                           TempAngle = TempAngle + 360
                                       Loop
                                       
                                       'Check that the angle is less between -95 and 95 (not behind them)
                                       If TempAngle < 95 Or TempAngle > 265 Then
                                           
                                           'Convert the angle to the distance from 0 degrees
                                           If TempAngle > 180 Then TempAngle = Abs(360 - TempAngle)
                                           If TempAngle = 360 Then TempAngle = 0
   
                                           'Calculate the value of the character
                                           'Value = Angle * 2 + Distance
                                           TempValue = (TempAngle * 0.5) + Engine_Distance(CharList(UserCharIndex).Pos.X, CharList(UserCharIndex).Pos.Y, CharList(j).Pos.X, CharList(j).Pos.Y)
                                           
                                           'Check if this value is lower then the first value
                                           If LowestValue = 0 Then
                                               LowestValue = TempValue
                                               LowestValueChar = j
                                           Else
                                               If LowestValue > TempValue Then
                                                   LowestValue = TempValue
                                                   LowestValueChar = j
                                               End If
                                           End If
                                           
                                       End If
                                   
                                   End If
                               End If
                           End If
                       End If
                   
                   End If
               End If
           End If
       End If
   
   Next j
   
   'Return the index of the character with the lowest value (best target)
   Game_ClosestTargetNPC = LowestValueChar

End Function

Function Game_SwearFilterString(ByVal s As String) As String '***************************************************************** 'Checks the passed string for any swear words to filter out 'More info: http://www.vbgore.com/GameClient.General.Game_SwearFilterString '***************************************************************** Dim i As Long

   'Check if we even have filtered words
   If LenB(FilterString) = 0 Then
       Game_SwearFilterString = s
       Exit Function
   End If
   'Loop through all the filters
   For i = 0 To UBound(FilterFind())
       s = Replace$(s, FilterFind(i), FilterReplace(i))
   Next i
   
   'Return the string
   Game_SwearFilterString = s

End Function

Function Game_CheckUserData() As Boolean '***************************************************************** 'Checks all user data for mistakes and reports any errors 'More info: http://www.vbgore.com/GameClient.General.Game_CheckUserData '*****************************************************************

   'Password
   If Len(UserPassword) < 3 Then
       MsgBox ("Password box is empty.")
       Exit Function
   End If
   If Len(UserPassword) > 10 Then
       MsgBox ("Password must be 10 characters or less.")
       Exit Function
   End If
   If Game_LegalString(UserPassword) = False Then
       MsgBox ("Invalid Password.")
       Exit Function
   End If
   
   'Name
   If Len(UserName) < 3 Then
       MsgBox ("Name box is empty.")
       Exit Function
   End If
   If Len(UserName) > 10 Then
       MsgBox ("Name must be 10 characters or less.")
       Exit Function
   End If
   If Game_LegalString(UserName) = False Then
       MsgBox ("Invalid Name.")
       Exit Function
   End If
   
   'If all good send true
   Game_CheckUserData = True

End Function

Function Game_ClickItem(ByVal ItemIndex As Byte, Optional ByVal InventoryType As Long = 1) As Long '*************************************************** 'Selects the item clicked if it's valid and return's its index 'More info: http://www.vbgore.com/GameClient.General.Game_ClickItem '***************************************************

   'Make sure item index is within limits
   If ItemIndex <= 0 Then Exit Function
   If ItemIndex > MAX_INVENTORY_SLOTS Then Exit Function
   
   'Check by the appropriate window
   Select Case InventoryType
       
       'User inventory
       Case 1
           If UserInventory(ItemIndex).GrhIndex > 0 Then Game_ClickItem = 1
           
       'Shop inventory
       Case 2
           If NPCTradeItems(ItemIndex).GrhIndex > 0 Then Game_ClickItem = 1
       
       'Bank depot
       Case 3
           If UserBank(ItemIndex).GrhIndex > 0 Then Game_ClickItem = 1
           
   End Select

End Function

Function Game_ValidCharacter(ByVal KeyAscii As Byte) As Boolean '***************************************************************** 'Only allow certain specified characters (this is used for chat/etc) 'Make sure you update the server's Server_ValidCharacter, too! 'More info: http://www.vbgore.com/GameClient.General.Game_ValidCharacter '*****************************************************************

   Log "Call Game_ValidCharacter(" & KeyAscii & ")", CodeTracker '//\\LOGLINE//\\
   'Remove bad characters
   If KeyAscii >= 32 Then Game_ValidCharacter = True

End Function

Function Game_LegalCharacter(ByVal KeyAscii As Byte) As Boolean '***************************************************************** 'Only allow certain specified characters (this is for username/pass) 'Make sure you update the server's Server_LegalCharacter, too! 'More info: http://www.vbgore.com/GameClient.General.Game_LegalCharacter '*****************************************************************

   On Error GoTo ErrOut
   'Allow numbers between 0 and 9
   If KeyAscii >= 48 Then
       If KeyAscii <= 57 Then
           Game_LegalCharacter = True
           Exit Function
       End If
   End If
   
   'Allow characters A to Z
   If KeyAscii >= 65 Then
       If KeyAscii <= 90 Then
           Game_LegalCharacter = True
           Exit Function
       End If
   End If
   
   'Allow characters a to z
   If KeyAscii >= 97 Then
       If KeyAscii <= 122 Then
           Game_LegalCharacter = True
           Exit Function
       End If
   End If
   
   'Allow foreign characters
   If KeyAscii >= 128 Then
       If KeyAscii <= 168 Then
           Game_LegalCharacter = True
           Exit Function
       End If
   End If
   

Exit Function

ErrOut:

   'Something bad happened, so the character must be invalid
   Game_LegalCharacter = False
   

End Function

Function Game_ValidString(ByVal CheckString As String) As Boolean '***************************************************************** 'Check for illegal characters in the string (wrapper for Game_ValidCharacter) 'More info: http://www.vbgore.com/GameClient.General.Game_ValidString '***************************************************************** Dim i As Long

   On Error GoTo ErrOut
   'Check for invalid string
   If CheckString = vbNullChar Then Exit Function
   If LenB(CheckString) < 1 Then Exit Function
   'Loop through the string
   For i = 1 To Len(CheckString)
       
       'Check the values
       If Game_ValidCharacter(AscB(Mid$(CheckString, i, 1))) = False Then Exit Function
       
   Next i
   
   'If we have made it this far, then all is good
   Game_ValidString = True

Exit Function

ErrOut:

   'Something bad happened, so the string must be invalid
   Game_ValidString = False

End Function

Function Game_LegalString(ByVal CheckString As String) As Boolean '***************************************************************** 'Check for illegal characters in the string (wrapper for Server_LegalCharacter) 'More info: http://www.vbgore.com/GameClient.General.Game_LegalString '***************************************************************** Dim i As Long

   On Error GoTo ErrOut
   'Check for invalid string
   If CheckString = vbNullChar Then Exit Function
   If LenB(CheckString) < 1 Then Exit Function
   'Loop through the string
   For i = 1 To Len(CheckString)
       
       'Check the values
       If Game_LegalCharacter(AscB(Mid$(CheckString, i, 1))) = False Then Exit Function
       
   Next i
   
   'If we have made it this far, then all is good
   Game_LegalString = True

Exit Function

ErrOut:

   'Something bad happened, so the string must be invalid
   Game_LegalString = False

End Function

Public Sub Game_Config_Load() '*************************************************** 'Load the user configuration (used skin and quickbar values) 'More info: http://www.vbgore.com/GameClient.General.Game_Config_Load '*************************************************** Dim i As Byte

   'Quickbar
   For i = 1 To 12
       QuickBarID(i).ID = Val(Var_Get(DataPath & "Game.ini", "QUICKBARVALUES", "Slot" & i & "ID"))
       QuickBarID(i).Type = Val(Var_Get(DataPath & "Game.ini", "QUICKBARVALUES", "Slot" & i & "Type"))
   Next i
   
   'Skin
   CurrentSkin = Var_Get(DataPath & "Game.ini", "INIT", "CurrentSkin")

End Sub

Sub Game_Map_Switch(Map As Integer) '***************************************************************** 'Loads a new map and switches to it 'More info: http://www.vbgore.com/GameClient.General.Game_Map_Switch '***************************************************************** Dim LargestTileSize As Long Dim MapBuf As DataBuffer Dim GetParticleCount As Integer Dim GetEffectNum As Byte Dim GetDirection As Integer Dim GetGfx As Byte Dim GetX As Integer Dim GetY As Integer Dim ByFlags As Long Dim MapNum As Byte Dim i As Integer Dim Y As Byte Dim X As Byte Dim b() As Byte Dim TempInt As Integer

   'Check if there was a map before this one - if so, clear it up
   If MapInfo.Width > 0 Then
       'Clear the offset values for the particle engine
       ParticleOffsetX = 0
       ParticleOffsetY = 0
       LastOffsetX = 0
       LastOffsetY = 0
   
       'Reset the user's position (it won't be drawn at 0,0 since it is an invalid position anyways)
       UserPos.X = 0
       UserPos.Y = 0
   
       'Erase characters
       LastChar = 0
       Erase CharList
       
       'Erase damage
       LastDamage = 0
       Erase DamageList
   
       'Erase objects
       LastObj = 0
       Erase OBJList
       
       'Erase particle effects
       LastEffect = 0
       ReDim Effect(1 To NumEffects)
   End If
   'Open map file
   MapNum = FreeFile
   Open MapPath & Map & ".map" For Binary As #MapNum
       Seek #MapNum, 1
       
       'Store the data in the buffer
       ReDim b(0 To LOF(MapNum) - 1)
       Get #MapNum, , b()
       
   'Close the map file
   Close #MapNum
   
   'Assign the buffer data
   Set MapBuf = New DataBuffer
   MapBuf.Set_Buffer b()
   
   'Clear the data array (since its now in the buffer)
   Erase b()
   'Map Header
   TempInt = MapBuf.Get_Integer    'Not stored in memory
   MapInfo.Width = MapBuf.Get_Byte
   MapInfo.Height = MapBuf.Get_Byte
   
   'Resize mapdata array
   ReDim MapData(1 To MapInfo.Width, 1 To MapInfo.Height) As MapBlock
   'Resize the save light buffer
   ReDim SaveLightBuffer(1 To MapInfo.Width, 1 To MapInfo.Height)
   
   'Load arrays
   For Y = 1 To MapInfo.Height
       For X = 1 To MapInfo.Width
       
           'Clear the graphic layers
           For i = 1 To 6
               MapData(X, Y).Graphic(i).GrhIndex = 0
           Next i
           'Get flag's byte
           ByFlags = MapBuf.Get_Long
           'Blocked
           If ByFlags And 1 Then MapData(X, Y).Blocked = MapBuf.Get_Byte Else MapData(X, Y).Blocked = 0
           'Graphic layers
           If ByFlags And 2 Then
               MapData(X, Y).Graphic(1).GrhIndex = MapBuf.Get_Long
               Engine_Init_Grh MapData(X, Y).Graphic(1), MapData(X, Y).Graphic(1).GrhIndex
               
               'Find the size of the largest tile used
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(1).GrhIndex).pixelWidth Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(1).GrhIndex).pixelWidth
               End If
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(1).GrhIndex).pixelHeight Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(1).GrhIndex).pixelHeight
               End If
               
           End If
           If ByFlags And 4 Then
               MapData(X, Y).Graphic(2).GrhIndex = MapBuf.Get_Long
               Engine_Init_Grh MapData(X, Y).Graphic(2), MapData(X, Y).Graphic(2).GrhIndex
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(2).GrhIndex).pixelWidth Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(2).GrhIndex).pixelWidth
               End If
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(2).GrhIndex).pixelHeight Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(2).GrhIndex).pixelHeight
               End If
           End If
           If ByFlags And 8 Then
               MapData(X, Y).Graphic(3).GrhIndex = MapBuf.Get_Long
               Engine_Init_Grh MapData(X, Y).Graphic(3), MapData(X, Y).Graphic(3).GrhIndex
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(3).GrhIndex).pixelWidth Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(3).GrhIndex).pixelWidth
               End If
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(3).GrhIndex).pixelHeight Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(3).GrhIndex).pixelHeight
               End If
           End If
           If ByFlags And 16 Then
               MapData(X, Y).Graphic(4).GrhIndex = MapBuf.Get_Long
               Engine_Init_Grh MapData(X, Y).Graphic(4), MapData(X, Y).Graphic(4).GrhIndex
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(4).GrhIndex).pixelWidth Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(4).GrhIndex).pixelWidth
               End If
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(4).GrhIndex).pixelHeight Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(4).GrhIndex).pixelHeight
               End If
           End If
           If ByFlags And 32 Then
               MapData(X, Y).Graphic(5).GrhIndex = MapBuf.Get_Long
               Engine_Init_Grh MapData(X, Y).Graphic(5), MapData(X, Y).Graphic(5).GrhIndex
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(5).GrhIndex).pixelWidth Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(5).GrhIndex).pixelWidth
               End If
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(5).GrhIndex).pixelHeight Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(5).GrhIndex).pixelHeight
               End If
           End If
           If ByFlags And 64 Then
               MapData(X, Y).Graphic(6).GrhIndex = MapBuf.Get_Long
               Engine_Init_Grh MapData(X, Y).Graphic(6), MapData(X, Y).Graphic(6).GrhIndex
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(6).GrhIndex).pixelWidth Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(6).GrhIndex).pixelWidth
               End If
               If LargestTileSize < GrhData(MapData(X, Y).Graphic(6).GrhIndex).pixelHeight Then
                   LargestTileSize = GrhData(MapData(X, Y).Graphic(6).GrhIndex).pixelHeight
               End If
           End If
           
           'Set light to default (-1) - it will be set again if it is not -1 from the code below
           For i = 1 To 24
               MapData(X, Y).Light(i) = -1
           Next i
           
           'Get lighting values
           If ByFlags And 128 Then
               For i = 1 To 4
                   MapData(X, Y).Light(i) = MapBuf.Get_Long
               Next i
           End If
           If ByFlags And 256 Then
               For i = 5 To 8
                   MapData(X, Y).Light(i) = MapBuf.Get_Long
               Next i
           End If
           If ByFlags And 512 Then
               For i = 9 To 12
                   MapData(X, Y).Light(i) = MapBuf.Get_Long
               Next i
           End If
           If ByFlags And 1024 Then
               For i = 13 To 16
                   MapData(X, Y).Light(i) = MapBuf.Get_Long
               Next i
           End If
           If ByFlags And 2048 Then
               For i = 17 To 20
                   MapData(X, Y).Light(i) = MapBuf.Get_Long
               Next i
           End If
           If ByFlags And 4096 Then
               For i = 21 To 24
                   MapData(X, Y).Light(i) = MapBuf.Get_Long
               Next i
           End If
           'Store the lighting in the SaveLightBuffer
           For i = 1 To 24
               SaveLightBuffer(X, Y).Light(i) = MapData(X, Y).Light(i)
           Next i
           'Mailbox - Not used by the client
           'If ByFlags And 8192 Then MapData(X, Y).Mailbox = 1 Else MapData(X, Y).Mailbox = 0
           'Shadows
           If ByFlags And 16384 Then MapData(X, Y).Shadow(1) = 1 Else MapData(X, Y).Shadow(1) = 0
           If ByFlags And 32768 Then MapData(X, Y).Shadow(2) = 1 Else MapData(X, Y).Shadow(2) = 0
           If ByFlags And 65536 Then MapData(X, Y).Shadow(3) = 1 Else MapData(X, Y).Shadow(3) = 0
           If ByFlags And 131072 Then MapData(X, Y).Shadow(4) = 1 Else MapData(X, Y).Shadow(4) = 0
           If ByFlags And 262144 Then MapData(X, Y).Shadow(5) = 1 Else MapData(X, Y).Shadow(5) = 0
           If ByFlags And 524288 Then MapData(X, Y).Shadow(6) = 1 Else MapData(X, Y).Shadow(6) = 0
           
           'Clear any old sfx
           If Not MapData(X, Y).Sfx Is Nothing Then
               MapData(X, Y).Sfx.Stop
               Set MapData(X, Y).Sfx = Nothing
           End If
           
           'Set the sfx
           If ByFlags And 1048576 Then
               i = MapBuf.Get_Integer
               Sound_SetToMap i, X, Y
           End If
           
           'Blocked attack
           If ByFlags And 2097152 Then MapData(X, Y).BlockedAttack = 1 Else MapData(X, Y).BlockedAttack = 0
           
           'Sign
           If ByFlags And 4194304 Then MapData(X, Y).Sign = MapBuf.Get_Integer Else MapData(X, Y).Sign = 0
           
           'If there is a warp
           If ByFlags And 8388608 Then MapData(X, Y).Warp = 1 Else MapData(X, Y).Warp = 0
       Next X
   Next Y
   
   'Get the number of effects
   Y = MapBuf.Get_Byte
   'Store the individual particle effect types
   If Y > 0 Then
       For X = 1 To Y
           GetEffectNum = MapBuf.Get_Byte
           GetX = MapBuf.Get_Integer
           GetY = MapBuf.Get_Integer
           GetParticleCount = MapBuf.Get_Integer
           GetGfx = MapBuf.Get_Byte
           GetDirection = MapBuf.Get_Integer
           Effect_Begin GetEffectNum, GetX, GetY, GetGfx, GetParticleCount, GetDirection
       Next X
   End If
   
   'Clear the map data
   Set MapBuf = Nothing
   
   'Create the minimap
   Engine_BuildMiniMap
   'Clear out old mapinfo variables
   MapInfo.Name = vbNullString
   'Set current map
   CurMap = Map
   
   'Auto-calculate the maximum size to set the tile buffer
   LargestTileSize = LargestTileSize + (32 - (LargestTileSize Mod 32)) 'Round to the next highest factor of 32
   TileBufferSize = (LargestTileSize \ 32) 'Divide into tiles
   
   'Force to 2 to draw characters since they are 2 tiles tall
   'If you have characters or paperdoll parts > 64 pixels in width or high, you need to increase this
   If TileBufferSize < 2 Then TileBufferSize = 2
   
   'Cache the TileBufferOffset value to prevent always having to calculate it on the fly
   TileBufferOffset = ((10 - TileBufferSize) * 32)

End Sub

Public Sub Game_Config_Save() '*************************************************** 'Saves the user configuration (quickbar, skin and skin position) 'More info: http://www.vbgore.com/GameClient.General.Game_Config_Save '*************************************************** Dim t As String Dim i As Byte

   'Quickbar
   For i = 1 To 12
       Var_Write DataPath & "Game.ini", "QUICKBARVALUES", "Slot" & i & "ID", Str$(QuickBarID(i).ID)
       Var_Write DataPath & "Game.ini", "QUICKBARVALUES", "Slot" & i & "Type", Str$(QuickBarID(i).Type)
   Next i
   
   'Skin
   Var_Write DataPath & "Game.ini", "INIT", "CurrentSkin", CurrentSkin
   
   'Skin positions
   t = DataPath & "Skins\" & CurrentSkin & ".dat"   'Set the custom positions file for the skin
   With GameWindow
       Var_Write t, "QUICKBAR", "ScreenX", Str$(.QuickBar.Screen.X)
       Var_Write t, "QUICKBAR", "ScreenY", Str$(.QuickBar.Screen.Y)
       Var_Write t, "CHATWINDOW", "ScreenX", Str$(.ChatWindow.Screen.X)
       Var_Write t, "CHATWINDOW", "ScreenY", Str$(.ChatWindow.Screen.Y)
       Var_Write t, "INVENTORY", "ScreenX", Str$(.Inventory.Screen.X)
       Var_Write t, "INVENTORY", "ScreenY", Str$(.Inventory.Screen.Y)
       Var_Write t, "SHOP", "ScreenX", Str$(.Shop.Screen.X)
       Var_Write t, "SHOP", "ScreenY", Str$(.Shop.Screen.Y)
       Var_Write t, "MAILBOX", "ScreenX", Str$(.Mailbox.Screen.X)
       Var_Write t, "MAILBOX", "ScreenY", Str$(.Mailbox.Screen.Y)
       Var_Write t, "VIEWMESSAGE", "ScreenX", Str$(.ViewMessage.Screen.X)
       Var_Write t, "VIEWMESSAGE", "ScreenY", Str$(.ViewMessage.Screen.Y)
       Var_Write t, "WRITEMESSAGE", "ScreenX", Str$(.WriteMessage.Screen.X)
       Var_Write t, "WRITEMESSAGE", "ScreenY", Str$(.WriteMessage.Screen.Y)
       Var_Write t, "AMOUNT", "ScreenX", Str$(.Amount.Screen.X)
       Var_Write t, "AMOUNT", "ScreenY", Str$(.Amount.Screen.Y)
       Var_Write t, "MENU", "ScreenX", Str$(.Menu.Screen.X)
       Var_Write t, "MENU", "ScreenY", Str$(.Menu.Screen.Y)
       Var_Write t, "BANK", "ScreenX", Str$(.Bank.Screen.X)
       Var_Write t, "BANK", "ScreenY", Str$(.Bank.Screen.Y)
       Var_Write t, "NPCCHAT", "ScreenX", Str$(.NPCChat.Screen.X)
       Var_Write t, "NPCCHAT", "ScreenY", Str$(.NPCChat.Screen.Y)
   End With

End Sub

Sub UpdateShownTextBuffer() '***************************************************************** 'Updates the ShownTextBuffer (the text displayed written into the text input box) 'More info: http://www.vbgore.com/GameClient.General.UpdateShownTextBuffer '***************************************************************** Dim X As Long Dim j As Long

   'Check if the width is larger then the screen
   If EnterTextBufferWidth > GameWindow.ChatWindow.Text.Width - 24 Then
       
       'Loop through the characters backwards
       For X = Len(EnterTextBuffer) To 1 Step -1
           
           'Add up the size
           j = j + Font_Default.HeaderInfo.CharWidth(Asc(Mid$(EnterTextBuffer, X, 1)))
           
           'Check if the size has become too large
           If j > GameWindow.ChatWindow.Text.Width - 24 Then
           
               'If the size has become too large, the character before (since we are looping backwards, it is + 1) is the limit
               ShownText = Right$(EnterTextBuffer, Len(EnterTextBuffer) - X + 1)
               Exit For
               
           End If
       Next X
   Else
   
       'Set the shown text buffer to the full buffer
       ShownText = EnterTextBuffer
   
   End If

End Sub

Sub Main() '***************************************************************** 'The entry-point for the client - gets the client ready and handles 'the main game loop that runs the whole time the client is running 'More info: http://www.vbgore.com/GameClient.General.Main '***************************************************************** Dim KeyClearTime As Long Dim PacketKeys() As String Dim LastUnloadTime As Long Dim StartTime As Long Dim i As Integer

   'Set the high-resolution timer
   timeBeginPeriod 1
   'Init file paths
   InitFilePaths
   
   'Load frmMain
   Load frmMain
   frmMain.Hide
   DoEvents
   'Check if we need to run the updater
   If ForceUpdateCheck Then
   
       'Check for the right parameter
       If Command$ <> "-sdf@041jkdf0)21`~" Then
           'Force the creation of frmConnect, thus forcing the creation of its hWnd
           Load frmConnect
           frmConnect.Show
           frmConnect.Hide
           
           'Load the updater
           ShellExecute frmConnect.hwnd, vbNullString, App.Path & "\UpdateClient.exe", vbNullString, vbNullString, 1   'The 1 means "show normal"
   
           'Unload the client
           Engine_UnloadAllForms
           End
       
       End If
   End If
   
   'Generate the packet keys
   GenerateEncryptionKeys PacketKeys
   frmMain.GOREsock.ClearPicture
   frmMain.GOREsock.SetEncryption PacketEncTypeServerIn, PacketEncTypeServerOut, PacketKeys()
   Erase PacketKeys
   
   'Number of bytes required to fill the skills
   NumBytesForSkills = Int((NumSkills - 1) / 8) + 1
   
   'Load the font information
   Engine_Init_FontSettings
   
   'Load the messages
   Engine_Init_Messages LCase$(Var_Get(DataPath & "Game.ini", "INIT", "Language"))
   Engine_Init_Signs LCase$(Var_Get(DataPath & "Game.ini", "INIT", "Language"))
   
   'Fill startup variables for the tile engine
   EnterTextBufferWidth = 1
   ReDim SkillListIDs(1 To NumSkills)
   'Set intial user position
   UserPos.X = 1
   UserPos.Y = 1
   
   'Set scroll pixels per frame
   ShowGameWindow(QuickBarWindow) = 1
   ShowGameWindow(ChatWindow) = 1
   'Set the array sizes by the number of graphic files
   NumGrhFiles = CLng(Var_Get(DataPath & "Grh.ini", "INIT", "NumGrhFiles"))
   ReDim SurfaceDB(1 To NumGrhFiles)
   ReDim SurfaceSize(1 To NumGrhFiles)
   ReDim SurfaceTimer(1 To NumGrhFiles)
   
   'Load graphic data into memory
   Engine_Init_GrhData
   Engine_Init_BodyData
   Engine_Init_WeaponData
   Engine_Init_WingData
   Engine_Init_HeadData
   Engine_Init_HairData
   
   'Load the config
   Game_Config_Load
   Engine_Init_GUI
   'Create the buffer
   Set sndBuf = New DataBuffer
   sndBuf.Clear
   'Set the form starting positions
   DoEvents
   'Load the data commands
   InitDataCommands
   
   'Build the word filters
   Game_BuildFilter
   'Display connect window
   frmConnect.Visible = True
   'Main Loop
   Do
   
       'Calculate the starttime - this is the absolute time it takes from start to finish, disincluding DoEvents
       ' The idea is that it works just like the ElapsedTime, but in slightly different placing
       StartTime = timeGetTime
   
       'Check if unloading
       If IsUnloading = 1 Then Exit Do
       
       'Clear the key cache
       If KeyClearTime < timeGetTime Then
           Input_Keys_ClearQueue
           KeyClearTime = timeGetTime + 200
       End If
       
       'Don't draw frame is window is minimized or there is no map loaded
       If frmMain.WindowState <> 1 Then
           If CurMap > 0 Then
               'Show the next frame
               Engine_ShowNextFrame
               'Check for key inputs
               Input_Keys_General
               
               'Keep the music looping
               If MapInfo.Music > 0 Then Music_Loop 1
           End If
       End If
       
       'Perform the following only if the connection to the server is open
       If SocketOpen Then
           
           'Send the data buffer
           Data_Send
           
           'Check the time since the last packet arrived
           If timeGetTime - LastServerPacketTime > 6000 Then
           
               'No response from the server in 5 seconds, must be disconnected :(
               IsUnloading = 1
               
           End If
           
       End If
       'Check to unload stuff from memory (only check every 5 seconds)
       If LastUnloadTime < timeGetTime Then
           For i = 1 To NumGrhFiles    'Check to unload surfaces
               If SurfaceTimer(i) > 0 Then 'Only update surfaces in use
                   If SurfaceTimer(i) < timeGetTime Then   'Unload the surface
                       Set SurfaceDB(i) = Nothing
                       SurfaceTimer(i) = 0
                   End If
               End If
           Next i
           For i = 1 To NumSfx 'Check to unload sound buffers
               If SoundBufferTimer(i) > 0 Then 'Only update sound buffers in use
                   If SoundBufferTimer(i) < timeGetTime Then   'Unload the sound buffer
                       Set DSBuffer(i) = Nothing
                       SoundBufferTimer(i) = 0
                   End If
               End If
           Next i
           LastUnloadTime = timeGetTime + 10000 'States we will check the unload routine again in 10 seconds
       End If
       
       'Check to change servers
       If SocketMoveToPort > 0 Then
           If frmMain.GOREsock.ShutDown <> soxERROR Then
               
               'Set up the socket
               'Leave the GetIPFromHost() wrapper there, this will convert a host name to IP if needed, or leave it as an IP if you pass an IP
               SoxID = frmMain.GOREsock.Connect(GetIPFromHost(SocketMoveToIP), SocketMoveToPort)
               SocketOpen = 1
               
               'If the SoxID = -1, then the connection failed, elsewise, we're good to go! W00t! ^_^
               If SoxID = -1 Then
                   MsgBox "Unable to connect to the game server!" & vbCrLf & "Either the server is down or you are not connected to the internet.", vbOKOnly Or vbCritical
                   IsUnloading = 1
               Else
                   frmMain.GOREsock.SetOption SoxID, soxSO_TCP_NODELAY, True
               End If
               
               'Clear the temp values
               SocketMoveToPort = 0
               SocketMoveToIP = vbNullString
               
           End If
       End If
       'Do other events
       DoEvents
       
       'Do sleep event - force FPS at the FPS cap
       If Not frmMain.Visible Then
           Sleep 100   'Don't hog resources at connect screen
       Else
           If FPSCap > 0 Then
               If (timeGetTime - StartTime) < FPSCap Then  'If Elapsed Time < Time required for requested highest fps
                   Sleep FPSCap - (timeGetTime - StartTime)
               End If
           End If
       End If
   Loop
   
   'Save the config
   Game_Config_Save
   
   'Close down
   frmMain.ShutdownTimer.Enabled = True

End Sub

Function Var_Get(ByVal File As String, ByVal Main As String, ByVal Var As String) As String '***************************************************************** 'Gets a string from a text file 'More info: http://www.vbgore.com/GameClient.General.Var_Get '*****************************************************************

   Var_Get = Space$(1000)
   getprivateprofilestring Main, Var, vbNullString, Var_Get, 1000, File
   Var_Get = RTrim$(Var_Get)
   If LenB(Var_Get) <> 0 Then Var_Get = Left$(Var_Get, Len(Var_Get) - 1)

End Function

Sub Var_Write(ByVal File As String, ByVal Main As String, ByVal Var As String, ByVal Value As String) '***************************************************************** 'Writes a string to a text file 'More info: http://www.vbgore.com/GameClient.General.Var_Write '*****************************************************************

   writeprivateprofilestring Main, Var, Value, File

End Sub

Public Function Engine_WordWrap(ByVal Text As String, ByVal MaxLineLen As Integer) As String '************************************************************ 'Wrap a long string to multiple lines by vbNewLine 'More info: http://www.vbgore.com/GameClient.General.Engine_WordWrap '************************************************************ Dim TempSplit() As String Dim TSLoop As Long Dim LastSpace As Long Dim Size As Long Dim i As Long Dim b As Long

   'Too small of text
   If Len(Text) < 2 Then
       Engine_WordWrap = Text
       Exit Function
   End If
   'Check if there are any line breaks - if so, we will support them
   TempSplit = Split(Text, vbNewLine)
   
   For TSLoop = 0 To UBound(TempSplit)
   
       'Clear the values for the new line
       Size = 0
       b = 1
       LastSpace = 1
       
       'Add back in the vbNewLines
       If TSLoop < UBound(TempSplit()) Then TempSplit(TSLoop) = TempSplit(TSLoop) & vbNewLine
       
       'Only check lines with a space
       If InStr(1, TempSplit(TSLoop), " ") Or InStr(1, TempSplit(TSLoop), "-") Or InStr(1, TempSplit(TSLoop), "_") Then
           
           'Loop through all the characters
           For i = 1 To Len(TempSplit(TSLoop))
           
               'If it is a space, store it so we can easily break at it
               Select Case Mid$(TempSplit(TSLoop), i, 1)
                   Case " ": LastSpace = i
                   Case "_": LastSpace = i
                   Case "-": LastSpace = i
               End Select
   
               'Add up the size - Do not count the "|" character (high-lighter)!
               If Not Mid$(TempSplit(TSLoop), i, 1) = "|" Then
                   Size = Size + Font_Default.HeaderInfo.CharWidth(Asc(Mid$(TempSplit(TSLoop), i, 1)))
               End If
               
               'Check for too large of a size
               If Size > MaxLineLen Then
                   
                   'Check if the last space was too far back
                   If i - LastSpace > 4 Then
                       
                       'Too far away to the last space, so break at the last character
                       Engine_WordWrap = Engine_WordWrap & Trim$(Mid$(TempSplit(TSLoop), b, (i - 1) - b)) & vbNewLine
                       b = i - 1
                       Size = 0
                       
                   Else
                   
                       'Break at the last space to preserve the word
                       Engine_WordWrap = Engine_WordWrap & Trim$(Mid$(TempSplit(TSLoop), b, LastSpace - b)) & vbNewLine
                       b = LastSpace + 1
                       
                       'Count all the words we ignored (the ones that weren't printed, but are before "i")
                       Size = Engine_GetTextWidth(Font_Default, Mid$(TempSplit(TSLoop), LastSpace, i - LastSpace))
                       
                   End If
                   
               End If
               
               'This handles the remainder
               If i = Len(TempSplit(TSLoop)) Then
                   If b <> i Then
                       Engine_WordWrap = Engine_WordWrap & Mid$(TempSplit(TSLoop), b, i)
                   End If
               End If
               
           Next i
           
       Else
       
           Engine_WordWrap = Engine_WordWrap & TempSplit(TSLoop)
       
       End If
       
   Next TSLoop

End Function </vb>

Input

<vb> Option Explicit

Public DI As DirectInput8 Public DIDevice As DirectInputDevice8 Public MousePos As POINTAPI Public MousePosAdd As POINTAPI Public MouseEvent As Long Public MouseLeftDown As Byte Public MouseRightDown As Byte

Private Const KeyPress_Shift As Integer = 2 ^ 12 Private Const KeyPress_Control As Integer = 2 ^ 13 Private Const KeyPress_Alt As Integer = 2 ^ 14

Private Type KeyDefinitions

   MiniMap As Integer
   PickUpObj As Integer
   QuickBar(1 To 12) As Integer
   Attack As Integer
   ChatBufferUp As Integer
   ChatBufferDown As Integer
   InventoryWindow As Integer
   QuickBarWindow As Integer
   ChatWindow As Integer
   StatWindow As Integer
   MenuWindow As Integer
   ZoomIn As Integer
   ZoomOut As Integer
   MoveNorth As Integer
   MoveEast As Integer
   MoveSouth As Integer
   MoveWest As Integer
   ResetGUI As Integer
   QuickTarget As Integer
   QuickReply As Integer

End Type Private KeyDefinitions As KeyDefinitions

Private IgnoreNextChatKey As Boolean 'Used to ignore the next keystroke going into the chat buffer (for pressing the quick-reply button)

Private Function Input_Keys_IsPressed(ByVal DefinitionValue As Integer, ByVal KeyCode As Integer) As Boolean '***************************************************************** 'Checks if the definition requirements are met - used to check if a defineable 'key or series of keys (such as Shift + A) have been pressed 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_IsPressed '***************************************************************** Dim CheckForInput As Boolean

   CheckForInput = True
   'Check for shift, alt and control requirements
   If DefinitionValue And KeyPress_Shift Then
       If GetAsyncKeyState(16) = 0 Then Exit Function
   End If
   If DefinitionValue And KeyPress_Control Then
       If GetAsyncKeyState(17) = 0 Then Exit Function
       CheckForInput = False   'No need to check for input if control is pressed
   End If
   If DefinitionValue And KeyPress_Alt Then
       If GetAsyncKeyState(18) = 0 Then Exit Function
       CheckForInput = False   'No need to check for input if alt is pressed
   End If
   
   'Remove the shift, alt and control bits, then check for the keycode requirements
   If (DefinitionValue And 2047) <> KeyCode Then Exit Function
   
   'Check for input boxes being active so we don't run commands when typing
   If CheckForInput Then
   
       'Typing in the chat buffer
       If EnterText Then Exit Function
   
       'Writing a message in the mail window
       If LastClickedWindow = WriteMessageWindow Then
           If ShowGameWindow(WriteMessageWindow) <> 0 Then Exit Function
       End If
       
       'Numeric only
       If Input_Keys_IsNumeric(KeyCode) Then
           
           'Entering a value in the amount window
           If LastClickedWindow = AmountWindow Then
               If ShowGameWindow(AmountWindow) <> 0 Then Exit Function
           End If
           
           'Entering a number on the NPC chat window
           If LastClickedWindow = NPCChatWindow Then
               If ShowGameWindow(NPCChatWindow) <> 0 Then Exit Function
           End If
           
       End If
   End If
   'Every test has been passed
   Input_Keys_IsPressed = True

End Function

Public Sub Input_Keys_LoadDefinitions() '***************************************************************** 'Load the key definitions for defineable keys made by GameConfig.exe 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_LoadDefinitions '***************************************************************** Dim i As Long

   KeyDefinitions.Attack = Val(Var_Get(DataPath & "Game.ini", "INPUT", "Attack"))
   KeyDefinitions.ChatBufferDown = Val(Var_Get(DataPath & "Game.ini", "INPUT", "ChatBufferDown"))
   KeyDefinitions.ChatBufferUp = Val(Var_Get(DataPath & "Game.ini", "INPUT", "ChatBufferUp"))
   KeyDefinitions.ChatWindow = Val(Var_Get(DataPath & "Game.ini", "INPUT", "ChatWindow"))
   KeyDefinitions.InventoryWindow = Val(Var_Get(DataPath & "Game.ini", "INPUT", "InventoryWindow"))
   KeyDefinitions.MenuWindow = Val(Var_Get(DataPath & "Game.ini", "INPUT", "MenuWindow"))
   KeyDefinitions.MiniMap = Val(Var_Get(DataPath & "Game.ini", "INPUT", "MiniMap"))
   KeyDefinitions.MoveEast = Val(Var_Get(DataPath & "Game.ini", "INPUT", "MoveEast"))
   KeyDefinitions.MoveNorth = Val(Var_Get(DataPath & "Game.ini", "INPUT", "MoveNorth"))
   KeyDefinitions.MoveSouth = Val(Var_Get(DataPath & "Game.ini", "INPUT", "MoveSouth"))
   KeyDefinitions.MoveWest = Val(Var_Get(DataPath & "Game.ini", "INPUT", "MoveWest"))
   KeyDefinitions.PickUpObj = Val(Var_Get(DataPath & "Game.ini", "INPUT", "PickUpObj"))
   KeyDefinitions.QuickBarWindow = Val(Var_Get(DataPath & "Game.ini", "INPUT", "QuickBarWindow"))
   KeyDefinitions.StatWindow = Val(Var_Get(DataPath & "Game.ini", "INPUT", "StatWindow"))
   KeyDefinitions.ZoomIn = Val(Var_Get(DataPath & "Game.ini", "INPUT", "ZoomIn"))
   KeyDefinitions.ZoomOut = Val(Var_Get(DataPath & "Game.ini", "INPUT", "ZoomOut"))
   KeyDefinitions.ResetGUI = Val(Var_Get(DataPath & "Game.ini", "INPUT", "ResetGUI"))
   KeyDefinitions.QuickTarget = Val(Var_Get(DataPath & "Game.ini", "INPUT", "QuickTarget"))
   KeyDefinitions.QuickReply = Val(Var_Get(DataPath & "Game.ini", "INPUT", "QuickReply"))
   For i = 1 To 12
       KeyDefinitions.QuickBar(i) = Val(Var_Get(DataPath & "Game.ini", "INPUT", "QuickBar" & i))
   Next i

End Sub

Public Sub Input_Keys_ClearQueue() '***************************************************************** 'Clears the GetAsyncKeyState queue to prevent key presses from a long time ' ago falling into "have been pressed" 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_ClearQueue '***************************************************************** Dim i As Long

   For i = 1 To 145
       GetAsyncKeyState i
   Next i

End Sub

Private Function Input_GetCommand(ByVal CommandString As String) As Boolean '***************************************************************** 'Checks if "CommandString" is the command entered in the buffer 'Partial matches return true, too, such as for example: 'Entered: /quit CommandString: /qui 'More info: http://www.vbgore.com/GameClient.Input.Input_GetCommand '*****************************************************************

   'Check for the command passed
   If UCase$(Left$(EnterTextBuffer, Len(CommandString))) = UCase$(CommandString) Then Input_GetCommand = True Else Input_GetCommand = False

End Function

Private Function Input_GetBufferArgs() As String

'***************************************************************** 'Returns the arguments for a command entered into the chat buffer ' (basically cuts off the command and the space after it) 'More info: http://www.vbgore.com/GameClient.Input.Input_GetBufferArgs '***************************************************************** Dim s() As String

   'Split between the first space only
   s = Split(EnterTextBuffer, " ", 2)
   
   'Return the parameters if they exist
   If UBound(s) > 0 Then Input_GetBufferArgs = Trim$(s(1))

End Function

Public Sub Input_Init() '***************************************************************** 'Init the input devices (keyboard and mouse) 'More info: http://www.vbgore.com/GameClient.Input.Input_Init '***************************************************************** Dim diProp As DIPROPLONG

   'Create the device
   Set DI = DX.DirectInputCreate
   Set DIDevice = DI.CreateDevice("guid_SysMouse")
   
   Call DIDevice.SetCommonDataFormat(DIFORMAT_MOUSE)
   
   'If in windowed mode, free the mouse from the screen
   If Windowed Then
       Call DIDevice.SetCooperativeLevel(frmMain.hwnd, DISCL_BACKGROUND Or DISCL_NONEXCLUSIVE)
   Else
       Call DIDevice.SetCooperativeLevel(frmMain.hwnd, DISCL_FOREGROUND Or DISCL_EXCLUSIVE)
   End If
   
   diProp.lHow = DIPH_DEVICE
   diProp.lObj = 0
   diProp.lData = 50
   Call DIDevice.SetProperty("DIPROP_BUFFERSIZE", diProp)
   MouseEvent = DX.CreateEvent(frmMain)
   DIDevice.SetEventNotification MouseEvent

End Sub

Sub Input_Keys_Press(ByVal KeyAscii As Integer) '***************************************************************** 'Handles input entering to windows (mostly just alphanumeric) 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_Press '***************************************************************** Dim StartGold As Long Dim b As Boolean

   '*************************
   '***** Amount window *****
   '*************************
   If LastClickedWindow = AmountWindow Then
       'Backspace
       If KeyAscii = 8 Then
           If Len(AmountWindowValue) > 0 Then
               AmountWindowValue = Left$(AmountWindowValue, Len(AmountWindowValue) - 1)
           End If
       End If
       'Number
       If IsNumeric(Chr$(KeyAscii)) Then
           AmountWindowValue = AmountWindowValue & Chr$(KeyAscii)
           If Val(AmountWindowValue) > MAXINT Then AmountWindowValue = Str(MAXINT)
       End If
   '*************************
   '***** Trade window ******
   '*************************
   ElseIf LastClickedWindow = TradeWindow Then
       StartGold = TradeTable.Gold1
       'Backspace
       If KeyAscii = 8 Then
           If Len(Str$(TradeTable.Gold1)) > 0 Then
               If Len(Str$(TradeTable.Gold1)) - 1 <= 1 Then
                   TradeTable.Gold1 = 0
               Else
                   TradeTable.Gold1 = Left$(Str$(TradeTable.Gold1), Len(Str$(TradeTable.Gold1)) - 1)
               End If
           End If
       End If
       'Number
       If IsNumeric(Chr$(KeyAscii)) Then
           If Len(Str$(TradeTable.Gold1) & Chr$(KeyAscii)) < Len(Str$(MAXLONG)) Then
               TradeTable.Gold1 = Val(Str$(TradeTable.Gold1) & Chr$(KeyAscii))
               If TradeTable.Gold1 > MAXLONG Then TradeTable.Gold1 = MAXLONG
           Else
               TradeTable.Gold1 = MAXLONG
           End If
           If TradeTable.Gold1 > BaseStats(SID.Gold) Then TradeTable.Gold1 = BaseStats(SID.Gold)
       End If
       'Check if the gold has changed, if so update it on the server
       If TradeTable.Gold1 <> StartGold Then
           sndBuf.Put_Byte DataCode.User_Trade_UpdateTrade
           sndBuf.Put_Byte 0
           sndBuf.Put_Long TradeTable.Gold1
       End If
       
   '*****************************
   '***** Write mail window *****
   '*****************************
   ElseIf LastClickedWindow = WriteMessageWindow Then
       If WMSelCon Then
           Select Case WMSelCon
               Case wmFrom
                   If KeyAscii = 8 Then
                       If Len(WriteMailData.RecieverName) > 0 Then
                           WriteMailData.RecieverName = Left$(WriteMailData.RecieverName, Len(WriteMailData.RecieverName) - 1)
                       End If
                   Else
                       If Len(WriteMailData.RecieverName) < 10 Then
                           If Game_ValidCharacter(KeyAscii) Then WriteMailData.RecieverName = WriteMailData.RecieverName & Chr$(KeyAscii)
                       End If
                   End If
               Case wmSubject
                   If KeyAscii = 8 Then
                       If Len(WriteMailData.Subject) > 0 Then
                           WriteMailData.Subject = Left$(WriteMailData.Subject, Len(WriteMailData.Subject) - 1)
                       End If
                   Else
                       If Len(WriteMailData.Subject) < 30 Then
                           If Game_ValidCharacter(KeyAscii) Then WriteMailData.Subject = WriteMailData.Subject & Chr$(KeyAscii)
                       End If
                   End If
               Case wmMessage
                   If KeyAscii = 8 Then
                       If Len(WriteMailData.Message) > 0 Then
                           WriteMailData.Message = Left$(WriteMailData.Message, Len(WriteMailData.Message) - 1)
                       End If
                   Else
                       If Len(WriteMailData.Message) < 500 Then
                           If Game_ValidCharacter(KeyAscii) Then WriteMailData.Message = WriteMailData.Message & Chr$(KeyAscii)
                       End If
                   End If
           End Select
       End If
   '*****************************
   '***** Text input buffer *****
   '*****************************
   Else
       If EnterText Then
           
           'Check if to ignore this keystroke
           If IgnoreNextChatKey Then
               IgnoreNextChatKey = False
           Else
               
               'Backspace
               If KeyAscii = 8 Then
                   If Len(EnterTextBuffer) > 0 Then EnterTextBuffer = Left$(EnterTextBuffer, Len(EnterTextBuffer) - 1)
                   b = True
               End If
               
               'Add to text buffer
               If Game_ValidCharacter(KeyAscii) Then
                   If Len(EnterTextBuffer) < 85 Then
                       If Game_ValidCharacter(KeyAscii) Then
                           EnterTextBuffer = EnterTextBuffer & Chr$(KeyAscii)
                           b = True
                       End If
                   End If
               End If
               
               
               'Update size
               If b Then
                   EnterTextBufferWidth = Engine_GetTextWidth(Font_Default, EnterTextBuffer)
                   UpdateShownTextBuffer
                   LastClickedWindow = 0
               End If
               
           End If
           
       End If
   End If

End Sub

Private Sub Input_Keys_Down_Return() '***************************************************************** 'Return was pressed down 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_Down_Return '***************************************************************** Dim j As Long Dim i As Long

   '*************************
   '***** Amount window *****
   '*************************
   If LastClickedWindow = AmountWindow Then
       If AmountWindowItemIndex Then
           If AmountWindowValue <> vbNullString Then
               If IsNumeric(AmountWindowValue) Then
                   'Drop into mail
                   If AmountWindowUsage = AW_InvToMail Then
                       'Check for duplicate entries
                       For j = 1 To MaxMailObjs
                           If WriteMailData.ObjIndex(j) = AmountWindowItemIndex Then
                               ShowGameWindow(AmountWindow) = 0
                               AmountWindowUsage = 0
                               If LastClickedWindow = AmountWindow Then LastClickedWindow = 0
                               Exit Sub
                           End If
                       Next j
                       'Find the next free slot
                       j = 0
                       Do
                           j = j + 1
                           If j > MaxMailObjs Then
                               ShowGameWindow(AmountWindow) = 0
                               AmountWindowUsage = 0
                               If LastClickedWindow = AmountWindow Then LastClickedWindow = 0
                               Exit Sub
                           End If
                       Loop While WriteMailData.ObjIndex(j) > 0
                       WriteMailData.ObjIndex(j) = AmountWindowItemIndex
                       WriteMailData.ObjAmount(j) = CInt(AmountWindowValue)
                   'Buy from NPC
                   ElseIf AmountWindowUsage = AW_ShopToInv Then
                       sndBuf.Allocate 4
                       sndBuf.Put_Byte DataCode.User_Trade_BuyFromNPC
                       sndBuf.Put_Byte AmountWindowItemIndex
                       sndBuf.Put_Integer CInt(AmountWindowValue)
                   'Sell to NPC
                   ElseIf AmountWindowUsage = AW_InvToShop Then
                       sndBuf.Allocate 4
                       sndBuf.Put_Byte DataCode.User_Trade_SellToNPC
                       sndBuf.Put_Byte AmountWindowItemIndex
                       sndBuf.Put_Integer CInt(AmountWindowValue)
                   'Take from bank
                   ElseIf AmountWindowUsage = AW_BankToInv Then
                       sndBuf.Allocate 4
                       sndBuf.Put_Byte DataCode.User_Bank_TakeItem
                       sndBuf.Put_Byte AmountWindowItemIndex
                       sndBuf.Put_Integer CInt(AmountWindowValue)
                   'Put in bank
                   ElseIf AmountWindowUsage = AW_InvToBank Then
                       sndBuf.Allocate 4
                       sndBuf.Put_Byte DataCode.User_Bank_PutItem
                       sndBuf.Put_Byte AmountWindowItemIndex
                       sndBuf.Put_Integer CInt(AmountWindowValue)
                   'Put in trade
                   ElseIf AmountWindowUsage = AW_InvToTrade Then
                       sndBuf.Put_Byte DataCode.User_Trade_UpdateTrade
                       sndBuf.Put_Byte AmountWindowItemIndex
                       sndBuf.Put_Long CInt(AmountWindowValue)
                            
                   'Drop on ground
                   Else
                       sndBuf.Allocate 4
                       sndBuf.Put_Byte DataCode.User_Drop
                       sndBuf.Put_Byte AmountWindowItemIndex
                       sndBuf.Put_Integer CInt(AmountWindowValue)
                   End If
               Else
                   AmountWindowValue = vbNullString
               End If
               ShowGameWindow(AmountWindow) = 0
               AmountWindowUsage = 0
               If LastClickedWindow = AmountWindow Then LastClickedWindow = 0
           End If
       End If
   '*****************************
   '***** Write mail window *****
   '*****************************
   ElseIf LastClickedWindow = WriteMessageWindow Then
       'Send message
       If LastMailSendTime + 4000 < timeGetTime Then   'DelayTimeMail (+1000ms for packet delay)
           If Len(WriteMailData.Subject) > 0 Then
               If Len(WriteMailData.Message) > 0 Then
                   If Len(WriteMailData.RecieverName) > 0 Then
                       For i = 1 To MaxMailObjs
                           If WriteMailData.ObjIndex(i) = 0 Then
                               i = i - 1
                               Exit For
                           End If
                       Next i
                       sndBuf.Allocate 6 + Len(WriteMailData.RecieverName) + Len(WriteMailData.Subject) + Len(WriteMailData.Message)
                       sndBuf.Put_Byte DataCode.Server_MailCompose
                       sndBuf.Put_String WriteMailData.RecieverName
                       sndBuf.Put_String WriteMailData.Subject
                       sndBuf.Put_StringEX WriteMailData.Message
                       sndBuf.Put_Byte i   'Number of objects
                       If i > 0 Then
                           For j = 1 To i
                               sndBuf.Allocate 3
                               sndBuf.Put_Byte WriteMailData.ObjIndex(j)
                               sndBuf.Put_Integer WriteMailData.ObjAmount(j)
                           Next j
                       End If
                       
                       WriteMailData.Message = vbNullString
                       WriteMailData.RecieverName = vbNullString
                       WriteMailData.Subject = vbNullString
                       ShowGameWindow(WriteMessageWindow) = 0
                       If LastClickedWindow = WriteMessageWindow Then LastClickedWindow = 0
                       LastMailSendTime = timeGetTime
                   End If
               End If
           End If
       End If
       
   End If
   
   '***********************
   '***** Chat screen *****
   '***********************
   If LastClickedWindow <> WriteMessageWindow Then
       If LastClickedWindow <> ViewMessageWindow Then
           If LastClickedWindow <> AmountWindow Then
               If EnterText = True Then
                   If EnterTextBuffer <> vbNullString Then Input_HandleCommands
                   EnterText = False
               Else
                   EnterText = True
               End If
           End If
       End If
   End If

End Sub

Private Function Input_Keys_IsNumeric(ByVal KeyCode As Integer) As Boolean '***************************************************************** 'Check if a numeric key (0 to 9) was pressed 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_IsNumeric '*****************************************************************

   '0 = 48
   '9 = 57
   If KeyCode > 47 Then
       If KeyCode < 58 Then
           Input_Keys_IsNumeric = True
       End If
   End If

End Function

Private Function Input_Keys_IsAlpha(ByVal KeyCode As Integer) As Boolean '***************************************************************** 'Check if an alphabet key (A to Z) was pressed 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_IsAlpha '*****************************************************************

   'a = 65
   'z = 90
   If KeyCode > 64 Then
       If KeyCode < 91 Then
           Input_Keys_IsAlpha = True
       End If
   End If
   

End Function

Private Function Input_Keys_IsAlphaNumeric(ByVal KeyCode As Integer) As Boolean '***************************************************************** 'Check if an alphanumeric key (A to Z, 0 to 9) was pressed 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_IsAlphaNumeric '*****************************************************************

   Input_Keys_IsAlphaNumeric = (Input_Keys_IsNumeric(KeyCode) And Input_Keys_IsAlpha(KeyCode))

End Function

Sub Input_Keys_Down(ByVal KeyCode As Integer) '***************************************************************** 'Checks keys and respond 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_Down '***************************************************************** Dim i As Long

   'Return was pressed
   If KeyCode = vbKeyReturn Then
       Input_Keys_Down_Return
       Exit Sub
   End If
   
   'Escape was pressed
   If KeyCode = vbKeyEscape Then
       If LastClickedWindow = 0 Then
           If ShowGameWindow(MenuWindow) = 0 Then
               If EnterText Then
                   EnterTextBuffer = vbNullString
                   EnterTextBufferWidth = 10
                   UpdateShownTextBuffer
                   EnterText = False
               End If
           End If
       Else
           ShowGameWindow(LastClickedWindow) = 0
           LastClickedWindow = 0
           Exit Sub
       End If
   End If
   
   'Hide/show the mini-map
   If Input_Keys_IsPressed(KeyDefinitions.MiniMap, KeyCode) Then
       If ShowMiniMap = 0 Then ShowMiniMap = 1 Else ShowMiniMap = 0
   End If
   
   'Get object off ground (alt)
   If Input_Keys_IsPressed(KeyDefinitions.PickUpObj, KeyCode) Then
       If Engine_OBJ_AtTile(CharList(UserCharIndex).Pos.X, CharList(UserCharIndex).Pos.Y) Then
           If LastLootTime < timeGetTime Then
               LastLootTime = timeGetTime + LootDelay
               sndBuf.Put_Byte DataCode.User_Get
           End If
       End If
   End If
   
   'Use the quick bar
   For i = 1 To 12
       If Input_Keys_IsPressed(KeyDefinitions.QuickBar(i), KeyCode) Then
           Engine_UseQuickBar KeyCode - vbKeyF1 + 1
       End If
   Next i
   
   'Attack key
   If Input_Keys_IsPressed(KeyDefinitions.Attack, KeyCode) Then
       If UserCharIndex > 0 Then
           If LastAttackTime < timeGetTime Then
               LastAttackTime = timeGetTime + AttackDelay
               
               'Check for a valid attacking distance
               If UserAttackRange > 1 Then
                   If TargetCharIndex > 0 Then
                       If TargetCharIndex <> UserCharIndex Then
                           If Engine_Distance(CharList(UserCharIndex).Pos.X, CharList(UserCharIndex).Pos.Y, CharList(TargetCharIndex).Pos.X, CharList(TargetCharIndex).Pos.Y) <= UserAttackRange Then
                               LastAttackTime = timeGetTime
                               sndBuf.Allocate 2
                               sndBuf.Put_Byte DataCode.User_Attack
                               sndBuf.Put_Byte CharList(UserCharIndex).Heading
                           Else
                               Engine_AddToChatTextBuffer Message(91), FontColor_Fight
                           End If
                       End If
                   End If
               Else
                   If Engine_UserIsFacingChar Then
                       LastAttackTime = timeGetTime
                       sndBuf.Allocate 2
                       sndBuf.Put_Byte DataCode.User_Attack
                       sndBuf.Put_Byte CharList(UserCharIndex).Heading
                   End If
               End If
               
           End If
       End If
   End If
   
   'Chat buffer scrolling
   If Input_Keys_IsPressed(KeyDefinitions.ChatBufferUp, KeyCode) Then
       If ShowGameWindow(ChatWindow) Then
           ChatBufferChunk = ChatBufferChunk + 0.5
           Engine_UpdateChatArray
       End If
   End If
   If Input_Keys_IsPressed(KeyDefinitions.ChatBufferDown, KeyCode) Then
       If ShowGameWindow(ChatWindow) Then
           If ChatBufferChunk > 1 Then
               ChatBufferChunk = ChatBufferChunk - 0.5
               Engine_UpdateChatArray
           End If
       End If
   End If
   
   'Hide/show windows
   If Input_Keys_IsPressed(KeyDefinitions.InventoryWindow, KeyCode) Then
       If ShowGameWindow(InventoryWindow) Then
           ShowGameWindow(InventoryWindow) = 0
           If LastClickedWindow = InventoryWindow Then LastClickedWindow = 0
       Else
           ShowGameWindow(InventoryWindow) = 1
           LastClickedWindow = InventoryWindow
       End If
   End If
   If Input_Keys_IsPressed(KeyDefinitions.QuickBarWindow, KeyCode) Then
       If ShowGameWindow(QuickBarWindow) Then
           ShowGameWindow(QuickBarWindow) = 0
           If LastClickedWindow = QuickBarWindow Then LastClickedWindow = 0
       Else
           ShowGameWindow(QuickBarWindow) = 1
           LastClickedWindow = QuickBarWindow
       End If
   End If
   If Input_Keys_IsPressed(KeyDefinitions.ChatWindow, KeyCode) Then
       If ShowGameWindow(ChatWindow) Then
           ShowGameWindow(ChatWindow) = 0
           If LastClickedWindow = ChatWindow Then LastClickedWindow = 0
       Else
           ShowGameWindow(ChatWindow) = 1
           LastClickedWindow = ChatWindow
       End If
   End If
   If Input_Keys_IsPressed(KeyDefinitions.StatWindow, KeyCode) Then
       If ShowGameWindow(StatWindow) Then
           ShowGameWindow(StatWindow) = 0
           If LastClickedWindow = StatWindow Then LastClickedWindow = 0
       Else
           ShowGameWindow(StatWindow) = 1
           LastClickedWindow = StatWindow
       End If
   End If
   If Input_Keys_IsPressed(KeyDefinitions.MenuWindow, KeyCode) Then
       If ShowGameWindow(MenuWindow) Then
           ShowGameWindow(MenuWindow) = 0
           If LastClickedWindow = MenuWindow Then LastClickedWindow = 0
       Else
           ShowGameWindow(MenuWindow) = 1
           LastClickedWindow = MenuWindow
       End If
   End If
   
   'Reset skin positions
   If Input_Keys_IsPressed(KeyDefinitions.ResetGUI, KeyCode) Then
       Engine_Init_GUI 0
       Game_Config_Save
   End If
   'Delete mail (Delete)
   If KeyCode = vbKeyDelete Then
       If LastClickedWindow = MailboxWindow Then
           If ShowGameWindow(MailboxWindow) Then
               If SelMessage > 0 Then
                   sndBuf.Allocate 2
                   sndBuf.Put_Byte DataCode.Server_MailDelete
                   sndBuf.Put_Byte SelMessage
               End If
           End If
       End If
   End If
   
   'Auto-write a reply to the last person to whisper to us
   If Input_Keys_IsPressed(KeyDefinitions.QuickReply, KeyCode) Then
       If LenB(LastWhisperName) <> 0 Then
           EnterText = True
           EnterTextBuffer = "/tell " & LastWhisperName & " "
           EnterTextBufferWidth = Engine_GetTextWidth(Font_Default, EnterTextBuffer)
           IgnoreNextChatKey = True
           UpdateShownTextBuffer
           LastClickedWindow = 0
       End If
   End If
   
   'Target the closest character
   If Input_Keys_IsPressed(KeyDefinitions.QuickTarget, KeyCode) Then
       i = Game_ClosestTargetNPC
       If i > 0 Then
           sndBuf.Allocate 3
           sndBuf.Put_Byte DataCode.User_Target
           sndBuf.Put_Integer i
       End If
   End If
   'Send an emoticon - but make sure we're not typing or entering in a mail message
   If EnterText = False Then
       If Not LastClickedWindow = WriteMessageWindow Then
           If Not LastClickedWindow = AmountWindow Then
               If ShowGameWindow(WriteMessageWindow) = 0 Then
                   If ShowGameWindow(NPCChatWindow) = 0 Then
                       If EmoticonDelay < timeGetTime Then
                           EmoticonDelay = timeGetTime + 2000  'Wait 2000ms (two seconds) between emoticon usages
                           
                           Select Case KeyCode
                               Case vbKey1
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.Dots
                               Case vbKey2
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.Exclimation
                               Case vbKey3
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.Question
                               Case vbKey4
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.Surprised
                               Case vbKey5
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.Heart
                               Case vbKey6
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.Hearts
                               Case vbKey7
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.HeartBroken
                               Case vbKey8
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.Utensils
                               Case vbKey9
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.Meat
                               Case vbKey0
                                   sndBuf.Allocate 2
                                   sndBuf.Put_Byte DataCode.User_Emote
                                   sndBuf.Put_Byte EmoID.ExcliQuestion
                           End Select
                           
                       End If
                       
                   Else
                       
                       If KeyCode >= 49 Then
                           If KeyCode - 48 <= GameWindow.NPCChat.NumAnswers Then
                               i = NPCChat(ActiveAsk.ChatIndex).Ask.Ask(ActiveAsk.AskIndex).Answer(KeyCode - 48).GotoID
                               If i > 0 Then
                                   Engine_ShowNPCChatWindow ActiveAsk.AskName, ActiveAsk.ChatIndex, i
                               Else
                                   ShowGameWindow(NPCChatWindow) = 0
                                   If LastClickedWindow = NPCChatWindow Then LastClickedWindow = 0
                               End If
                           End If
                       End If
                   
                   End If
               End If
           End If
       End If
   End If

End Sub

Private Sub Input_HandleCommands() '***************************************************************** 'Handles all the chat commands - when aborting, use either GoTo CleanUp ' to ignore the keystroke (buffer is not cleared) or GoTo CleanUp to ' clear the buffer, too (its all just about preference) 'More info: http://www.vbgore.com/GameClient.Input.Input_HandleCommands '***************************************************************** Dim TempS() As String Dim s As String Dim s2 As String Dim i As Long Dim j As Long

   '***** Check for commands *****
   If Input_GetCommand("/BLI") Then
       sndBuf.Put_Byte DataCode.User_Blink
       
   ElseIf Input_GetCommand("/LOOKL") Then
       sndBuf.Put_Byte DataCode.User_LookLeft
       
   ElseIf Input_GetCommand("/LOOKR") Then
       sndBuf.Put_Byte DataCode.User_LookRight
       
   ElseIf Input_GetCommand("/WHO") Then
       sndBuf.Put_Byte DataCode.Server_Who
       
   ElseIf Input_GetCommand("/SH") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.Comm_Shout
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/GINFO") Or Input_GetCommand("/GROUPI") Then
       sndBuf.Put_Byte DataCode.User_Group_Info
       
   ElseIf Input_GetCommand("/TELL") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       TempS() = Split(s, " ", 2)
       If UBound(TempS) < 1 Then GoTo CleanUp
       If LenB(Trim$(TempS(0))) = 0 Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.Comm_Whisper
       sndBuf.Put_String Trim$(TempS(0))
       sndBuf.Put_String Trim$(TempS(1))
       
   ElseIf Input_GetCommand("/DEP") Then
       j = Val(Input_GetBufferArgs)
       If j <= 0 Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.User_Bank_Deposit
       sndBuf.Put_Long j
       'We will assume that the deposit was successful
       Engine_AddToChatTextBuffer Replace$(Message(118), "<amount>", Str(j)), FontColor_Info
       
   ElseIf Input_GetCommand("/WITH") Then
       j = Val(Input_GetBufferArgs)
       If j <= 0 Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.User_Bank_Withdraw
       sndBuf.Put_Long j
   ElseIf Input_GetCommand("/TRADE") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then
           Engine_AddToChatTextBuffer Message(136), FontColor_Info
           GoTo CleanUp
       End If
       If UCase$(s) = UCase$(CharList(UserCharIndex).Name) Then
           Engine_AddToChatTextBuffer Message(133), FontColor_Info
           GoTo CleanUp
       End If
       sndBuf.Put_Byte DataCode.User_Trade_Trade
       sndBuf.Put_String s
   ElseIf Input_GetCommand("/BALAN") Then
       sndBuf.Put_Byte DataCode.User_Bank_Balance
       
   ElseIf Input_GetCommand("/G ") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.Comm_GroupTalk
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/CREATEG") Or Input_GetCommand("/MAKEG") Or Input_GetCommand("/NEWG") Then
       sndBuf.Put_Byte DataCode.User_Group_Make
   
   ElseIf Input_GetCommand("/INVITE") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.User_Group_Invite
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/LEAVEG") Or Input_GetCommand("/EXITG") Then
       sndBuf.Put_Byte DataCode.User_Group_Leave
       
   ElseIf Input_GetCommand("/JOING") Then
       sndBuf.Put_Byte DataCode.User_Group_Join
       
   ElseIf Input_GetCommand("/ME") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.Comm_Emote
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/EM") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.Comm_Emote
       sndBuf.Put_String s
   ElseIf Input_GetCommand("/LANG") Then
       s = LCase$(Input_GetBufferArgs)
       If s = vbNullString Then GoTo CleanUp
       If Engine_FileExist(MessagePath & s & "*.ini", vbNormal) Then
           s = Dir$(MessagePath & s & "*.ini", vbNormal)
           s = Left$(s, Len(s) - 4)
           s = Engine_Init_Messages(s)
           Engine_Init_Signs s
           Var_Write DataPath & "Game.ini", "INIT", "Language", s
           Engine_AddToChatTextBuffer Replace$(Message(90), "<lang>", s), FontColor_Info
       Else
           Engine_AddToChatTextBuffer Message(87), FontColor_Info
       End If
       
   ElseIf Input_GetCommand("/SKIN") Then
       s = LCase$(Input_GetBufferArgs)
       If s = vbNullString Then
           Engine_AddToChatTextBuffer Engine_BuildSkinsList, FontColor_Info
           GoTo CleanUp
       End If
       If Engine_FileExist(DataPath & "Skins\" & s & "*.ini", vbNormal) Then
           s = Dir$(DataPath & "Skins\" & s & "*.ini", vbNormal)
           CurrentSkin = Left$(s, Len(s) - 4)
           Engine_Init_GUI 0
           Var_Write DataPath & "Game.ini", "INIT", "CurrentSkin", CurrentSkin
           Engine_AddToChatTextBuffer Replace$(Message(89), "<skin>", CurrentSkin), FontColor_Info
       Else
           Engine_AddToChatTextBuffer Message(88), FontColor_Info
       End If
       
   ElseIf Input_GetCommand("/QUEST") Then
       If QuestInfoUBound = 0 Then
           'No quests in place
           Engine_AddToChatTextBuffer Message(103), FontColor_Quest
       Else
           j = Val(Input_GetBufferArgs)
           If j < 1 Or j > QuestInfoUBound Then
               'No valid number specified, give the list
               Engine_AddToChatTextBuffer Message(104), FontColor_Quest
               For i = 1 To QuestInfoUBound
                   Engine_AddToChatTextBuffer "  " & i & ". " & QuestInfo(i).Name, FontColor_Quest
               Next i
           Else
               'Give the info on the specific quest
               Engine_AddToChatTextBuffer QuestInfo(j).Name & ":", FontColor_Quest
               Engine_AddToChatTextBuffer QuestInfo(j).Desc, FontColor_Quest
           End If
       End If
       
   ElseIf Input_GetCommand("/CANCELQUEST") Or Input_GetCommand("/ENDQUEST") Then
       If QuestInfoUBound = 0 Then GoTo CleanUp
       j = Val(Input_GetBufferArgs)
       If j < 1 Or j > QuestInfoUBound Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.User_CancelQuest
       sndBuf.Put_Byte CByte(j)
               
   ElseIf Input_GetCommand("/THR") Then
       TempS = Split(EnterTextBuffer)
       If UBound(TempS) <> 0 Then
           If IsNumeric(TempS(1)) Then
               sndBuf.Put_Byte DataCode.GM_Thrall
               sndBuf.Put_Integer Val(TempS(1))
               If UBound(TempS) > 1 Then
                   If IsNumeric(TempS(2)) Then
                       sndBuf.Put_Integer Val(TempS(2))
                   Else
                       sndBuf.Put_Integer 1
                   End If
                   sndBuf.Put_Integer 1
               End If
           End If
       End If
       
   ElseIf Input_GetCommand("/DETHR") Then
       sndBuf.Put_Byte DataCode.GM_DeThrall
       
   ElseIf Input_GetCommand("/QUIT") Then
       IsUnloading = 1
       
   ElseIf Input_GetCommand("/ACCEPT") Then
       sndBuf.Put_Byte DataCode.User_StartQuest
       
   ElseIf Input_GetCommand("/DESC") Then
       s = Input_GetBufferArgs
       sndBuf.Put_Byte DataCode.User_Desc
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/HELP") Then
       sndBuf.Put_Byte DataCode.Server_Help
       
   ElseIf Input_GetCommand("/APPR") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.GM_Approach
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/SUM") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.GM_Summon
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/SETGM") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       TempS = Split(s, " ")
       If UBound(TempS) > 0 Then
           If IsNumeric(TempS(1)) Then
               sndBuf.Allocate 3 + Len(TempS(0))
               sndBuf.Put_Byte DataCode.GM_SetGMLevel
               sndBuf.Put_String TempS(0)
               sndBuf.Put_Byte CByte(TempS(1))
           End If
       End If
       
   ElseIf Input_GetCommand("/CLICKWARP") Then
       If UseClickWarp = 1 Then UseClickWarp = 0 Else UseClickWarp = 1
       Engine_AddToChatTextBuffer Replace$(Message(124), "<value>", UseClickWarp), FontColor_Info
       
   ElseIf Input_GetCommand("/BANIP") Then
       s = Input_GetBufferArgs 'Remove the command
       If LenB(s) < 4 Then 'Not enough information entered
           Engine_AddToChatTextBuffer Message(92), FontColor_Info
           GoTo CleanUp
       End If
       TempS = Split(s, " ", 2)    'Split up the IP and reason
       If UBound(TempS) = 0 Then
           Engine_AddToChatTextBuffer Message(93), FontColor_Info
           GoTo CleanUp
       Else
           s = TempS(0)
           s2 = TempS(1)
       End If
       TempS = Split(s, ".")
       If UBound(TempS) <> 3 Then
           Engine_AddToChatTextBuffer Message(92), FontColor_Info
           GoTo CleanUp
       End If
       For j = 0 To 3
           If Val(TempS(j)) < 0 Or Val(TempS(j)) > 255 Then
               Engine_AddToChatTextBuffer Message(92), FontColor_Info
               GoTo CleanUp
           End If
       Next j
       sndBuf.Put_Byte DataCode.GM_BanIP
       sndBuf.Put_String Trim$(s)
       sndBuf.Put_String Trim$(s2)
       
   ElseIf Input_GetCommand("/UNBANIP") Then
       s = Input_GetBufferArgs 'Remove the command
       If LenB(s) < 4 Then 'Not enough information entered
           Engine_AddToChatTextBuffer Message(92), FontColor_Info
           GoTo CleanUp
       End If
       TempS = Split(s, ".")
       If UBound(TempS) <> 3 Then
           Engine_AddToChatTextBuffer Message(92), FontColor_Info
           GoTo CleanUp
       End If
       For j = 0 To 3
           If TempS(j) <> "*" Then
               If Val(TempS(j)) < 0 Or Val(TempS(j)) > 255 Then
                   Engine_AddToChatTextBuffer Message(92), FontColor_Info
                   GoTo CleanUp
               End If
           End If
       Next j
       sndBuf.Put_Byte DataCode.GM_UnBanIP
       sndBuf.Put_String Trim$(s)
       
   ElseIf Input_GetCommand("/KICK") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.GM_Kick
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/SEARCHI") Or Input_GetCommand("/FINDI") Or Input_GetCommand("/FINDO") Or Input_GetCommand("/SEARCHO") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.GM_FindItem
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/GIVESK") Or Input_GetCommand("/GIVESP") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       TempS = Split(s, " ")
       If UBound(TempS) <> 1 Then GoTo CleanUp
       If Val(TempS(1)) <= 0 Or Val(TempS(1)) > 255 Then Exit Sub
       sndBuf.Put_Byte DataCode.GM_GiveSkill
       sndBuf.Put_String TempS(0)
       sndBuf.Put_Long Val(TempS(1))
       
   ElseIf Input_GetCommand("/SQL") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       sndBuf.Put_Byte DataCode.GM_SQL
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/KILLMAP") Then
       sndBuf.Put_Byte DataCode.GM_KillMap
   ElseIf Input_GetCommand("/KILL") Then
       If TargetCharIndex = UserCharIndex Or TargetCharIndex = 0 Then
           Engine_AddToChatTextBuffer "Suicide is not the answer...", FontColor_Info
       Else
           sndBuf.Put_Byte DataCode.GM_Kill
       End If
       
   ElseIf Input_GetCommand("/GIVEGO") Then
       s = Input_GetBufferArgs
       If Val(s) <= 0 Or Val(s) > MAXLONG Then
           Engine_AddToChatTextBuffer "Please enter an amount greater than 0.", FontColor_Info
           GoTo CleanUp
       End If
       sndBuf.Put_Byte DataCode.GM_GiveGold
       sndBuf.Put_Long Val(s)
       
   ElseIf Input_GetCommand("/GIVEOBJ") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       TempS = Split(s, " ")
       If UBound(TempS) <> 1 Then
           Engine_AddToChatTextBuffer "Please use the format: <ObjIndex> <Amount>", FontColor_Info
           GoTo CleanUp
       End If
       If Val(TempS(0)) <= 0 Or Val(TempS(0)) > MAXINT Then
           Engine_AddToChatTextBuffer "Invalid ObjIndex parameter - enter a value between 1 and " & MAXINT & ".", FontColor_Info
           GoTo CleanUp
       End If
       If Val(TempS(1)) <= 0 Or Val(TempS(1)) > MAXINT Then
           Engine_AddToChatTextBuffer "Invalid Amount parameter - enter a value between 1 and " & MAXINT & ".", FontColor_Info
           GoTo CleanUp
       End If
       sndBuf.Put_Byte DataCode.GM_GiveObject
       sndBuf.Put_Integer Val(TempS(0))
       sndBuf.Put_Integer Val(TempS(1))
       
   ElseIf Input_GetCommand("/WARP") Then
       i = Val(Input_GetBufferArgs)
       If Not Engine_FileExist(MapPath & i & ".map", vbNormal) Then
           Engine_AddToChatTextBuffer "Please enter a valid map number.", FontColor_Info
           GoTo CleanUp
       End If
       sndBuf.Put_Byte DataCode.GM_WarpToMap
       sndBuf.Put_Integer i
   
   ElseIf Input_GetCommand("/IPINFO") Then
       s = Input_GetBufferArgs
       If s = vbNullString Then GoTo CleanUp
       TempS = Split(s, ".")   'All of this is just a check for a valid IP
       If UBound(TempS) <> 3 Then  'Check for 3 periods
           Engine_AddToChatTextBuffer Message(92), FontColor_Info
           GoTo CleanUp
       End If
       For j = 0 To 3  'Check for values between 0 and 255
           If Val(TempS(j)) < 0 Or Val(TempS(j)) > 255 Then
               Engine_AddToChatTextBuffer Message(92), FontColor_Info
               GoTo CleanUp
           End If
       Next j
       sndBuf.Put_Byte DataCode.GM_IPInfo
       sndBuf.Put_String s
       
   ElseIf Input_GetCommand("/BANLIST") Then
       sndBuf.Put_Byte DataCode.GM_BanList
       
   ElseIf Input_GetCommand("/RAISE") Then
       TempS() = Split(Input_GetBufferArgs, " ")
       If UBound(TempS) > 0 Then
           If IsNumeric(TempS(1)) Then
               sndBuf.Allocate 6 + Len(TempS(0))
               sndBuf.Put_Byte DataCode.GM_Raise
               sndBuf.Put_String TempS(0)
               sndBuf.Put_Long CLng(TempS(1))
           End If
       End If
       
       
   Else
       '*** No commands sent, send as text ***
       EnterTextBuffer = Trim$(EnterTextBuffer)
       sndBuf.Allocate 2 + Len(EnterTextBuffer)
       sndBuf.Put_Byte DataCode.Comm_Talk
       sndBuf.Put_String EnterTextBuffer
       
       'We just sent a chat message, so check if it had triggers!
       Engine_NPCChat_CheckForChatTriggers EnterTextBuffer
       
   End If
   

CleanUp:

   'Cleans up the buffer
   EnterTextBuffer = vbNullString
   EnterTextBufferWidth = 10
   ShownText = vbNullString

End Sub

Sub Input_Keys_General() '***************************************************************** 'Checks keys and respond 'More info: http://www.vbgore.com/GameClient.Input.Input_Keys_General '*****************************************************************

   If GetActiveWindow = 0 Then Exit Sub
   
   'Dont move when Control is pressed
   If GetAsyncKeyState(vbKeyControl) Then Exit Sub
   'Check if certain screens are open that require ASDW keys
   If ShowGameWindow(WriteMessageWindow) Then
       If WMSelCon <> 0 Then Exit Sub
   End If
   'Zoom in / out
   If LastClickedWindow <> TradeWindow Then
       If LastClickedWindow <> ChatWindow Then
           If GetAsyncKeyState(KeyDefinitions.ZoomIn) Then       'In
               ZoomLevel = ZoomLevel + (ElapsedTime * 0.0003)
               If ZoomLevel > MaxZoomLevel Then ZoomLevel = MaxZoomLevel
           ElseIf GetAsyncKeyState(KeyDefinitions.ZoomOut) Then  'Out
               ZoomLevel = ZoomLevel - (ElapsedTime * 0.0003)
               If ZoomLevel < 0 Then ZoomLevel = 0
           End If
       End If
   End If
   'Don't allow any these keys during movement..
   If UserMoving = 0 Then
       If GetAsyncKeyState(vbKeyTab) Then
           'Move Up-Right
           If GetKeyState(vbKeyUp) < 0 And GetKeyState(vbKeyRight) < 0 Then
               Engine_ChangeHeading NORTHEAST
               Exit Sub
           End If
           'Move Up-Left
           If GetKeyState(vbKeyUp) < 0 And GetKeyState(vbKeyLeft) < 0 Then
               Engine_ChangeHeading NORTHWEST
               Exit Sub
           End If
           'Move Down-Right
           If GetKeyState(vbKeyDown) < 0 And GetKeyState(vbKeyRight) < 0 Then
               Engine_ChangeHeading SOUTHEAST
               Exit Sub
           End If
           'Move Down-Left
           If GetKeyState(vbKeyDown) < 0 And GetKeyState(vbKeyLeft) < 0 Then
               Engine_ChangeHeading SOUTHWEST
               Exit Sub
           End If
           'Move Up
           If GetKeyState(vbKeyUp) < 0 Then
               Engine_ChangeHeading NORTH
               Exit Sub
           End If
           'Move Right
           If GetKeyState(vbKeyRight) < 0 Then
               Engine_ChangeHeading EAST
               Exit Sub
           End If
           'Move down
           If GetKeyState(vbKeyDown) < 0 Then
               Engine_ChangeHeading SOUTH
               Exit Sub
           End If
           'Move left
           If GetKeyState(vbKeyLeft) < 0 Then
               Engine_ChangeHeading WEST
               Exit Sub
           End If
           If EnterText = False Then
               If GetKeyState(KeyDefinitions.MoveNorth) < 0 And GetKeyState(KeyDefinitions.MoveEast) < 0 Then
                   Engine_ChangeHeading NORTHEAST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveNorth) < 0 And GetKeyState(KeyDefinitions.MoveWest) < 0 Then
                   Engine_ChangeHeading NORTHWEST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveSouth) < 0 And GetKeyState(KeyDefinitions.MoveEast) < 0 Then
                   Engine_ChangeHeading SOUTHEAST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveSouth) < 0 And GetKeyState(KeyDefinitions.MoveWest) < 0 Then
                   Engine_ChangeHeading SOUTHWEST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveNorth) < 0 Then
                   Engine_ChangeHeading NORTH
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveEast) < 0 Then
                   Engine_ChangeHeading EAST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveSouth) < 0 Then
                   Engine_ChangeHeading SOUTH
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveWest) < 0 Then
                   Engine_ChangeHeading WEST
                   Exit Sub
               End If
           End If
       Else
           'Move Up-Right
           If GetKeyState(vbKeyUp) < 0 And GetKeyState(vbKeyRight) < 0 Then
               Engine_MoveUser NORTHEAST
               Exit Sub
           End If
           'Move Up-Left
           If GetKeyState(vbKeyUp) < 0 And GetKeyState(vbKeyLeft) < 0 Then
               Engine_MoveUser NORTHWEST
               Exit Sub
           End If
           'Move Down-Right
           If GetKeyState(vbKeyDown) < 0 And GetKeyState(vbKeyRight) < 0 Then
               Engine_MoveUser SOUTHEAST
               Exit Sub
           End If
           'Move Down-Left
           If GetKeyState(vbKeyDown) < 0 And GetKeyState(vbKeyLeft) < 0 Then
               Engine_MoveUser SOUTHWEST
               Exit Sub
           End If
           'Move Up
           If GetKeyState(vbKeyUp) < 0 Then
               Engine_MoveUser NORTH
               Exit Sub
           End If
           'Move Right
           If GetKeyState(vbKeyRight) < 0 Then
               Engine_MoveUser EAST
               Exit Sub
           End If
           'Move down
           If GetKeyState(vbKeyDown) < 0 Then
               Engine_MoveUser SOUTH
               Exit Sub
           End If
           'Move left
           If GetKeyState(vbKeyLeft) < 0 Then
               Engine_MoveUser WEST
               Exit Sub
           End If
           If EnterText = False Then
               If GetKeyState(KeyDefinitions.MoveNorth) < 0 And GetKeyState(KeyDefinitions.MoveEast) < 0 Then
                   Engine_MoveUser NORTHEAST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveNorth) < 0 And GetKeyState(KeyDefinitions.MoveWest) < 0 Then
                   Engine_MoveUser NORTHWEST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveSouth) < 0 And GetKeyState(KeyDefinitions.MoveEast) < 0 Then
                   Engine_MoveUser SOUTHEAST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveSouth) < 0 And GetKeyState(KeyDefinitions.MoveWest) < 0 Then
                   Engine_MoveUser SOUTHWEST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveNorth) < 0 Then
                   Engine_MoveUser NORTH
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveEast) < 0 Then
                   Engine_MoveUser EAST
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveSouth) < 0 Then
                   Engine_MoveUser SOUTH
                   Exit Sub
               End If
               If GetKeyState(KeyDefinitions.MoveWest) < 0 Then
                   Engine_MoveUser WEST
                   Exit Sub
               End If
           End If
       End If
   End If

End Sub

Sub Input_Mouse_LeftClick() '***************************************************************** 'Left click mouse 'More info: http://www.vbgore.com/GameClient.Input.Input_Mouse_LeftClick '***************************************************************** Dim tX As Integer Dim tY As Integer Dim i As Long

   'Make sure engine is running
   If Not EngineRun Then Exit Sub
   '***Check for skill list click***
   'Skill lists, because it is not actually a window, must be handled differently
   If QuickBarSetSlot <= 0 Then DrawSkillList = 0
   If DrawSkillList Then
       If SkillListSize Then
           For tX = 1 To SkillListSize
               If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, SkillList(tX).X, SkillList(tX).Y, 32, 32) Then
                   QuickBarID(QuickBarSetSlot).ID = SkillList(tX).SkillID
                   QuickBarID(QuickBarSetSlot).Type = QuickBarType_Skill
                   DrawSkillList = 0
                   QuickBarSetSlot = 0
                   Exit Sub
               End If
           Next tX
       End If
   End If
   '***Check for a window click***
   WMSelCon = 0
   'Start with the last clicked window, then move in order of importance
   If LastClickedWindow > 0 Then
       If Input_Mouse_LeftClick_Window(LastClickedWindow) = 1 Then Exit Sub
   End If
   For i = 1 To NumGameWindows
       If LastClickedWindow <> i Then
           If Input_Mouse_LeftClick_Window(i) = 1 Then Exit Sub
       End If
   Next i
   'No windows clicked, so a tile click will take place
   'Get the tile positions
   Engine_ConvertCPtoTP MousePos.X, MousePos.Y, tX, tY
   'Send left click
   sndBuf.Allocate 3
   sndBuf.Put_Byte DataCode.User_LeftClick
   sndBuf.Put_Byte CByte(tX)
   sndBuf.Put_Byte CByte(tY)
   'If there was a click on the game screen and the
   ' skill list is up, but no window clicked, set to 0
   If DrawSkillList Then
       If QuickBarSetSlot Then
           QuickBarID(QuickBarSetSlot).ID = 0
           QuickBarID(QuickBarSetSlot).Type = 0
           DrawSkillList = 0
           QuickBarSetSlot = 0
       End If
   End If
   
   'Last clicked window was nothing, so set to nothing :)
   LastClickedWindow = 0

End Sub

Function Input_Mouse_LeftClick_Window(ByVal WindowIndex As Byte) As Byte '***************************************************************** 'Left click a game window 'More info: http://www.vbgore.com/GameClient.Input.Input_Mouse_LeftClick_Window '***************************************************************** Dim i As Byte Dim j As Byte

   Select Case WindowIndex
       Case TradeWindow
           If ShowGameWindow(TradeWindow) Then
               With GameWindow.Trade
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = TradeWindow
                       'Item window
                       For i = 1 To 9
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Trade1(i).X, .Screen.Y + .Trade1(i).Y, 32, 32) Then
                               sndBuf.Allocate 2
                               sndBuf.Put_Byte DataCode.User_Trade_RemoveItem
                               sndBuf.Put_Byte i
                               Exit Function
                           End If
                       Next i
                       'Accept button
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Accept.X, .Screen.Y + .Accept.Y, .Accept.Width, .Accept.Height) Then
                           sndBuf.Put_Byte DataCode.User_Trade_Accept
                           Exit Function
                       End If
                       'Finish button
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Trade.X, .Screen.Y + .Trade.Y, .Trade.Width, .Trade.Height) Then
                           sndBuf.Put_Byte DataCode.User_Trade_Finish
                           Exit Function
                       End If
                       'Cancel button
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Cancel.X, .Screen.Y + .Cancel.Y, .Cancel.Width, .Cancel.Height) Then
                           sndBuf.Put_Byte DataCode.User_Trade_Cancel
                           Exit Function
                       End If
                       SelGameWindow = TradeWindow
                   End If
               End With
           End If
       
       Case NPCChatWindow
           If ShowGameWindow(NPCChatWindow) Then
               With GameWindow.NPCChat
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = NPCChatWindow
                       For i = 1 To .NumAnswers
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Answer(i).X, .Screen.Y + .Answer(i).Y, .Answer(i).Width, .Answer(i).Height) Then
                               j = NPCChat(ActiveAsk.ChatIndex).Ask.Ask(ActiveAsk.AskIndex).Answer(i).GotoID
                               If j > 0 Then
                                   Engine_ShowNPCChatWindow ActiveAsk.AskName, ActiveAsk.ChatIndex, j
                               Else
                                   ShowGameWindow(NPCChatWindow) = 0
                                   If LastClickedWindow = NPCChatWindow Then LastClickedWindow = 0
                               End If
                               Exit For
                           End If
                       Next i
                       SelGameWindow = NPCChatWindow
                   End If
               End With
           End If
   
       Case MenuWindow
           If ShowGameWindow(MenuWindow) Then
               With GameWindow.Menu
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = MenuWindow
                       'Quit button
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .QuitLbl.X, .Screen.Y + .QuitLbl.Y, .QuitLbl.Width, .QuitLbl.Height) Then
                           IsUnloading = 1
                           Exit Function
                       End If
                       SelGameWindow = MenuWindow
                   End If
               End With
           End If
           
       Case StatWindow
           If ShowGameWindow(StatWindow) Then
               With GameWindow.StatWindow
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = StatWindow
                       'Raise str
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .AddStr.X, .Screen.Y + .AddStr.Y, .AddStr.Width, .AddStr.Height) Then
                           sndBuf.Allocate 2
                           sndBuf.Put_Byte DataCode.User_BaseStat
                           sndBuf.Put_Byte SID.Str
                       End If
                       'Raise agi
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .AddAgi.X, .Screen.Y + .AddAgi.Y, .AddAgi.Width, .AddAgi.Height) Then
                           sndBuf.Allocate 2
                           sndBuf.Put_Byte DataCode.User_BaseStat
                           sndBuf.Put_Byte SID.Agi
                       End If
                       'Raise mag
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .AddMag.X, .Screen.Y + .AddMag.Y, .AddMag.Width, .AddMag.Height) Then
                           sndBuf.Allocate 2
                           sndBuf.Put_Byte DataCode.User_BaseStat
                           sndBuf.Put_Byte SID.Mag
                       End If
                       SelGameWindow = StatWindow
                   End If
               End With
           End If
           
       Case ChatWindow
           If ShowGameWindow(ChatWindow) Then
               With GameWindow.ChatWindow
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Text.X, .Screen.Y + .Text.Y, .Text.Width, .Text.Height) Then
                           EnterText = True
                       End If
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = ChatWindow
                       SelGameWindow = ChatWindow
                       Exit Function
                   End If
               End With
           End If
       
       Case QuickBarWindow
           If ShowGameWindow(QuickBarWindow) Then
               With GameWindow.QuickBar
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = QuickBarWindow
                       'Cancel changes to quick bar items
                       DrawSkillList = 0
                       QuickBarSetSlot = 0
                       'Check if an item was clicked
                       For i = 1 To 12
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               If GetAsyncKeyState(vbKeyShift) Then
                                   QuickBarSetSlot = i
                                   DrawSkillList = 1
                               Else
                                   Engine_UseQuickBar i
                               End If
                               Exit Function
                           End If
                       Next i
                       'Item was not clicked
                       SelGameWindow = QuickBarWindow
                       Exit Function
                   End If
               End With
           End If
           
       Case InventoryWindow
           If ShowGameWindow(InventoryWindow) Then
               With GameWindow.Inventory
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = InventoryWindow
                       'Check if an item was clicked
                       For i = 1 To MAX_INVENTORY_SLOTS
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               If GetAsyncKeyState(vbKeyShift) Then
                                   If Game_ClickItem(i) Then
                                       If UserInventory(i).Amount = 1 Then
                                           'Drop item into mailbox
                                           If ShowGameWindow(WriteMessageWindow) Then
                                               'Check for duplicate entries
                                               For j = 1 To MaxMailObjs
                                                   If WriteMailData.ObjIndex(j) = i Then Exit Function
                                               Next j
                                               'Place item in next free slot (if any)
                                               j = 0
                                               Do
                                                   j = j + 1
                                                   If j > MaxMailObjs Then Exit Function
                                               Loop While WriteMailData.ObjIndex(j) > 0
                                               WriteMailData.ObjIndex(j) = i
                                               WriteMailData.ObjAmount(j) = 1
                                           'Sell item to shopkeeper
                                           ElseIf ShowGameWindow(ShopWindow) Then
                                               sndBuf.Allocate 4
                                               sndBuf.Put_Byte DataCode.User_Trade_SellToNPC
                                               sndBuf.Put_Byte i
                                               sndBuf.Put_Integer 1
                                           'Put item in the bank
                                           ElseIf ShowGameWindow(BankWindow) Then
                                               sndBuf.Allocate 4
                                               sndBuf.Put_Byte DataCode.User_Bank_PutItem
                                               sndBuf.Put_Byte i
                                               sndBuf.Put_Integer 1
                                           'Drop item on ground
                                           Else
                                               sndBuf.Allocate 4
                                               sndBuf.Put_Byte DataCode.User_Drop
                                               sndBuf.Put_Byte i
                                               sndBuf.Put_Integer 1
                                           End If
                                       Else
                                           'Drop item into mailbox
                                           If ShowGameWindow(WriteMessageWindow) Then
                                               'Check for duplicate entries
                                               For j = 1 To MaxMailObjs
                                                   If WriteMailData.ObjIndex(j) = i Then Exit Function
                                               Next j
                                               'Check for free slots
                                               j = 0
                                               Do
                                                   j = j + 1
                                                   If j > MaxMailObjs Then Exit Function
                                               Loop While WriteMailData.ObjIndex(j) > 0
                                               'Open the amount window
                                               ShowGameWindow(AmountWindow) = 1
                                               LastClickedWindow = AmountWindow
                                               AmountWindowValue = vbNullString
                                               AmountWindowItemIndex = i
                                               AmountWindowUsage = AW_InvToMail
                                           'Sell item to shopkeeper
                                           ElseIf ShowGameWindow(ShopWindow) Then
                                               ShowGameWindow(AmountWindow) = 1
                                               LastClickedWindow = AmountWindow
                                               AmountWindowValue = vbNullString
                                               AmountWindowItemIndex = i
                                               AmountWindowUsage = AW_InvToShop
                                           'Put item in the bank
                                           ElseIf ShowGameWindow(BankWindow) Then
                                               ShowGameWindow(AmountWindow) = 1
                                               LastClickedWindow = AmountWindow
                                               AmountWindowValue = vbNullString
                                               AmountWindowItemIndex = i
                                               AmountWindowUsage = AW_InvToBank
                                           'Drop item on ground
                                           Else
                                               ShowGameWindow(AmountWindow) = 1
                                               LastClickedWindow = AmountWindow
                                               AmountWindowValue = vbNullString
                                               AmountWindowItemIndex = i
                                               AmountWindowUsage = AW_Drop
                                           End If
                                       End If
                                   End If
                               Else
                                   If Game_ClickItem(i) Then
                                       sndBuf.Allocate 2
                                       sndBuf.Put_Byte DataCode.User_Use
                                       sndBuf.Put_Byte i
                                   End If
                               End If
                               Exit Function
                           End If
                       Next i
                       'Item was not clicked
                       SelGameWindow = InventoryWindow
                       Exit Function
                   End If
               End With
           End If
           
       Case ShopWindow
           If ShowGameWindow(ShopWindow) Then
               With GameWindow.Shop
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = ShopWindow
                       'Check if an item was clicked
                       For i = 1 To MAX_INVENTORY_SLOTS
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               If Game_ClickItem(i, 2) > 0 Then
                                   sndBuf.Allocate 4
                                   sndBuf.Put_Byte DataCode.User_Trade_BuyFromNPC
                                   sndBuf.Put_Byte i
                                   sndBuf.Put_Integer 1
                               End If
                               Exit Function
                           End If
                       Next i
                       'Item was not clicked
                       SelGameWindow = ShopWindow
                       Exit Function
                   End If
               End With
           End If
           
       Case BankWindow
           If ShowGameWindow(BankWindow) Then
               With GameWindow.Bank
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = BankWindow
                       'Check if an item was clicked
                       For i = 1 To MAX_INVENTORY_SLOTS
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               If Game_ClickItem(i, 3) > 0 Then
                                   sndBuf.Allocate 4
                                   sndBuf.Put_Byte DataCode.User_Bank_TakeItem
                                   sndBuf.Put_Byte i
                                   sndBuf.Put_Integer 1
                               End If
                               Exit Function
                           End If
                       Next i
                       'Item was not clicked
                       SelGameWindow = BankWindow
                       Exit Function
                   End If
               End With
           End If
           
       Case MailboxWindow
           If ShowGameWindow(MailboxWindow) Then
               With GameWindow.Mailbox
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = MailboxWindow
                       'Check if Write was clicked
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .WriteLbl.X, .Screen.Y + .WriteLbl.Y, .WriteLbl.Width, .WriteLbl.Height) Then
                           For i = 1 To MaxMailObjs
                               WriteMailData.ObjIndex(i) = 0
                               WriteMailData.ObjAmount(i) = 0
                           Next i
                           WriteMailData.Message = vbNullString
                           WriteMailData.Subject = vbNullString
                           WriteMailData.RecieverName = vbNullString
                           ShowGameWindow(MailboxWindow) = 0
                           ShowGameWindow(WriteMessageWindow) = 1
                           LastClickedWindow = WriteMessageWindow
                           Exit Function
                       End If
                       If SelMessage > 0 Then
                           'Check if Delete was clicked
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .DeleteLbl.X, .Screen.Y + .DeleteLbl.Y, .DeleteLbl.Width, .DeleteLbl.Height) Then
                               sndBuf.Allocate 2
                               sndBuf.Put_Byte DataCode.Server_MailDelete
                               sndBuf.Put_Byte SelMessage
                               Exit Function
                           End If
                           'Check if Read was clicked
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .ReadLbl.X, .Screen.Y + .ReadLbl.Y, .ReadLbl.Width, .ReadLbl.Height) Then
                               sndBuf.Allocate 2
                               sndBuf.Put_Byte DataCode.Server_MailMessage
                               sndBuf.Put_Byte SelMessage
                               Exit Function
                           End If
                       End If
                       'Check if List was clicked
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .List.X + .List.X, .Screen.Y + .List.Y, .List.Width, .List.Height) Then
                           For i = 1 To (.List.Height \ Font_Default.CharHeight)
                               If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .List.X + .List.X, .Screen.Y + .List.Y + ((i - 1) * Font_Default.CharHeight), .List.Width, Font_Default.CharHeight) Then
                                   If SelMessage = i Then
                                       sndBuf.Allocate 2
                                       sndBuf.Put_Byte DataCode.Server_MailMessage
                                       sndBuf.Put_Byte i
                                   Else
                                       SelMessage = i
                                   End If
                                   Exit Function
                               End If
                           Next i
                           Exit Function
                       End If
                       SelGameWindow = MailboxWindow
                       Exit Function
                   End If
               End With
           End If
           
       Case ViewMessageWindow
           If ShowGameWindow(ViewMessageWindow) Then
               With GameWindow.ViewMessage
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = ViewMessageWindow
                       'Click an item
                       For i = 1 To MaxMailObjs
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Image(i).X, .Screen.Y + .Image(i).Y, .Image(i).Width, .Image(i).Height) Then
                               sndBuf.Allocate 2
                               sndBuf.Put_Byte DataCode.Server_MailItemTake
                               sndBuf.Put_Byte i
                               Exit Function
                           End If
                       Next i
                       SelGameWindow = ViewMessageWindow
                       Exit Function
                   End If
               End With
           End If
           
       Case WriteMessageWindow
           If ShowGameWindow(WriteMessageWindow) Then
               With GameWindow.WriteMessage
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = WriteMessageWindow
                       'Click From
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .From.X + .Screen.X, .From.Y + .Screen.Y, .From.Width, .From.Height) Then
                           WMSelCon = wmFrom
                           Exit Function
                       End If
                       'Click Subject
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Subject.X + .Screen.X, .Subject.Y + .Screen.Y, .Subject.Width, .Subject.Height) Then
                           WMSelCon = wmSubject
                           Exit Function
                       End If
                       'Click Message
                       If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Message.X + .Screen.X, .Message.Y + .Screen.Y, .Message.Width, .Message.Height) Then
                           WMSelCon = wmMessage
                           Exit Function
                       End If
                       'Click an item
                       For i = 1 To MaxMailObjs
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Image(i).X, .Screen.Y + .Image(i).Y, .Image(i).Width, .Image(i).Height) Then
                               WriteMailData.ObjIndex(i) = 0
                               WriteMailData.ObjAmount(i) = 0
                               Exit Function
                           End If
                       Next i
                       SelGameWindow = WriteMessageWindow
                       Exit Function
                   End If
               End With
           End If
           
       Case AmountWindow
           If ShowGameWindow(AmountWindow) Then
               With GameWindow.Amount
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_LeftClick_Window = 1
                       LastClickedWindow = AmountWindow
                   End If
                   SelGameWindow = AmountWindow
                   Exit Function
               End With
           End If
       
   End Select

End Function

Sub Input_Mouse_Move() '***************************************************************** 'Handles events for when the mouse moves (mostly just game window moving) 'More info: http://www.vbgore.com/GameClient.Input.Input_Mouse_Move '*****************************************************************

   'Make sure engine is running
   If Not EngineRun Then Exit Sub
   'Clear item info display
   ItemDescLines = 0
   'Check if left mouse is pressed
   If MouseLeftDown Then
       Select Case SelGameWindow
               'Move QuickBar
           Case QuickBarWindow
               With GameWindow.QuickBar.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move ChatWindow
           Case ChatWindow
               With GameWindow.ChatWindow.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
                   Engine_UpdateChatArray
               End With
               
               'Move Stat Window
           Case StatWindow
               With GameWindow.StatWindow.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move Inventory
           Case InventoryWindow
               With GameWindow.Inventory.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move Shop
           Case ShopWindow
               With GameWindow.Shop.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move Bank
           Case BankWindow
               With GameWindow.Bank.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move Mailbox
           Case MailboxWindow
               With GameWindow.Mailbox.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move View Message
           Case ViewMessageWindow
               With GameWindow.ViewMessage.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move write message
           Case WriteMessageWindow
               With GameWindow.WriteMessage.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move Amount
           Case AmountWindow
               With GameWindow.Amount.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move Chat window
           Case NPCChatWindow
               With GameWindow.NPCChat.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
               
               'Move the trade window
           Case TradeWindow
               With GameWindow.Trade.Screen
                   .X = .X + MousePosAdd.X
                   .Y = .Y + MousePosAdd.Y
                   If WindowsInScreen Then
                       If .X < 0 Then .X = 0
                       If .Y < 0 Then .Y = 0
                       If .X > ScreenWidth - .Width Then .X = ScreenWidth - .Width
                       If .Y > ScreenHeight - .Height Then .Y = ScreenHeight - .Height
                   End If
               End With
           
       End Select
       
   End If

End Sub

Sub Input_Mouse_RightClick() '***************************************************************** 'Handles mouse right-click events 'More info: http://www.vbgore.com/GameClient.Input.Input_Mouse_RightClick '***************************************************************** Dim tX As Integer Dim tY As Integer Dim i As Long

   'Make sure engine is running
   If Not EngineRun Then Exit Sub
   '***Check for a window click***
   'Start with the last clicked window, then move in order of importance
   If Input_Mouse_RightClick_Window(LastClickedWindow) = 1 Then Exit Sub
   For i = 1 To NumGameWindows
       If Input_Mouse_RightClick_Window(i) = 1 Then Exit Sub
   Next i
                                                               
   'No windows clicked, so a tile click will take place
   'Get the tile positions
   Engine_ConvertCPtoTP MousePos.X, MousePos.Y, tX, tY
   
   'Check if a NPC was clicked that has ASK responses
   For i = 1 To LastChar
       If CharList(i).Pos.X = tX Then
           If CharList(i).Pos.Y = tY Then
               If CharList(i).NPCChatIndex > 0 Then
                   If NPCChat(CharList(i).NPCChatIndex).Ask.StartAsk > 0 Then
                       Engine_ShowNPCChatWindow CharList(i).Name, CharList(i).NPCChatIndex, NPCChat(CharList(i).NPCChatIndex).Ask.StartAsk
                   End If
               End If
               Exit For
           End If
       End If
   Next i
   'Normal click
   If UseClickWarp = 0 Then
       
       'Check if a sign was clicked
       If MapData(tX, tY).Sign Then Engine_AddToChatTextBuffer Replace$(Message(126), "<text>", Signs(MapData(tX, tY).Sign)), FontColor_Info
       
       'Send left click
       sndBuf.Allocate 3
       sndBuf.Put_Byte DataCode.User_RightClick
       sndBuf.Put_Byte CByte(tX)
       sndBuf.Put_Byte CByte(tY)
       
   'Warp click
   Else
   
       sndBuf.Allocate 3
       sndBuf.Put_Byte DataCode.GM_Warp
       sndBuf.Put_Byte CByte(tX)
       sndBuf.Put_Byte CByte(tY)
       
   End If

End Sub

Function Input_Mouse_RightClick_Window(ByVal WindowIndex As Byte) As Byte '***************************************************************** 'If a game window was right-clicked 'More info: http://www.vbgore.com/GameClient.Input.Input_Mouse_RightClick_Window '***************************************************************** Dim i As Integer

   Select Case WindowIndex
   
       Case TradeWindow
           If ShowGameWindow(TradeWindow) Then
               With GameWindow.Trade
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = TradeWindow
                       'Item window for user 1
                       For i = 1 To 9
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Trade1(i).X, .Screen.Y + .Trade1(i).Y, 32, 32) Then
                               If TradeTable.Trade1(i).Grh > 0 Then
                                   Engine_SetItemDesc TradeTable.Trade1(i).Name, TradeTable.Trade1(i).Amount, TradeTable.Trade1(i).Value
                                   Exit Function
                               End If
                           End If
                       Next i
                       'Item window for user 2
                       For i = 1 To 9
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Trade2(i).X, .Screen.Y + .Trade2(i).Y, 32, 32) Then
                               If TradeTable.Trade2(i).Grh > 0 Then
                                   Engine_SetItemDesc TradeTable.Trade2(i).Name, TradeTable.Trade2(i).Amount, TradeTable.Trade2(i).Value
                                   Exit Function
                               End If
                           End If
                       Next i
                   End If
               End With
           End If
       
       Case QuickBarWindow
           If ShowGameWindow(QuickBarWindow) Then
               With GameWindow.QuickBar
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = QuickBarWindow
                       'Check if an item was clicked
                       For i = 1 To 12
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               'An item in the quickbar was clicked - get description
                               If QuickBarID(i).Type = QuickBarType_Item Then
                                   Engine_SetItemDesc UserInventory(QuickBarID(i).ID).Name, UserInventory(QuickBarID(i).ID).Amount
                                   'A skill in the quickbar was clicked - get the name
                               ElseIf QuickBarID(i).Type = QuickBarType_Skill Then
                                   Engine_SetItemDesc Engine_SkillIDtoSkillName(QuickBarID(i).ID)
                               End If
                               Exit Function
                           End If
                       Next i
                   End If
               End With
           End If
           
       Case InventoryWindow
           If ShowGameWindow(InventoryWindow) Then
               With GameWindow.Inventory
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = InventoryWindow
                       'Check if an item was clicked
                       For i = 1 To MAX_INVENTORY_SLOTS
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               If UserInventory(i).GrhIndex > 0 Then
                                   Engine_SetItemDesc UserInventory(i).Name, UserInventory(i).Amount, UserInventory(i).Value
                                   DragSourceWindow = InventoryWindow
                                   DragItemSlot = i
                               End If
                               Exit Function
                           End If
                       Next i
                   End If
               End With
           End If
           
       Case ShopWindow
           If ShowGameWindow(ShopWindow) Then
               With GameWindow.Shop
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = ShopWindow
                       'Check if an item was clicked
                       For i = 1 To MAX_INVENTORY_SLOTS
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               If i <= NPCTradeItemArraySize Then
                                   If NPCTradeItems(i).GrhIndex > 0 Then
                                       Engine_SetItemDesc NPCTradeItems(i).Name, 0, NPCTradeItems(i).Value
                                       DragSourceWindow = ShopWindow
                                       DragItemSlot = i
                                   End If
                               End If
                               Exit Function
                           End If
                       Next i
                   End If
               End With
           End If
           
       Case BankWindow
           If ShowGameWindow(BankWindow) Then
               With GameWindow.Bank
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = BankWindow
                       'Check if an item was clicked
                       For i = 1 To MAX_INVENTORY_SLOTS
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               If UserBank(i).GrhIndex > 0 Then Engine_SetItemDesc UserBank(i).Name, UserBank(i).Amount
                               DragSourceWindow = BankWindow
                               DragItemSlot = i
                               Exit Function
                           End If
                       Next i
                   End If
               End With
           End If
           
       Case ViewMessageWindow
           If ShowGameWindow(ViewMessageWindow) Then
               With GameWindow.ViewMessage
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = ViewMessageWindow
                       'Click an item
                       For i = 1 To MaxMailObjs
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Image(i).X, .Screen.Y + .Image(i).Y, .Image(i).Width, .Image(i).Height) Then
                               Engine_SetItemDesc ReadMailData.ObjName(i), ReadMailData.ObjAmount(i)
                               Exit Function
                           End If
                       Next i
                   End If
               End With
           End If
           
       Case WriteMessageWindow
           If ShowGameWindow(WriteMessageWindow) Then
               With GameWindow.WriteMessage
                   'Check if the screen was clicked
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = WriteMessageWindow
                       'Click an item
                       For i = 1 To MaxMailObjs
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X + .Image(i).X, .Screen.Y + .Image(i).Y, .Image(i).Width, .Image(i).Height) Then
                               Engine_SetItemDesc UserInventory(WriteMailData.ObjIndex(i)).Name, WriteMailData.ObjAmount(i)
                               Exit Function
                           End If
                       Next i
                   End If
               End With
           End If
           
           
       Case ChatWindow
           If ShowGameWindow(ChatWindow) Then
               With GameWindow.ChatWindow
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = ChatWindow
                   End If
               End With
           End If
       
       Case MenuWindow
           If ShowGameWindow(MenuWindow) Then
               With GameWindow.Menu
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = MenuWindow
                   End If
               End With
           End If
           
       Case StatWindow
           If ShowGameWindow(StatWindow) Then
               With GameWindow.StatWindow
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = StatWindow
                   End If
               End With
           End If
           
       Case ViewMessageWindow
           If ShowGameWindow(ViewMessageWindow) Then
               With GameWindow.ViewMessage
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = ViewMessageWindow
                   End If
               End With
           End If
           
       Case AmountWindow
           If ShowGameWindow(AmountWindow) Then
               With GameWindow.Amount
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = AmountWindow
                   End If
               End With
           End If
           
       Case NPCChatWindow
           If ShowGameWindow(NPCChatWindow) Then
               With GameWindow.NPCChat
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       Input_Mouse_RightClick_Window = 1
                       LastClickedWindow = NPCChatWindow
                   End If
               End With
           End If
   
   End Select

End Function

Sub Input_Mouse_RightRelease() '***************************************************************** 'Right mouse button released events 'More info: http://www.vbgore.com/GameClient.Input.Input_Mouse_RightRelease '***************************************************************** Dim i As Byte

   'Check if we released mouse and have an item in being dragged
   If DragItemSlot Then
   
       'Inventory -> Trade Window
       If DragSourceWindow = InventoryWindow Then
           If ShowGameWindow(TradeWindow) Then
               With GameWindow.Trade
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       For i = 1 To 9
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Trade1(i).X + .Screen.X, .Trade1(i).Y + .Screen.Y, 32, 32) Then
                               If UserInventory(DragItemSlot).Amount = 1 Then
                                   sndBuf.Put_Byte DataCode.User_Trade_UpdateTrade
                                   sndBuf.Put_Byte DragItemSlot
                                   sndBuf.Put_Long 1
                               Else
                                   ShowGameWindow(AmountWindow) = 1
                                   LastClickedWindow = AmountWindow
                                   AmountWindowItemIndex = DragItemSlot
                                   AmountWindowValue = vbNullString
                                   AmountWindowUsage = AW_InvToTrade
                               End If
                               
                               'Clear and leave
                               DragSourceWindow = 0
                               DragItemSlot = 0
                               
                               Exit Sub
                               
                           End If
                           
                       Next i
                   End If
               End With
           End If
       End If
       
       'Inventory -> Inventory (change slot)
       If DragSourceWindow = InventoryWindow Then
           If ShowGameWindow(InventoryWindow) Then
               With GameWindow.Inventory
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       For i = 1 To MAX_INVENTORY_SLOTS
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               If DragItemSlot <> i Then
                                   'Switch slots
                                   sndBuf.Allocate 3
                                   sndBuf.Put_Byte DataCode.User_ChangeInvSlot
                                   sndBuf.Put_Byte DragItemSlot
                                   sndBuf.Put_Byte i
                                   'Clear and leave
                                   DragSourceWindow = 0
                                   DragItemSlot = 0
                                   Exit Sub
                               End If
                           End If
                       Next i
                       'Clear and leave
                       DragSourceWindow = 0
                       DragItemSlot = 0
                       Exit Sub
                   End If
               End With
           End If
       End If
       'Inventory -> Quick Bar
       If DragSourceWindow = InventoryWindow Then
           If ShowGameWindow(QuickBarWindow) Then
               With GameWindow.QuickBar
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       For i = 1 To 12
                           If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Image(i).X + .Screen.X, .Image(i).Y + .Screen.Y, .Image(i).Width, .Image(i).Height) Then
                               'Drop into quick use slot
                               QuickBarID(i).Type = QuickBarType_Item
                               QuickBarID(i).ID = DragItemSlot
                               'Clear and leave
                               DragSourceWindow = 0
                               DragItemSlot = 0
                               Exit Sub
                           End If
                       Next i
                   End If
               End With
           End If
       End If
       
       'Inventory -> Depot
       If DragSourceWindow = InventoryWindow Then
           If ShowGameWindow(BankWindow) Then
               With GameWindow.Bank
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       'Single item
                       If UserInventory(DragItemSlot).Amount = 1 Then
                           sndBuf.Allocate 4
                           sndBuf.Put_Byte DataCode.User_Bank_PutItem
                           sndBuf.Put_Byte DragItemSlot
                           sndBuf.Put_Integer 1
                       'Multiple items
                       Else
                           ShowGameWindow(AmountWindow) = 1
                           LastClickedWindow = AmountWindow
                           AmountWindowValue = vbNullString
                           AmountWindowItemIndex = DragItemSlot
                           AmountWindowUsage = AW_InvToBank
                       End If
                       'Clear and leave
                       DragSourceWindow = 0
                       DragItemSlot = 0
                   End If
               End With
           End If
       End If
       
       'Inventory -> Shop
       If DragSourceWindow = InventoryWindow Then
           If ShowGameWindow(ShopWindow) Then
               With GameWindow.Shop
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       'Single item
                       If UserInventory(DragItemSlot).Amount = 1 Then
                           sndBuf.Allocate 4
                           sndBuf.Put_Byte DataCode.User_Trade_SellToNPC
                           sndBuf.Put_Byte DragItemSlot
                           sndBuf.Put_Integer 1
                       'Multiple items
                       Else
                           ShowGameWindow(AmountWindow) = 1
                           LastClickedWindow = AmountWindow
                           AmountWindowValue = vbNullString
                           AmountWindowItemIndex = DragItemSlot
                           AmountWindowUsage = AW_InvToShop
                       End If
                       'Clear and leave
                       DragSourceWindow = 0
                       DragItemSlot = 0
                       Exit Sub
                   End If
               End With
           End If
       End If
       
       'Shop -> Inventory
       If DragSourceWindow = ShopWindow Then
           If ShowGameWindow(InventoryWindow) Then
               With GameWindow.Inventory
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       'Bring up amount window for bulk buying
                       ShowGameWindow(AmountWindow) = 1
                       LastClickedWindow = AmountWindow
                       AmountWindowValue = vbNullString
                       AmountWindowItemIndex = DragItemSlot
                       AmountWindowUsage = AW_ShopToInv
                       'Clear and leave
                       DragSourceWindow = 0
                       DragItemSlot = 0
                       Exit Sub
                   End If
               End With
           End If
       End If
       
       'Bank -> Inventory
       If DragSourceWindow = BankWindow Then
           If ShowGameWindow(InventoryWindow) Then
               With GameWindow.Inventory
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       If UserBank(DragItemSlot).Amount > 1 Then
                           'Bring up amount window for bulk withdrawing
                           ShowGameWindow(AmountWindow) = 1
                           LastClickedWindow = AmountWindow
                           AmountWindowValue = vbNullString
                           AmountWindowItemIndex = DragItemSlot
                           AmountWindowUsage = AW_BankToInv
                       Else
                           sndBuf.Allocate 4
                           sndBuf.Put_Byte DataCode.User_Bank_TakeItem
                           sndBuf.Put_Byte DragItemSlot
                           sndBuf.Put_Integer 1
                       End If
                       'Clear and leave
                       DragSourceWindow = 0
                       DragItemSlot = 0
                       Exit Sub
                   End If
               End With
           End If
       End If
                               
       'Inventory -> Mail
       If DragSourceWindow = InventoryWindow Then
           If ShowGameWindow(WriteMessageWindow) Then
               With GameWindow.WriteMessage
                   If Engine_Collision_Rect(MousePos.X, MousePos.Y, 1, 1, .Screen.X, .Screen.Y, .Screen.Width, .Screen.Height) Then
                       'Single item
                       If UserInventory(DragItemSlot).Amount = 1 Then
                           'Check for duplicate entries
                           For i = 1 To MaxMailObjs
                               If WriteMailData.ObjIndex(i) = DragItemSlot Then
                                   DragSourceWindow = 0
                                   DragItemSlot = 0
                                   Exit Sub
                               End If
                           Next i
                           'Place item in next free slot (if any)
                           i = 0
                           Do
                               i = i + 1
                               If i > MaxMailObjs Then
                                   DragSourceWindow = 0
                                   DragItemSlot = 0
                                   Exit Sub
                               End If
                           Loop While WriteMailData.ObjIndex(i) > 0
                           WriteMailData.ObjIndex(i) = DragItemSlot
                           WriteMailData.ObjAmount(i) = 1
                       'Multiple items
                       Else
                           ShowGameWindow(AmountWindow) = 1
                           LastClickedWindow = AmountWindow
                           AmountWindowValue = vbNullString
                           AmountWindowItemIndex = DragItemSlot
                           AmountWindowUsage = AW_InvToMail
                       End If
                       'Clear and leave
                       DragSourceWindow = 0
                       DragItemSlot = 0
                       Exit Sub
                   End If
               End With
           End If
       End If
       
       'Inventory -> Ground
       If DragSourceWindow = InventoryWindow Then
           'Single item
           If UserInventory(DragItemSlot).Amount = 1 Then
               sndBuf.Allocate 4
               sndBuf.Put_Byte DataCode.User_Drop
               sndBuf.Put_Byte DragItemSlot
               sndBuf.Put_Integer 1
           'Multiple items
           Else
               ShowGameWindow(AmountWindow) = 1
               LastClickedWindow = AmountWindow
               AmountWindowValue = vbNullString
               AmountWindowItemIndex = DragItemSlot
               AmountWindowUsage = AW_Drop
           End If
           'Clear and leave
           DragSourceWindow = 0
           DragItemSlot = 0
           Exit Sub
       End If
       'Didn't release over a valid area
       DragSourceWindow = 0
       DragItemSlot = 0
   End If

End Sub </vb>

Particles

<vb> Option Explicit Private Type Effect

   X As Single                 'Location of effect
   Y As Single
   GoToX As Single             'Location to move to
   GoToY As Single
   KillWhenAtTarget As Boolean     'If the effect is at its target (GoToX/Y), then Progression is set to 0
   KillWhenTargetLost As Boolean   'Kill the effect if the target is lost (sets progression = 0)
   Gfx As Byte                 'Particle texture used
   Used As Boolean             'If the effect is in use
   EffectNum As Byte           'What number of effect that is used
   Modifier As Integer         'Misc variable (depends on the effect)
   FloatSize As Long           'The size of the particles
   Direction As Integer        'Misc variable (depends on the effect)
   Particles() As Particle     'Information on each particle
   Progression As Single       'Progression state, best to design where 0 = effect ends
   PartVertex() As TLVERTEX    'Used to point render particles
   PreviousFrame As Long       'Tick time of the last frame
   ParticleCount As Integer    'Number of particles total
   ParticlesLeft As Integer    'Number of particles left - only for non-repetitive effects
   BindToChar As Integer       'Setting this value will bind the effect to move towards the character
   BindSpeed As Single         'How fast the effect moves towards the character
   BoundToMap As Byte          'If the effect is bound to the map or not (used only by the map editor)

End Type Public NumEffects As Byte 'Maximum number of effects at once Public Effect() As Effect 'List of all the active effects

'Constants With The Order Number For Each Effect Public Const EffectNum_Fire As Byte = 1 'Burn baby, burn! Flame from a central point that blows in a specified direction Public Const EffectNum_Snow As Byte = 2 'Snow that covers the screen - weather effect Public Const EffectNum_Heal As Byte = 3 'Healing effect that can bind to a character, ankhs float up and fade Public Const EffectNum_Bless As Byte = 4 'Following three effects are same: create a circle around the central point Public Const EffectNum_Protection As Byte = 5 ' (often the character) and makes the given particle on the perimeter Public Const EffectNum_Strengthen As Byte = 6 ' which float up and fade out Public Const EffectNum_Rain As Byte = 7 'Exact same as snow, but moves much faster and more alpha value - weather effect Public Const EffectNum_EquationTemplate As Byte = 8 'Template for creating particle effects through equations - a page with some equations can be found here: http://www.vbgore.com/modules.php?name=Forums&file=viewtopic&t=221 Public Const EffectNum_Waterfall As Byte = 9 'Waterfall effect Public Const EffectNum_Summon As Byte = 10 'Summon effect

Private Declare Sub ZeroMemory Lib "kernel32.dll" Alias "RtlZeroMemory" (ByRef Destination As Any, ByVal Length As Long)

Function Effect_EquationTemplate_Begin(ByVal X As Single, ByVal Y As Single, ByVal Gfx As Integer, ByVal Particles As Integer, Optional ByVal Progression As Single = 1) As Integer '***************************************************************** 'Particle effect template for effects as described on the 'wiki page: http://www.vbgore.com/Particle_effect_equations 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_EquationTemplate_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_EquationTemplate_Begin = EffectIndex
   'Set The Effect's Variables
   Effect(EffectIndex).EffectNum = EffectNum_EquationTemplate  'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles       'Set the number of particles
   Effect(EffectIndex).Used = True                     'Enable the effect
   Effect(EffectIndex).X = X                           'Set the effect's X coordinate
   Effect(EffectIndex).Y = Y                           'Set the effect's Y coordinate
   Effect(EffectIndex).Gfx = Gfx                       'Set the graphic
   Effect(EffectIndex).Progression = Progression       'If we loop the effect
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(8)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_EquationTemplate_Reset EffectIndex, LoopC
   Next LoopC
   'Set The Initial Time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_EquationTemplate_Reset(ByVal EffectIndex As Integer, ByVal Index As Long) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_EquationTemplate_Reset '***************************************************************** Dim X As Single Dim Y As Single Dim R As Single

   Effect(EffectIndex).Progression = Effect(EffectIndex).Progression + 0.1
   R = (Index / 20) * EXP(Index / Effect(EffectIndex).Progression Mod 3)
   X = R * Cos(Index)
   Y = R * Sin(Index)
   
   'Reset the particle
   Effect(EffectIndex).Particles(Index).ResetIt Effect(EffectIndex).X + X, Effect(EffectIndex).Y + Y, 0, 0, 0, 0
   Effect(EffectIndex).Particles(Index).ResetColor 1, 1, 1, 1, 0.2 + (Rnd * 0.2)

End Sub

Private Sub Effect_EquationTemplate_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_EquationTemplate_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long

   'Calculate The Time Difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   'Go Through The Particle Loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       'Check If Particle Is In Use
       If Effect(EffectIndex).Particles(LoopC).Used Then
           'Update The Particle
           Effect(EffectIndex).Particles(LoopC).UpdateParticle ElapsedTime
           'Check if the particle is ready to die
           If Effect(EffectIndex).Particles(LoopC).sngA <= 0 Then
               'Check if the effect is ending
               If Effect(EffectIndex).Progression > 0 Then
                   'Reset the particle
                   Effect_EquationTemplate_Reset EffectIndex, LoopC
               Else
                   'Disable the particle
                   Effect(EffectIndex).Particles(LoopC).Used = False
                   'Subtract from the total particle count
                   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticlesLeft - 1
                   'Check if the effect is out of particles
                   If Effect(EffectIndex).ParticlesLeft = 0 Then Effect(EffectIndex).Used = False
                   'Clear the color (dont leave behind any artifacts)
                   Effect(EffectIndex).PartVertex(LoopC).Color = 0
               End If
           Else
               'Set the particle information on the particle vertex
               Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(Effect(EffectIndex).Particles(LoopC).sngR, Effect(EffectIndex).Particles(LoopC).sngG, Effect(EffectIndex).Particles(LoopC).sngB, Effect(EffectIndex).Particles(LoopC).sngA)
               Effect(EffectIndex).PartVertex(LoopC).X = Effect(EffectIndex).Particles(LoopC).sngX
               Effect(EffectIndex).PartVertex(LoopC).Y = Effect(EffectIndex).Particles(LoopC).sngY
           End If
       End If
   Next LoopC

End Sub

Function Effect_Bless_Begin(ByVal X As Single, ByVal Y As Single, ByVal Gfx As Integer, ByVal Particles As Integer, Optional ByVal Size As Byte = 30, Optional ByVal Time As Single = 10) As Integer '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Bless_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_Bless_Begin = EffectIndex
   'Set The Effect's Variables
   Effect(EffectIndex).EffectNum = EffectNum_Bless     'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles       'Set the number of particles
   Effect(EffectIndex).Used = True             'Enabled the effect
   Effect(EffectIndex).X = X                   'Set the effect's X coordinate
   Effect(EffectIndex).Y = Y                   'Set the effect's Y coordinate
   Effect(EffectIndex).Gfx = Gfx               'Set the graphic
   Effect(EffectIndex).Modifier = Size         'How large the circle is
   Effect(EffectIndex).Progression = Time      'How long the effect will last
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(20)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_Bless_Reset EffectIndex, LoopC
   Next LoopC
   'Set The Initial Time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_Bless_Reset(ByVal EffectIndex As Integer, ByVal Index As Long) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Bless_Reset '***************************************************************** Dim a As Single Dim X As Single Dim Y As Single

   'Get the positions
   a = Rnd * 360 * DegreeToRadian
   X = Effect(EffectIndex).X - (Sin(a) * Effect(EffectIndex).Modifier)
   Y = Effect(EffectIndex).Y + (Cos(a) * Effect(EffectIndex).Modifier)
   'Reset the particle
   Effect(EffectIndex).Particles(Index).ResetIt X, Y, 0, Rnd * -1, 0, -2
   Effect(EffectIndex).Particles(Index).ResetColor 1, 1, 0.2, 0.6 + (Rnd * 0.4), 0.06 + (Rnd * 0.2)

End Sub

Private Sub Effect_Bless_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Bless_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long

   'Calculate The Time Difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   'Update the life span
   If Effect(EffectIndex).Progression > 0 Then Effect(EffectIndex).Progression = Effect(EffectIndex).Progression - ElapsedTime
   'Go Through The Particle Loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       'Check If Particle Is In Use
       If Effect(EffectIndex).Particles(LoopC).Used Then
           'Update The Particle
           Effect(EffectIndex).Particles(LoopC).UpdateParticle ElapsedTime
           'Check if the particle is ready to die
           If Effect(EffectIndex).Particles(LoopC).sngA <= 0 Then
               'Check if the effect is ending
               If Effect(EffectIndex).Progression > 0 Then
                   'Reset the particle
                   Effect_Bless_Reset EffectIndex, LoopC
               Else
                   'Disable the particle
                   Effect(EffectIndex).Particles(LoopC).Used = False
                   'Subtract from the total particle count
                   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticlesLeft - 1
                   'Check if the effect is out of particles
                   If Effect(EffectIndex).ParticlesLeft = 0 Then Effect(EffectIndex).Used = False
                   'Clear the color (dont leave behind any artifacts)
                   Effect(EffectIndex).PartVertex(LoopC).Color = 0
               End If
           Else
               'Set the particle information on the particle vertex
               Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(Effect(EffectIndex).Particles(LoopC).sngR, Effect(EffectIndex).Particles(LoopC).sngG, Effect(EffectIndex).Particles(LoopC).sngB, Effect(EffectIndex).Particles(LoopC).sngA)
               Effect(EffectIndex).PartVertex(LoopC).X = Effect(EffectIndex).Particles(LoopC).sngX
               Effect(EffectIndex).PartVertex(LoopC).Y = Effect(EffectIndex).Particles(LoopC).sngY
           End If
       End If
   Next LoopC

End Sub

Function Effect_Fire_Begin(ByVal X As Single, ByVal Y As Single, ByVal Gfx As Integer, ByVal Particles As Integer, Optional ByVal Direction As Integer = 180, Optional ByVal Progression As Single = 1) As Integer '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Fire_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_Fire_Begin = EffectIndex
   'Set The Effect's Variables
   Effect(EffectIndex).EffectNum = EffectNum_Fire      'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles       'Set the number of particles
   Effect(EffectIndex).Used = True     'Enabled the effect
   Effect(EffectIndex).X = X           'Set the effect's X coordinate
   Effect(EffectIndex).Y = Y           'Set the effect's Y coordinate
   Effect(EffectIndex).Gfx = Gfx       'Set the graphic
   Effect(EffectIndex).Direction = Direction       'The direction the effect is animat
   Effect(EffectIndex).Progression = Progression   'Loop the effect
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(15)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_Fire_Reset EffectIndex, LoopC
   Next LoopC
   'Set The Initial Time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_Fire_Reset(ByVal EffectIndex As Integer, ByVal Index As Long) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Fire_Reset '*****************************************************************

   'Reset the particle
   Effect(EffectIndex).Particles(Index).ResetIt Effect(EffectIndex).X - 10 + Rnd * 20, Effect(EffectIndex).Y - 10 + Rnd * 20, -Sin((Effect(EffectIndex).Direction + (Rnd * 70) - 35) * DegreeToRadian) * 8, Cos((Effect(EffectIndex).Direction + (Rnd * 70) - 35) * DegreeToRadian) * 8, 0, 0
   Effect(EffectIndex).Particles(Index).ResetColor 1, 0.2, 0.2, 0.4 + (Rnd * 0.2), 0.03 + (Rnd * 0.07)

End Sub

Private Sub Effect_Fire_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Fire_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long

   'Calculate The Time Difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   'Go Through The Particle Loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       'Check If Particle Is In Use
       If Effect(EffectIndex).Particles(LoopC).Used Then
           'Update The Particle
           Effect(EffectIndex).Particles(LoopC).UpdateParticle ElapsedTime
           'Check if the particle is ready to die
           If Effect(EffectIndex).Particles(LoopC).sngA <= 0 Then
               'Check if the effect is ending
               If Effect(EffectIndex).Progression <> 0 Then
                   'Reset the particle
                   Effect_Fire_Reset EffectIndex, LoopC
               Else
                   'Disable the particle
                   Effect(EffectIndex).Particles(LoopC).Used = False
                   'Subtract from the total particle count
                   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticlesLeft - 1
                   'Check if the effect is out of particles
                   If Effect(EffectIndex).ParticlesLeft = 0 Then Effect(EffectIndex).Used = False
                   'Clear the color (dont leave behind any artifacts)
                   Effect(EffectIndex).PartVertex(LoopC).Color = 0
               End If
           Else
               'Set the particle information on the particle vertex
               Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(Effect(EffectIndex).Particles(LoopC).sngR, Effect(EffectIndex).Particles(LoopC).sngG, Effect(EffectIndex).Particles(LoopC).sngB, Effect(EffectIndex).Particles(LoopC).sngA)
               Effect(EffectIndex).PartVertex(LoopC).X = Effect(EffectIndex).Particles(LoopC).sngX
               Effect(EffectIndex).PartVertex(LoopC).Y = Effect(EffectIndex).Particles(LoopC).sngY
           End If
       End If
   Next LoopC

End Sub

Private Function Effect_FToDW(F As Single) As Long '***************************************************************** 'Converts a float to a D-Word, or in Visual Basic terms, a Single to a Long 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_FToDW '***************************************************************** Dim Buf As D3DXBuffer

   'Converts a single into a long (Float to DWORD)
   Set Buf = D3DX.CreateBuffer(4)
   D3DX.BufferSetData Buf, 0, 4, 1, F
   D3DX.BufferGetData Buf, 0, 4, 1, Effect_FToDW

End Function

Function Effect_Heal_Begin(ByVal X As Single, ByVal Y As Single, ByVal Gfx As Integer, ByVal Particles As Integer, Optional ByVal Progression As Single = 1) As Integer '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Heal_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_Heal_Begin = EffectIndex
   'Set The Effect's Variables
   Effect(EffectIndex).EffectNum = EffectNum_Heal      'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles       'Set the number of particles
   Effect(EffectIndex).Used = True     'Enabled the effect
   Effect(EffectIndex).X = X           'Set the effect's X coordinate
   Effect(EffectIndex).Y = Y           'Set the effect's Y coordinate
   Effect(EffectIndex).Gfx = Gfx       'Set the graphic
   Effect(EffectIndex).Progression = Progression   'Loop the effect
   Effect(EffectIndex).KillWhenAtTarget = True     'End the effect when it reaches the target (progression = 0)
   Effect(EffectIndex).KillWhenTargetLost = True   'End the effect if the target is lost (progression = 0)
   
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(16)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_Heal_Reset EffectIndex, LoopC
   Next LoopC
   'Set The Initial Time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_Heal_Reset(ByVal EffectIndex As Integer, ByVal Index As Long) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Heal_Reset '*****************************************************************

   'Reset the particle
   Effect(EffectIndex).Particles(Index).ResetIt Effect(EffectIndex).X - 10 + Rnd * 20, Effect(EffectIndex).Y - 10 + Rnd * 20, -Sin((180 + (Rnd * 90) - 45) * 0.0174533) * 8 + (Rnd * 3), Cos((180 + (Rnd * 90) - 45) * 0.0174533) * 8 + (Rnd * 3), 0, 0
   Effect(EffectIndex).Particles(Index).ResetColor 0.8, 0.2, 0.2, 0.6 + (Rnd * 0.2), 0.01 + (Rnd * 0.5)
   

End Sub

Private Sub Effect_Heal_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Heal_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long Dim i As Integer

   'Calculate the time difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   
   'Go through the particle loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       'Check If Particle Is In Use
       If Effect(EffectIndex).Particles(LoopC).Used Then
           'Update The Particle
           Effect(EffectIndex).Particles(LoopC).UpdateParticle ElapsedTime
           'Check if the particle is ready to die
           If Effect(EffectIndex).Particles(LoopC).sngA <= 0 Then
               'Check if the effect is ending
               If Effect(EffectIndex).Progression <> 0 Then
                   'Reset the particle
                   Effect_Heal_Reset EffectIndex, LoopC
               Else
                   'Disable the particle
                   Effect(EffectIndex).Particles(LoopC).Used = False
                   'Subtract from the total particle count
                   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticlesLeft - 1
                   'Check if the effect is out of particles
                   If Effect(EffectIndex).ParticlesLeft = 0 Then Effect(EffectIndex).Used = False
                   'Clear the color (dont leave behind any artifacts)
                   Effect(EffectIndex).PartVertex(LoopC).Color = 0
               End If
           Else
               
               'Set the particle information on the particle vertex
               Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(Effect(EffectIndex).Particles(LoopC).sngR, Effect(EffectIndex).Particles(LoopC).sngG, Effect(EffectIndex).Particles(LoopC).sngB, Effect(EffectIndex).Particles(LoopC).sngA)
               Effect(EffectIndex).PartVertex(LoopC).X = Effect(EffectIndex).Particles(LoopC).sngX
               Effect(EffectIndex).PartVertex(LoopC).Y = Effect(EffectIndex).Particles(LoopC).sngY
           End If
       End If
   Next LoopC

End Sub

Sub Effect_Kill(ByVal EffectIndex As Integer, Optional ByVal KillAll As Boolean = False) '***************************************************************** 'Kills (stops) a single effect or all effects 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Kill '***************************************************************** Dim LoopC As Long

   'Check If To Kill All Effects
   If KillAll = True Then
       'Loop Through Every Effect
       For LoopC = 1 To NumEffects
           'Stop The Effect
           Effect(LoopC).Used = False
       Next
       
   Else
       'Stop The Selected Effect
       Effect(EffectIndex).Used = False
       
   End If

End Sub

Private Function Effect_NextOpenSlot() As Integer '***************************************************************** 'Finds the next open effects index 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_NextOpenSlot '***************************************************************** Dim EffectIndex As Integer

   'Find The Next Open Effect Slot
   Do
       EffectIndex = EffectIndex + 1   'Check The Next Slot
       If EffectIndex > NumEffects Then    'Dont Go Over Maximum Amount
           Effect_NextOpenSlot = -1
           Exit Function
       End If
   Loop While Effect(EffectIndex).Used = True    'Check Next If Effect Is In Use
   'Return the next open slot
   Effect_NextOpenSlot = EffectIndex
   'Clear the old information from the effect
   Erase Effect(EffectIndex).Particles()
   Erase Effect(EffectIndex).PartVertex()
   ZeroMemory Effect(EffectIndex), LenB(Effect(EffectIndex))
   Effect(EffectIndex).GoToX = -30000
   Effect(EffectIndex).GoToY = -30000

End Function

Function Effect_Protection_Begin(ByVal X As Single, ByVal Y As Single, ByVal Gfx As Integer, ByVal Particles As Integer, Optional ByVal Size As Byte = 30, Optional ByVal Time As Single = 10) As Integer '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Protection_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_Protection_Begin = EffectIndex
   'Set The Effect's Variables
   Effect(EffectIndex).EffectNum = EffectNum_Protection    'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles           'Set the number of particles
   Effect(EffectIndex).Used = True             'Enabled the effect
   Effect(EffectIndex).X = X                   'Set the effect's X coordinate
   Effect(EffectIndex).Y = Y                   'Set the effect's Y coordinate
   Effect(EffectIndex).Gfx = Gfx               'Set the graphic
   Effect(EffectIndex).Modifier = Size         'How large the circle is
   Effect(EffectIndex).Progression = Time      'How long the effect will last
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(20)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_Protection_Reset EffectIndex, LoopC
   Next LoopC
   'Set The Initial Time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_Protection_Reset(ByVal EffectIndex As Integer, ByVal Index As Long) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Protection_Reset '***************************************************************** Dim a As Single Dim X As Single Dim Y As Single

   'Get the positions
   a = Rnd * 360 * DegreeToRadian
   X = Effect(EffectIndex).X - (Sin(a) * Effect(EffectIndex).Modifier)
   Y = Effect(EffectIndex).Y + (Cos(a) * Effect(EffectIndex).Modifier)
   'Reset the particle
   Effect(EffectIndex).Particles(Index).ResetIt X, Y, 0, Rnd * -1, 0, -2
   Effect(EffectIndex).Particles(Index).ResetColor 0.1, 0.1, 0.9, 0.6 + (Rnd * 0.4), 0.06 + (Rnd * 0.2)

End Sub

Private Sub Effect_UpdateOffset(ByVal EffectIndex As Integer) '*************************************************** 'Update an effect's position if the screen has moved 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_UpdateOffset '***************************************************

   Effect(EffectIndex).X = Effect(EffectIndex).X + (LastOffsetX - ParticleOffsetX)
   Effect(EffectIndex).Y = Effect(EffectIndex).Y + (LastOffsetY - ParticleOffsetY)

End Sub

Private Sub Effect_UpdateBinding(ByVal EffectIndex As Integer)

'*************************************************** 'Updates the binding of a particle effect to a target, if 'the effect is bound to a character 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_UpdateBinding '*************************************************** Dim TargetI As Integer Dim TargetA As Single

   'Update position through character binding
   If Effect(EffectIndex).BindToChar > 0 Then

       'Store the character index
       TargetI = Effect(EffectIndex).BindToChar

       'Check for a valid binding index
       If TargetI > LastChar Then
           Effect(EffectIndex).BindToChar = 0
           If Effect(EffectIndex).KillWhenTargetLost Then
               Effect(EffectIndex).Progression = 0
               Exit Sub
           End If
       ElseIf CharList(TargetI).Active = 0 Then
           Effect(EffectIndex).BindToChar = 0
           If Effect(EffectIndex).KillWhenTargetLost Then
               Effect(EffectIndex).Progression = 0
               Exit Sub
           End If
       Else

           'Calculate the X and Y positions
           Effect(EffectIndex).GoToX = Engine_TPtoSPX(CharList(Effect(EffectIndex).BindToChar).Pos.X) + 16
           Effect(EffectIndex).GoToY = Engine_TPtoSPY(CharList(Effect(EffectIndex).BindToChar).Pos.Y)

       End If

   End If

   'Move to the new position if needed
   If Effect(EffectIndex).GoToX > -30000 Or Effect(EffectIndex).GoToY > -30000 Then
       If Effect(EffectIndex).GoToX <> Effect(EffectIndex).X Or Effect(EffectIndex).GoToY <> Effect(EffectIndex).Y Then

           'Calculate the angle
           TargetA = Engine_GetAngle(Effect(EffectIndex).X, Effect(EffectIndex).Y, Effect(EffectIndex).GoToX, Effect(EffectIndex).GoToY) + 180

           'Update the position of the effect
           Effect(EffectIndex).X = Effect(EffectIndex).X - Sin(TargetA * DegreeToRadian) * Effect(EffectIndex).BindSpeed
           Effect(EffectIndex).Y = Effect(EffectIndex).Y + Cos(TargetA * DegreeToRadian) * Effect(EffectIndex).BindSpeed

           'Check if the effect is close enough to the target to just stick it at the target
           If Effect(EffectIndex).GoToX > -30000 Then
               If Abs(Effect(EffectIndex).X - Effect(EffectIndex).GoToX) < 6 Then Effect(EffectIndex).X = Effect(EffectIndex).GoToX
           End If
           If Effect(EffectIndex).GoToY > -30000 Then
               If Abs(Effect(EffectIndex).Y - Effect(EffectIndex).GoToY) < 6 Then Effect(EffectIndex).Y = Effect(EffectIndex).GoToY
           End If

           'Check if the position of the effect is equal to that of the target
           If Effect(EffectIndex).X = Effect(EffectIndex).GoToX Then
               If Effect(EffectIndex).Y = Effect(EffectIndex).GoToY Then

                   'For some effects, if the position is reached, we want to end the effect
                   If Effect(EffectIndex).KillWhenAtTarget Then
                       Effect(EffectIndex).BindToChar = 0
                       Effect(EffectIndex).Progression = 0
                       Effect(EffectIndex).GoToX = Effect(EffectIndex).X
                       Effect(EffectIndex).GoToY = Effect(EffectIndex).Y
                   End If
                   Exit Sub    'The effect is at the right position, don't update

               End If
           End If

       End If
   End If

End Sub

Private Sub Effect_Protection_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Protection_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long

   'Calculate The Time Difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   'Update the life span
   If Effect(EffectIndex).Progression > 0 Then Effect(EffectIndex).Progression = Effect(EffectIndex).Progression - ElapsedTime
   'Go through the particle loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       'Check If Particle Is In Use
       If Effect(EffectIndex).Particles(LoopC).Used Then
           'Update The Particle
           Effect(EffectIndex).Particles(LoopC).UpdateParticle ElapsedTime
           'Check if the particle is ready to die
           If Effect(EffectIndex).Particles(LoopC).sngA <= 0 Then
               'Check if the effect is ending
               If Effect(EffectIndex).Progression > 0 Then
                   'Reset the particle
                   Effect_Protection_Reset EffectIndex, LoopC
               Else
                   'Disable the particle
                   Effect(EffectIndex).Particles(LoopC).Used = False
                   'Subtract from the total particle count
                   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticlesLeft - 1
                   'Check if the effect is out of particles
                   If Effect(EffectIndex).ParticlesLeft = 0 Then Effect(EffectIndex).Used = False
                   'Clear the color (dont leave behind any artifacts)
                   Effect(EffectIndex).PartVertex(LoopC).Color = 0
               End If
           Else
               'Set the particle information on the particle vertex
               Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(Effect(EffectIndex).Particles(LoopC).sngR, Effect(EffectIndex).Particles(LoopC).sngG, Effect(EffectIndex).Particles(LoopC).sngB, Effect(EffectIndex).Particles(LoopC).sngA)
               Effect(EffectIndex).PartVertex(LoopC).X = Effect(EffectIndex).Particles(LoopC).sngX
               Effect(EffectIndex).PartVertex(LoopC).Y = Effect(EffectIndex).Particles(LoopC).sngY
           End If
       End If
   Next LoopC

End Sub

Public Sub Effect_Render(ByVal EffectIndex As Integer, Optional ByVal SetRenderStates As Boolean = True) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Render '*****************************************************************

   'Check if we have the device
   If D3DDevice.TestCooperativeLevel <> D3D_OK Then Exit Sub
   'Set the render state for the size of the particle
   D3DDevice.SetRenderState D3DRS_POINTSIZE, Effect(EffectIndex).FloatSize
   
   'Set the render state to point blitting
   If SetRenderStates Then D3DDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_ONE
   
   'Set the last texture to a random number to force the engine to reload the texture
   LastTexture = -65489
   'Set the texture
   D3DDevice.SetTexture 0, ParticleTexture(Effect(EffectIndex).Gfx)
   'Draw all the particles at once
   D3DDevice.DrawPrimitiveUP D3DPT_POINTLIST, Effect(EffectIndex).ParticleCount, Effect(EffectIndex).PartVertex(0), Len(Effect(EffectIndex).PartVertex(0))
   'Reset the render state back to normal
   If SetRenderStates Then D3DDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA

End Sub

Function Effect_Snow_Begin(ByVal Gfx As Integer, ByVal Particles As Integer) As Integer '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Snow_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_Snow_Begin = EffectIndex
   'Set The Effect's Variables
   Effect(EffectIndex).EffectNum = EffectNum_Snow      'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles       'Set the number of particles
   Effect(EffectIndex).Used = True     'Enabled the effect
   Effect(EffectIndex).Gfx = Gfx       'Set the graphic
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(15)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_Snow_Reset EffectIndex, LoopC, 1
   Next LoopC
   'Set the initial time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_Snow_Reset(ByVal EffectIndex As Integer, ByVal Index As Long, Optional ByVal FirstReset As Byte = 0) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Snow_Reset '*****************************************************************

   If FirstReset = 1 Then
       'The very first reset
       Effect(EffectIndex).Particles(Index).ResetIt -200 + (Rnd * (ScreenWidth + 400)), Rnd * (ScreenHeight + 50), Rnd * 5, 5 + Rnd * 3, 0, 0
   Else
       'Any reset after first
       Effect(EffectIndex).Particles(Index).ResetIt -200 + (Rnd * (ScreenWidth + 400)), -15 - Rnd * 185, Rnd * 5, 5 + Rnd * 3, 0, 0
       If Effect(EffectIndex).Particles(Index).sngX < -20 Then Effect(EffectIndex).Particles(Index).sngY = Rnd * (ScreenHeight + 50)
       If Effect(EffectIndex).Particles(Index).sngX > ScreenWidth Then Effect(EffectIndex).Particles(Index).sngY = Rnd * (ScreenHeight + 50)
       If Effect(EffectIndex).Particles(Index).sngY > ScreenHeight Then Effect(EffectIndex).Particles(Index).sngX = Rnd * (ScreenWidth + 50)
   End If
   'Set the color
   Effect(EffectIndex).Particles(Index).ResetColor 1, 1, 1, 0.8, 0

End Sub

Private Sub Effect_Snow_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Snow_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long

   'Calculate the time difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   'Go through the particle loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       'Check if particle is in use
       If Effect(EffectIndex).Particles(LoopC).Used Then
           'Update The Particle
           Effect(EffectIndex).Particles(LoopC).UpdateParticle ElapsedTime
           'Check if to reset the particle
           If Effect(EffectIndex).Particles(LoopC).sngX < -200 Then Effect(EffectIndex).Particles(LoopC).sngA = 0
           If Effect(EffectIndex).Particles(LoopC).sngX > (ScreenWidth + 200) Then Effect(EffectIndex).Particles(LoopC).sngA = 0
           If Effect(EffectIndex).Particles(LoopC).sngY > (ScreenHeight + 200) Then Effect(EffectIndex).Particles(LoopC).sngA = 0
           'Time for a reset, baby!
           If Effect(EffectIndex).Particles(LoopC).sngA <= 0 Then
               'Reset the particle
               Effect_Snow_Reset EffectIndex, LoopC
           Else
               'Set the particle information on the particle vertex
               Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(Effect(EffectIndex).Particles(LoopC).sngR, Effect(EffectIndex).Particles(LoopC).sngG, Effect(EffectIndex).Particles(LoopC).sngB, Effect(EffectIndex).Particles(LoopC).sngA)
               Effect(EffectIndex).PartVertex(LoopC).X = Effect(EffectIndex).Particles(LoopC).sngX
               Effect(EffectIndex).PartVertex(LoopC).Y = Effect(EffectIndex).Particles(LoopC).sngY
           End If
       End If
   Next LoopC

End Sub

Function Effect_Strengthen_Begin(ByVal X As Single, ByVal Y As Single, ByVal Gfx As Integer, ByVal Particles As Integer, Optional ByVal Size As Byte = 30, Optional ByVal Time As Single = 10) As Integer '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Strengthen_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_Strengthen_Begin = EffectIndex
   'Set the effect's variables
   Effect(EffectIndex).EffectNum = EffectNum_Strengthen    'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles           'Set the number of particles
   Effect(EffectIndex).Used = True             'Enabled the effect
   Effect(EffectIndex).X = X                   'Set the effect's X coordinate
   Effect(EffectIndex).Y = Y                   'Set the effect's Y coordinate
   Effect(EffectIndex).Gfx = Gfx               'Set the graphic
   Effect(EffectIndex).Modifier = Size         'How large the circle is
   Effect(EffectIndex).Progression = Time      'How long the effect will last
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(20)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_Strengthen_Reset EffectIndex, LoopC
   Next LoopC
   'Set The Initial Time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_Strengthen_Reset(ByVal EffectIndex As Integer, ByVal Index As Long) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Strengthen_Reset '***************************************************************** Dim a As Single Dim X As Single Dim Y As Single

   'Get the positions
   a = Rnd * 360 * DegreeToRadian
   X = Effect(EffectIndex).X - (Sin(a) * Effect(EffectIndex).Modifier)
   Y = Effect(EffectIndex).Y + (Cos(a) * Effect(EffectIndex).Modifier)
   'Reset the particle
   Effect(EffectIndex).Particles(Index).ResetIt X, Y, 0, Rnd * -1, 0, -2
   Effect(EffectIndex).Particles(Index).ResetColor 0.2, 1, 0.2, 0.6 + (Rnd * 0.4), 0.06 + (Rnd * 0.2)

End Sub

Private Sub Effect_Strengthen_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Strengthen_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long

   'Calculate the time difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   'Update the life span
   If Effect(EffectIndex).Progression > 0 Then Effect(EffectIndex).Progression = Effect(EffectIndex).Progression - ElapsedTime
   'Go through the particle loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       'Check if particle is in use
       If Effect(EffectIndex).Particles(LoopC).Used Then
           'Update the particle
           Effect(EffectIndex).Particles(LoopC).UpdateParticle ElapsedTime
           'Check if the particle is ready to die
           If Effect(EffectIndex).Particles(LoopC).sngA <= 0 Then
               'Check if the effect is ending
               If Effect(EffectIndex).Progression > 0 Then
                   'Reset the particle
                   Effect_Strengthen_Reset EffectIndex, LoopC
               Else
                   'Disable the particle
                   Effect(EffectIndex).Particles(LoopC).Used = False
                   'Subtract from the total particle count
                   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticlesLeft - 1
                   'Check if the effect is out of particles
                   If Effect(EffectIndex).ParticlesLeft = 0 Then Effect(EffectIndex).Used = False
                   'Clear the color (dont leave behind any artifacts)
                   Effect(EffectIndex).PartVertex(LoopC).Color = 0
               End If
           Else
               'Set the particle information on the particle vertex
               Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(Effect(EffectIndex).Particles(LoopC).sngR, Effect(EffectIndex).Particles(LoopC).sngG, Effect(EffectIndex).Particles(LoopC).sngB, Effect(EffectIndex).Particles(LoopC).sngA)
               Effect(EffectIndex).PartVertex(LoopC).X = Effect(EffectIndex).Particles(LoopC).sngX
               Effect(EffectIndex).PartVertex(LoopC).Y = Effect(EffectIndex).Particles(LoopC).sngY
           End If
       End If
   Next LoopC

End Sub

Sub Effect_UpdateAll() '***************************************************************** 'Updates all of the effects and renders them 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_UpdateAll '***************************************************************** Dim LoopC As Long

   'Make sure we have effects
   If NumEffects = 0 Then Exit Sub
   'Set the render state for the particle effects
   D3DDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_ONE
   'Update every effect in use
   For LoopC = 1 To NumEffects
       'Make sure the effect is in use
       If Effect(LoopC).Used Then
       
           'Update the effect position if the screen has moved
           Effect_UpdateOffset LoopC
       
           'Update the effect position if it is binded
           Effect_UpdateBinding LoopC
           'Find out which effect is selected, then update it
           If Effect(LoopC).EffectNum = EffectNum_Fire Then Effect_Fire_Update LoopC
           If Effect(LoopC).EffectNum = EffectNum_Snow Then Effect_Snow_Update LoopC
           If Effect(LoopC).EffectNum = EffectNum_Heal Then Effect_Heal_Update LoopC
           If Effect(LoopC).EffectNum = EffectNum_Bless Then Effect_Bless_Update LoopC
           If Effect(LoopC).EffectNum = EffectNum_Protection Then Effect_Protection_Update LoopC
           If Effect(LoopC).EffectNum = EffectNum_Strengthen Then Effect_Strengthen_Update LoopC
           If Effect(LoopC).EffectNum = EffectNum_Rain Then Effect_Rain_Update LoopC
           If Effect(LoopC).EffectNum = EffectNum_EquationTemplate Then Effect_EquationTemplate_Update LoopC
           If Effect(LoopC).EffectNum = EffectNum_Waterfall Then Effect_Waterfall_Update LoopC
           If Effect(LoopC).EffectNum = EffectNum_Summon Then Effect_Summon_Update LoopC
           
           'Render the effect
           Effect_Render LoopC, False
       End If
   Next
   
   'Set the render state back for normal rendering
   D3DDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA

End Sub

Function Effect_Rain_Begin(ByVal Gfx As Integer, ByVal Particles As Integer) As Integer '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Rain_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_Rain_Begin = EffectIndex
   'Set the effect's variables
   Effect(EffectIndex).EffectNum = EffectNum_Rain      'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles       'Set the number of particles
   Effect(EffectIndex).Used = True     'Enabled the effect
   Effect(EffectIndex).Gfx = Gfx       'Set the graphic
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(10)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_Rain_Reset EffectIndex, LoopC, 1
   Next LoopC
   'Set The Initial Time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_Rain_Reset(ByVal EffectIndex As Integer, ByVal Index As Long, Optional ByVal FirstReset As Byte = 0) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Rain_Reset '*****************************************************************

   If FirstReset = 1 Then
       'The very first reset
       Effect(EffectIndex).Particles(Index).ResetIt -200 + (Rnd * (ScreenWidth + 400)), Rnd * (ScreenHeight + 50), Rnd * 5, 25 + Rnd * 12, 0, 0
   Else
       'Any reset after first
       Effect(EffectIndex).Particles(Index).ResetIt -200 + (Rnd * 1200), -15 - Rnd * 185, Rnd * 5, 25 + Rnd * 12, 0, 0
       If Effect(EffectIndex).Particles(Index).sngX < -20 Then Effect(EffectIndex).Particles(Index).sngY = Rnd * (ScreenHeight + 50)
       If Effect(EffectIndex).Particles(Index).sngX > ScreenWidth Then Effect(EffectIndex).Particles(Index).sngY = Rnd * (ScreenHeight + 50)
       If Effect(EffectIndex).Particles(Index).sngY > ScreenHeight Then Effect(EffectIndex).Particles(Index).sngX = Rnd * (ScreenWidth + 50)
   End If
   'Set the color
   Effect(EffectIndex).Particles(Index).ResetColor 1, 1, 1, 0.4, 0

End Sub

Private Sub Effect_Rain_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Rain_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long

   'Calculate the time difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   'Go through the particle loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       'Check if the particle is in use
       If Effect(EffectIndex).Particles(LoopC).Used Then
           'Update the particle
           Effect(EffectIndex).Particles(LoopC).UpdateParticle ElapsedTime
           'Check if to reset the particle
           If Effect(EffectIndex).Particles(LoopC).sngX < -200 Then Effect(EffectIndex).Particles(LoopC).sngA = 0
           If Effect(EffectIndex).Particles(LoopC).sngX > (ScreenWidth + 200) Then Effect(EffectIndex).Particles(LoopC).sngA = 0
           If Effect(EffectIndex).Particles(LoopC).sngY > (ScreenHeight + 200) Then Effect(EffectIndex).Particles(LoopC).sngA = 0
           'Time for a reset, baby!
           If Effect(EffectIndex).Particles(LoopC).sngA <= 0 Then
               'Reset the particle
               Effect_Rain_Reset EffectIndex, LoopC
           Else
               'Set the particle information on the particle vertex
               Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(Effect(EffectIndex).Particles(LoopC).sngR, Effect(EffectIndex).Particles(LoopC).sngG, Effect(EffectIndex).Particles(LoopC).sngB, Effect(EffectIndex).Particles(LoopC).sngA)
               Effect(EffectIndex).PartVertex(LoopC).X = Effect(EffectIndex).Particles(LoopC).sngX
               Effect(EffectIndex).PartVertex(LoopC).Y = Effect(EffectIndex).Particles(LoopC).sngY
           End If
       End If
   Next LoopC

End Sub

Public Sub Effect_Begin(ByVal EffectIndex As Integer, ByVal X As Single, ByVal Y As Single, ByVal GfxIndex As Byte, ByVal Particles As Byte, Optional ByVal Direction As Single = 180, Optional ByVal BindToMap As Boolean = False) '***************************************************************** 'A very simplistic form of initialization for particle effects 'Should only be used for starting map-based effects 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Begin '***************************************************************** Dim RetNum As Byte

   Select Case EffectIndex
       Case EffectNum_Fire
           RetNum = Effect_Fire_Begin(X, Y, GfxIndex, Particles, Direction, 1)
       Case EffectNum_Waterfall
           RetNum = Effect_Waterfall_Begin(X, Y, GfxIndex, Particles)
   End Select
   
   'Bind the effect to the map if needed
   If BindToMap Then Effect(RetNum).BoundToMap = 1
   

End Sub

Function Effect_Waterfall_Begin(ByVal X As Single, ByVal Y As Single, ByVal Gfx As Integer, ByVal Particles As Integer) As Integer '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Waterfall_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_Waterfall_Begin = EffectIndex
   'Set the effect's variables
   Effect(EffectIndex).EffectNum = EffectNum_Waterfall     'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles           'Set the number of particles
   Effect(EffectIndex).Used = True             'Enabled the effect
   Effect(EffectIndex).X = X                   'Set the effect's X coordinate
   Effect(EffectIndex).Y = Y                   'Set the effect's Y coordinate
   Effect(EffectIndex).Gfx = Gfx               'Set the graphic
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(20)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_Waterfall_Reset EffectIndex, LoopC
   Next LoopC
   'Set The Initial Time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_Waterfall_Reset(ByVal EffectIndex As Integer, ByVal Index As Long) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Waterfall_Reset '*****************************************************************

   If Int(Rnd * 10) = 1 Then
       Effect(EffectIndex).Particles(Index).ResetIt Effect(EffectIndex).X + (Rnd * 60), Effect(EffectIndex).Y + (Rnd * 130), 0, 8 + (Rnd * 6), 0, 0
   Else
       Effect(EffectIndex).Particles(Index).ResetIt Effect(EffectIndex).X + (Rnd * 60), Effect(EffectIndex).Y + (Rnd * 10), 0, 8 + (Rnd * 6), 0, 0
   End If
   Effect(EffectIndex).Particles(Index).ResetColor 0.1, 0.1, 0.9, 0.6 + (Rnd * 0.4), 0
   

End Sub

Private Sub Effect_Waterfall_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Waterfall_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long

   'Calculate The Time Difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   'Update the life span
   If Effect(EffectIndex).Progression > 0 Then Effect(EffectIndex).Progression = Effect(EffectIndex).Progression - ElapsedTime
   'Go through the particle loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
   
       With Effect(EffectIndex).Particles(LoopC)
   
           'Check if the particle is in use
           If .Used Then
   
               'Update The Particle
               .UpdateParticle ElapsedTime
               'Check if the particle is ready to die
               If (.sngY > Effect(EffectIndex).Y + 140) Or (.sngA = 0) Then
   
                   'Reset the particle
                   Effect_Waterfall_Reset EffectIndex, LoopC
   
               Else
                   'Set the particle information on the particle vertex
                   Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(.sngR, .sngG, .sngB, .sngA)
                   Effect(EffectIndex).PartVertex(LoopC).X = .sngX
                   Effect(EffectIndex).PartVertex(LoopC).Y = .sngY
   
               End If
   
           End If
           
       End With
   Next LoopC

End Sub

Function Effect_Summon_Begin(ByVal X As Single, ByVal Y As Single, ByVal Gfx As Integer, ByVal Particles As Integer, Optional ByVal Progression As Single = 0) As Integer '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Summon_Begin '***************************************************************** Dim EffectIndex As Integer Dim LoopC As Long

   'Get the next open effect slot
   EffectIndex = Effect_NextOpenSlot
   If EffectIndex = -1 Then Exit Function
   'Return the index of the used slot
   Effect_Summon_Begin = EffectIndex
   'Set The Effect's Variables
   Effect(EffectIndex).EffectNum = EffectNum_Summon    'Set the effect number
   Effect(EffectIndex).ParticleCount = Particles       'Set the number of particles
   Effect(EffectIndex).Used = True                     'Enable the effect
   Effect(EffectIndex).X = X                           'Set the effect's X coordinate
   Effect(EffectIndex).Y = Y                           'Set the effect's Y coordinate
   Effect(EffectIndex).Gfx = Gfx                       'Set the graphic
   Effect(EffectIndex).Progression = Progression       'If we loop the effect
   'Set the number of particles left to the total avaliable
   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticleCount
   'Set the float variables
   Effect(EffectIndex).FloatSize = Effect_FToDW(8)    'Size of the particles
   'Redim the number of particles
   ReDim Effect(EffectIndex).Particles(0 To Effect(EffectIndex).ParticleCount)
   ReDim Effect(EffectIndex).PartVertex(0 To Effect(EffectIndex).ParticleCount)
   'Create the particles
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       Set Effect(EffectIndex).Particles(LoopC) = New Particle
       Effect(EffectIndex).Particles(LoopC).Used = True
       Effect(EffectIndex).PartVertex(LoopC).Rhw = 1
       Effect_Summon_Reset EffectIndex, LoopC
   Next LoopC
   'Set The Initial Time
   Effect(EffectIndex).PreviousFrame = timeGetTime

End Function

Private Sub Effect_Summon_Reset(ByVal EffectIndex As Integer, ByVal Index As Long) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Summon_Reset '***************************************************************** Dim X As Single Dim Y As Single Dim R As Single

   If Effect(EffectIndex).Progression > 1000 Then
       Effect(EffectIndex).Progression = Effect(EffectIndex).Progression + 1.4
   Else
       Effect(EffectIndex).Progression = Effect(EffectIndex).Progression + 0.5
   End If
   R = (Index / 30) * EXP(Index / Effect(EffectIndex).Progression)
   X = R * Cos(Index)
   Y = R * Sin(Index)
   
   'Reset the particle
   Effect(EffectIndex).Particles(Index).ResetIt Effect(EffectIndex).X + X, Effect(EffectIndex).Y + Y, 0, 0, 0, 0
   Effect(EffectIndex).Particles(Index).ResetColor 0, Rnd, 0, 0.9, 0.2 + (Rnd * 0.2)

End Sub

Private Sub Effect_Summon_Update(ByVal EffectIndex As Integer) '***************************************************************** 'More info: http://www.vbgore.com/CommonCode.Particles.Effect_Summon_Update '***************************************************************** Dim ElapsedTime As Single Dim LoopC As Long

   'Calculate The Time Difference
   ElapsedTime = (timeGetTime - Effect(EffectIndex).PreviousFrame) * 0.01
   Effect(EffectIndex).PreviousFrame = timeGetTime
   'Go Through The Particle Loop
   For LoopC = 0 To Effect(EffectIndex).ParticleCount
       'Check If Particle Is In Use
       If Effect(EffectIndex).Particles(LoopC).Used Then
           'Update The Particle
           Effect(EffectIndex).Particles(LoopC).UpdateParticle ElapsedTime
           'Check if the particle is ready to die
           If Effect(EffectIndex).Particles(LoopC).sngA <= 0 Then
               'Check if the effect is ending
               If Effect(EffectIndex).Progression < 1800 Then
                   'Reset the particle
                   Effect_Summon_Reset EffectIndex, LoopC
               Else
                   'Disable the particle
                   Effect(EffectIndex).Particles(LoopC).Used = False
                   'Subtract from the total particle count
                   Effect(EffectIndex).ParticlesLeft = Effect(EffectIndex).ParticlesLeft - 1
                   'Check if the effect is out of particles
                   If Effect(EffectIndex).ParticlesLeft = 0 Then Effect(EffectIndex).Used = False
                   'Clear the color (dont leave behind any artifacts)
                   Effect(EffectIndex).PartVertex(LoopC).Color = 0
               End If
           Else
           
               'Set the particle information on the particle vertex
               Effect(EffectIndex).PartVertex(LoopC).Color = D3DColorMake(Effect(EffectIndex).Particles(LoopC).sngR, Effect(EffectIndex).Particles(LoopC).sngG, Effect(EffectIndex).Particles(LoopC).sngB, Effect(EffectIndex).Particles(LoopC).sngA)
               Effect(EffectIndex).PartVertex(LoopC).X = Effect(EffectIndex).Particles(LoopC).sngX
               Effect(EffectIndex).PartVertex(LoopC).Y = Effect(EffectIndex).Particles(LoopC).sngY
           End If
       End If
   Next LoopC

End Sub </vb>

PictureTextBox

<vb> Option Explicit

'Notice - Text boxes must be multiline for this to work! 'I know this isn't the best way to go about doing this, but it isn't 'used for very long nor is it used in any other projects, so no point in wasting time 'making it very versitile

'Holds the returns from SetWindowLong Private frmNewPrev As Long Private frmConnectPrev As Long

'APIs we will be using Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long Private Declare Function BitBlt Lib "GDI32" (ByVal hDC As Long, ByVal DX As Long, ByVal DY As Long, ByVal DWidth As Long, ByVal DHeight As Long, ByVal ShDC As Long, ByVal SX As Long, ByVal SY As Long, ByVal vbSrCopy As Long) As Long Private Declare Function SetBkMode Lib "GDI32" (ByVal hDC As Long, ByVal hMode As Long) As Long Private Declare Function WindowFromDC Lib "user32" (ByVal hDC As Long) As Long

Public Sub SetPictureTextboxes(ByVal hwnd As Long) '***************************************************************** 'Sets the textboxes either on frmConnect or frmNew to a picture background 'More info: http://www.vbgore.com/GameClient.PictureTextBox.SetPictureTextboxes '*****************************************************************

   'Set the form to subclass and the textbox heights
   Select Case hwnd
   
   Case frmConnect.hwnd
       frmConnectPrev = SetWindowLong(hwnd, -4, AddressOf frmConnectProc)
       With frmConnect
           .NameTxt.Height = Int(.NameTxt.Height \ .TextHeight("_")) * .TextHeight("_")
           .PasswordTxt.Height = Int(.PasswordTxt.Height \ .TextHeight("_")) * .TextHeight("_")
       End With
       
   Case frmNew.hwnd
       frmNewPrev = SetWindowLong(hwnd, -4, AddressOf frmNewProc)
       With frmNew
           .NameTxt.Height = Int(.NameTxt.Height \ .TextHeight("_")) * .TextHeight("_")
           .PasswordTxt.Height = Int(.PasswordTxt.Height \ .TextHeight("_")) * .TextHeight("_")
       End With
       
   End Select
   

End Sub

Public Sub FreePictureTextboxes(ByVal hwnd As Long) '***************************************************************** 'Removes the picture textboxes (must be done when the form is unloaded!) 'More info: http://www.vbgore.com/GameClient.PictureTextBox.FreePictureTextboxes '*****************************************************************

   'Free the form
   Select Case hwnd
       
   Case frmConnect.hwnd
       SetWindowLong hwnd, -4, frmConnectPrev
       
   Case frmNew.hwnd
       SetWindowLong hwnd, -4, frmNewPrev
   
   End Select
   

End Sub

Private Function frmNewProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long '***************************************************************** 'Subclassing routine for frmNew 'More info: http://www.vbgore.com/GameClient.PictureTextBox.frmNewProc '*****************************************************************

   'Check for a message we want
   If uMsg = &H133 Then
   
       'Make sure our form is visible
       If frmNew.Visible Then
       
           'Look for the hWnds we want and handle accordingly
           Select Case WindowFromDC(wParam)
           
           Case frmNew.PasswordTxt.hwnd
               With frmNew.PasswordTxt
                   SetBkMode wParam, 1
                   BitBlt wParam, 0, 0, .Width, .Height, frmNew.hDC, .Left, .Top, vbSrcCopy
               End With
   
           Case frmNew.NameTxt.hwnd
               With frmNew.NameTxt
                   SetBkMode wParam, 1
                   BitBlt wParam, 0, 0, .Width, .Height, frmNew.hDC, .Left, .Top, vbSrcCopy
               End With
       
           Case frmNew.ClassCmb.hwnd
               With frmNew.ClassCmb
                   SetBkMode wParam, 1
                   BitBlt wParam, 0, 0, .Width, .Height, frmNew.hDC, .Left, .Top, vbSrcCopy
               End With
               
           Case frmNew.BodyCmb.hwnd
               With frmNew.BodyCmb
                   SetBkMode wParam, 1
                   BitBlt wParam, 0, 0, .Width, .Height, frmNew.hDC, .Left, .Top, vbSrcCopy
               End With
               
           Case frmNew.HeadCmb.hwnd
               With frmNew.HeadCmb
                   SetBkMode wParam, 1
                   BitBlt wParam, 0, 0, .Width, .Height, frmNew.hDC, .Left, .Top, vbSrcCopy
               End With
                   
           End Select
           
       End If
       
   End If
   
   'Send the message to the form
   frmNewProc = CallWindowProc(frmNewPrev, hwnd, uMsg, wParam, lParam)
   

End Function

Private Function frmConnectProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long '***************************************************************** 'Subclassing routine for frmConnect 'More info: http://www.vbgore.com/GameClient.PictureTextBox.frmConnectProc '*****************************************************************

   'Check for a message we want
   If uMsg = &H133 Then
   
       'Make sure our form is visible
       If frmConnect.Visible Then
       
           'Look for the hWnds we want and handle accordingly
           Select Case WindowFromDC(wParam)
           
           Case frmConnect.PasswordTxt.hwnd
               With frmConnect.PasswordTxt
                   SetBkMode wParam, 1
                   BitBlt wParam, 0, 0, .Width, .Height, frmConnect.hDC, .Left, .Top, vbSrcCopy
               End With
   
           Case frmConnect.NameTxt.hwnd
               With frmConnect.NameTxt
                   SetBkMode wParam, 1
                   BitBlt wParam, 0, 0, .Width, .Height, frmConnect.hDC, .Left, .Top, vbSrcCopy
               End With
   
           End Select
           
       End If
       
   End If
   
   'Send the message to the form
   frmConnectProc = CallWindowProc(frmConnectPrev, hwnd, uMsg, wParam, lParam)
   

End Function </vb>

Sound

<vb> Option Explicit

Public Const SoundBufferTimerMax As Long = 300000 'How long a sound stays in memory unused (miliseconds) Public SoundBufferTimer() As Long 'How long until the sound buffer unloads Public DS As DirectSound8 Public DSBDesc As DSBUFFERDESC Public DSBuffer() As DirectSoundSecondaryBuffer8

Public Sub Sound_Init() '************************************************************ 'Initialize the 3D sound device 'More info: http://www.vbgore.com/GameClient.Sound.Sound_Init '************************************************************

   'Make sure we try not to load a file while the engine is unloading
   If IsUnloading Then Exit Sub
   
   On Error GoTo ErrOut
   
   If UseSfx = 0 Then Exit Sub
   
   'Create the DirectSound device (with the default device)
   Set DS = DX.DirectSoundCreate("")
   DS.SetCooperativeLevel frmMain.hwnd, DSSCL_PRIORITY
   
   'Set up the buffer description for later use
   'We are only using panning and volume - combined, we will use this to create a custom 3D effect
   DSBDesc.lFlags = DSBCAPS_CTRLPAN Or DSBCAPS_CTRLVOLUME
   
   'Check if the texture exists
   If Engine_FileExist(SfxPath & "Sfx.ini", vbNormal) = False Then
       MsgBox "Error! Could not find the following data file:" & vbCrLf & SfxPath & "Sfx.ini", vbOKOnly
       IsUnloading = 1
       Exit Sub
   End If
   'Get the number of sound effects
   NumSfx = Val(Var_Get(SfxPath & "Sfx.ini", "INIT", "NumSfx"))
   
   'Resize the sound buffer array
   If NumSfx > 0 Then
       ReDim DSBuffer(1 To NumSfx)
       ReDim SoundBufferTimer(1 To NumSfx)
   End If
   
   On Error GoTo 0
   
   Exit Sub
   

ErrOut:

   'Failure loading sounds, so we won't use them
   UseSfx = 0
   UseMusic = 0

End Sub

Public Sub Sound_SetToMap(ByVal SoundID As Integer, ByVal TileX As Byte, ByVal TileY As Byte) '************************************************************ 'Create a looping sound on the tile 'More info: http://www.vbgore.com/GameClient.Sound.Sound_SetToMap '************************************************************

   If UseSfx = 0 Then Exit Sub
   'Make sure the sound isn't already going
   If Not MapData(TileX, TileY).Sfx Is Nothing Then
       MapData(TileX, TileY).Sfx.Stop
       Set MapData(TileX, TileY).Sfx = Nothing
   End If
   
   'Create the buffer
   Sound_Set MapData(TileX, TileY).Sfx, SoundID
   
   'Exit if theres an error
   If MapData(TileX, TileY).Sfx Is Nothing Then Exit Sub
   'Start the loop
   MapData(TileX, TileY).Sfx.Play DSBPLAY_LOOPING
   
   'Since we dont want to start hearing the sound until we have calculated the panning/volume, we set the volume to off for now
   MapData(TileX, TileY).Sfx.SetVolume -10000

End Sub

Public Sub Sound_UpdateMap() '************************************************************ 'Update the panning and volume on the map sounds to create a 3d effect 'More info: http://www.vbgore.com/GameClient.Sound.Sound_UpdateMap '************************************************************ Dim SX As Integer Dim SY As Integer Dim X As Byte Dim Y As Byte Dim L As Long

   If UseSfx = 0 Then Exit Sub
   'Set the user's position to sX/sY
   SX = CharList(UserCharIndex).Pos.X
   SY = CharList(UserCharIndex).Pos.Y
   
   'Loop through all the map tiles
   For X = 1 To MapInfo.Width
       For Y = 1 To MapInfo.Height
           
           'Only update used tiles
           If Not MapData(X, Y).Sfx Is Nothing Then
               
               'Calculate the volume and check for valid range
               L = Sound_CalcVolume(SX, SY, X, Y)
               If L < -5000 Then
                   MapData(X, Y).Sfx.Stop
               Else
                   If L > 0 Then L = 0
                   If MapData(X, Y).Sfx.GetStatus <> DSBSTATUS_LOOPING Then MapData(X, Y).Sfx.Play DSBPLAY_LOOPING
                   MapData(X, Y).Sfx.SetVolume L
               End If
               
               'Calculate the panning and check for a valid range
               L = Sound_CalcPan(SX, X)
               If L > 10000 Then L = 10000
               If L < -10000 Then L = -10000
               MapData(X, Y).Sfx.SetPan L
               
           End If
           
       Next Y
   Next X

End Sub

Public Sub Sound_Play(ByRef SoundBuffer As DirectSoundSecondaryBuffer8, Optional ByVal flags As CONST_DSBPLAYFLAGS = DSBPLAY_DEFAULT) '************************************************************ 'Used for non area-specific sound effects, such as weather 'More info: http://www.vbgore.com/GameClient.Sound.Sound_Play '************************************************************

   'Make sure we are using sound
   If UseSfx = 0 Then Exit Sub
   'Confirm the buffer exists
   If Not SoundBuffer Is Nothing Then
   
       'Reset the sounds values (in case they were ever changed)
       SoundBuffer.SetCurrentPosition 0
       Sound_Pan SoundBuffer, 0
       Sound_Volume SoundBuffer, 0
       
       'Play the sound
       SoundBuffer.Play flags
       
   End If
  

End Sub

Public Sub Sound_Erase(ByRef SoundBuffer As DirectSoundSecondaryBuffer8) '************************************************************ 'Erase the sound buffer 'More info: http://www.vbgore.com/GameClient.Sound.Sound_Erase '************************************************************

   If UseSfx = 0 Then Exit Sub
   
   'Make sure the object exists
   If Not SoundBuffer Is Nothing Then
   
       'If it is playing, we have to stop it first
       If SoundBuffer.GetStatus > 0 Then SoundBuffer.Stop
       
       'Clear the object
       Set SoundBuffer = Nothing
       
   End If

End Sub

Public Sub Sound_Set(ByRef SoundBuffer As DirectSoundSecondaryBuffer8, ByVal SoundID As Integer) '************************************************************ 'Set the SoundID to the sound buffer 'More info: http://www.vbgore.com/GameClient.Sound.Sound_Set '************************************************************

   If UseSfx = 0 Then Exit Sub
   'Check if the sound buffer is in use
   Sound_Erase SoundBuffer
   
   'Set the buffer
   If Engine_FileExist(SfxPath & SoundID & ".wav", vbNormal) Then Set SoundBuffer = DS.CreateSoundBufferFromFile(SfxPath & SoundID & ".wav", DSBDesc)

End Sub

Public Sub Sound_Play3D(ByVal SoundID As Integer, ByVal TileX As Integer, ByVal TileY As Integer) '************************************************************ 'Play a pseudo-3D sound by the sound buffer ID 'More info: http://www.vbgore.com/GameClient.Sound.Sound_Play3D '************************************************************ Dim SX As Integer Dim SY As Integer

   If UseSfx = 0 Then Exit Sub
   'Make sure we have the UserCharIndex, or else we cant play the sound! :o
   If UserCharIndex = 0 Then Exit Sub
   'Check for a valid sound
   If SoundID <= 0 Then Exit Sub
   'Create the buffer if needed
   If SoundBufferTimer(SoundID) < timeGetTime Then
       If DSBuffer(SoundID) Is Nothing Then Sound_Set DSBuffer(SoundID), SoundID
   End If
   
   'Update the timer
   SoundBufferTimer(SoundID) = timeGetTime + SoundBufferTimerMax
   
   'Clear the position (used in case the sound was already playing - we can only have one of each sound play at a time)
   DSBuffer(SoundID).SetCurrentPosition 0
   
   'Set the user's position to sX/sY
   SX = CharList(UserCharIndex).Pos.X
   SY = CharList(UserCharIndex).Pos.Y
   
   'Calculate the panning
   Sound_Pan DSBuffer(SoundID), Sound_CalcPan(SX, TileX)
   
   'Calculate the volume
   Sound_Volume DSBuffer(SoundID), Sound_CalcVolume(SX, SY, TileX, TileY)
   
   'Play the sound
   DSBuffer(SoundID).Play DSBPLAY_DEFAULT
   

End Sub

Public Function Sound_CalcPan(ByVal x1 As Integer, ByVal x2 As Integer) As Long '************************************************************ 'Calculate the panning for 3D sound based on the user's position and the sound's position 'More info: http://www.vbgore.com/GameClient.Sound.Sound_CalcPan '************************************************************

   If UseSfx = 0 Then Exit Function
   Sound_CalcPan = (x1 - x2) * 75 * ReverseSound
   

End Function

Public Function Sound_CalcVolume(ByVal x1 As Integer, ByVal Y1 As Integer, ByVal x2 As Integer, ByVal Y2 As Integer) As Long '************************************************************ 'Calculate the volume for 3D sound based on the user's position and the sound's position 'the (Abs(sX - TileX) * 25) is put on the end to make up for the simulated ' volume loss during panning (since one speaker gets muted to create the panning) 'More info: http://www.vbgore.com/GameClient.Sound.Sound_CalcVolume '************************************************************ Dim Dist As Single

   If UseSfx = 0 Then Exit Function
   'Store the distance
   Dist = Sqr(((Y1 - Y2) * (Y1 - Y2)) + ((x1 - x2) * (x1 - x2)))
   
   'Apply the initial value
   Sound_CalcVolume = -(Dist * 80) + (Abs(x1 - x2) * 25)
   
   'Once we get out of the screen (>= 13 tiles away) then we want to fade fast
   If Dist > 13 Then Sound_CalcVolume = Sound_CalcVolume - ((Dist - 13) * 180)
   

End Function

Private Sub Sound_Pan(ByRef SoundBuffer As DirectSoundSecondaryBuffer8, ByVal Value As Long) '************************************************************ 'Pan the selected SoundID (-10,000 to 10,000) 'More info: http://www.vbgore.com/GameClient.Sound.Sound_Pan '************************************************************

   If UseSfx = 0 Then Exit Sub
   If SoundBuffer Is Nothing Then Exit Sub
   SoundBuffer.SetPan Value

End Sub

Private Sub Sound_Volume(ByRef SoundBuffer As DirectSoundSecondaryBuffer8, ByVal Value As Long) '************************************************************ 'Pan the selected SoundID (-10,000 to 0) 'More info: http://www.vbgore.com/GameClient.Sound.Sound_Volume '************************************************************

   If UseSfx = 0 Then Exit Sub
   If SoundBuffer Is Nothing Then Exit Sub
   If Value > 0 Then Value = 0
   If Value < -10000 Then Value = -10000
   SoundBuffer.SetVolume Value

End Sub

Public Sub Music_Load(ByVal FilePath As String, ByVal BufferNumber As Long) '************************************************************ 'Loads a mp3 by the specified path 'More info: http://www.vbgore.com/GameClient.Sound.Music_Load '************************************************************

   If UseMusic = 0 Then Exit Sub
   On Error GoTo Error_Handler
               
   If Right$(FilePath, 4) = ".mp3" Then
   
       Set DirectShow_Control(BufferNumber) = New FilgraphManager
       DirectShow_Control(BufferNumber).RenderFile FilePath
   
       Set DirectShow_Audio(BufferNumber) = DirectShow_Control(BufferNumber)
       
       DirectShow_Audio(BufferNumber).Volume = 0
       DirectShow_Audio(BufferNumber).Balance = 0
   
       Set DirectShow_Event(BufferNumber) = DirectShow_Control(BufferNumber)
       Set DirectShow_Position(BufferNumber) = DirectShow_Control(BufferNumber)
       
       DirectShow_Position(BufferNumber).Rate = 1
       
       DirectShow_Position(BufferNumber).CurrentPosition = 0
   
   End If

Error_Handler:

End Sub

Public Sub Music_Play(ByVal BufferNumber As Long) '************************************************************ 'Plays the mp3 in the specified buffer 'More info: http://www.vbgore.com/GameClient.Sound.Music_Play '************************************************************

   On Error GoTo Error_Handler
   
   If UseMusic = 0 Then Exit Sub
   
   DirectShow_Control(BufferNumber).Run

Error_Handler:

End Sub

Public Sub Music_Stop(ByVal BufferNumber As Long) '************************************************************ 'Stops the mp3 in the specified buffer 'More info: http://www.vbgore.com/GameClient.Sound.Music_Stop '************************************************************

   On Error GoTo Error_Handler
   
   If UseMusic = 0 Then Exit Sub
   
   DirectShow_Control(BufferNumber).Stop
   
   DirectShow_Position(BufferNumber).CurrentPosition = 0
   Exit Sub

Error_Handler:

End Sub

Public Sub Music_Pause(ByVal BufferNumber As Long) '************************************************************ 'Pause the music in the specified buffer 'More info: http://www.vbgore.com/GameClient.Sound.Music_Pause '************************************************************

   On Error GoTo Error_Handler
   
   If UseMusic = 0 Then Exit Sub
   
   DirectShow_Control(BufferNumber).Stop
   

Error_Handler:

End Sub

Public Sub Music_Volume(ByVal Volume As Long, ByVal BufferNumber As Long) '************************************************************ 'Set the volume of the music in the specified buffer 'More info: http://www.vbgore.com/GameClient.Sound.Music_Volume '************************************************************

   On Error GoTo Error_Handler
   
   If UseMusic = 0 Then Exit Sub
   
   If Volume >= Music_MaxVolume Then Volume = Music_MaxVolume
   
   If Volume <= 0 Then Volume = 0
   
   DirectShow_Audio(BufferNumber).Volume = (Volume * Music_MaxVolume) - 10000
   

Error_Handler:

End Sub

Public Sub Music_Balance(ByVal Balance As Long, ByVal BufferNumber As Long) '************************************************************ 'Set the balance of the music in the specified buffer 'More info: http://www.vbgore.com/GameClient.Sound.Music_Balance '************************************************************

   On Error GoTo Error_Handler
   
   If UseMusic = 0 Then Exit Sub
   
   If Balance >= Music_MaxBalance Then Balance = Music_MaxBalance
   
   If Balance <= -Music_MaxBalance Then Balance = -Music_MaxBalance
   
   DirectShow_Audio(BufferNumber).Balance = Balance * Music_MaxBalance

Error_Handler:

End Sub

Public Sub Music_Speed(ByVal Speed As Single, ByVal BufferNumber As Long) '************************************************************ 'Set the speed of the music in the specified buffer 'More info: http://www.vbgore.com/GameClient.Sound.Music_Speed '************************************************************

   On Error GoTo Error_Handler
   
   If UseMusic = 0 Then Exit Sub
   If Speed >= Music_MaxSpeed Then Speed = Music_MaxSpeed
   
   If Speed <= 0 Then Speed = 0
   DirectShow_Position(BufferNumber).Rate = Speed / 100

Error_Handler:

End Sub

Public Sub Music_SetPosition(ByVal Hours As Long, ByVal Minutes As Long, ByVal Seconds As Long, Milliseconds As Single, ByVal BufferNumber As Long) '************************************************************ 'Set the speed of the music in the specified buffer 'More info: http://www.vbgore.com/GameClient.Sound.Music_SetPosition '************************************************************

   On Error GoTo Error_Handler
   
   Dim Max_Position As Single
   
   Dim Position As Double
   
   Dim Decimal_Milliseconds As Single
   
   If UseMusic = 0 Then Exit Sub
   
   'Keep minutes within range
   Minutes = Minutes Mod 60
       
   'Keep seconds within range
   Seconds = Seconds Mod 60
       
   'Keep milliseconds within range and keep decimal
   Decimal_Milliseconds = Milliseconds - Int(Milliseconds)
   Milliseconds = Milliseconds Mod 1000
   Milliseconds = Milliseconds + Decimal_Milliseconds
   
   'Convert Minutes & Seconds to Position time
   Position = (Hours * 3600) + (Minutes * 60) + Seconds + (Milliseconds * 0.001)
   
   Max_Position = DirectShow_Position(BufferNumber).StopTime
   If Position >= Max_Position Then
       Position = 0
       GoTo Error_Handler
   End If
   
   If Position <= 0 Then
       Position = 0
       GoTo Error_Handler
   End If
   
   DirectShow_Position(BufferNumber).CurrentPosition = Position

Error_Handler:

End Sub

Public Sub Music_End(ByVal BufferNumber As Long) '************************************************************ 'End the music in the specified buffer 'More info: http://www.vbgore.com/GameClient.Sound.Music_End '************************************************************

   On Error GoTo Error_Handler
   
   If UseMusic = 0 Then Exit Sub
   
   'Check if the buffer is looping
   If Not Music_Loop(BufferNumber) Then
   
       'Check if the current position is past the stop time
       If DirectShow_Position(BufferNumber).CurrentPosition >= DirectShow_Position(BufferNumber).StopTime Then Music_Stop BufferNumber
   
   End If

Error_Handler:

End Sub

Public Function Music_Loop(ByVal Media_Number As Long) As Boolean '************************************************************ 'Loop the music in the specified buffer 'More info: http://www.vbgore.com/GameClient.Sound.Music_Loop '************************************************************

   On Error GoTo Error_Handler
   
   If UseMusic = 0 Then Exit Function
   
   'Check if the current position is past the stop time - if so, reset it
   If DirectShow_Position(Media_Number).CurrentPosition >= DirectShow_Position(Media_Number).StopTime Then
       DirectShow_Position(Media_Number).CurrentPosition = 0
   End If
   
   Music_Loop = True
   Exit Function

Error_Handler:

   Music_Loop = False

End Function </vb>

TCP

<vb> Option Explicit

'************************************************************ 'ABOUT THE TCP MODULE PACKET HEADER COMMENTS '************************************************************ 'All Data_ methods in the TCP module, in both the server and client, are used to handle 'packets coming in to the application (forwarded from GOREsock_DataArrival). These methods 'all contain a comment in their header on how the packet is formatted. For example: ' '<Name(S)><Class(B)><Etc(L)> ' 'Each <> denotes a different variable. Inside the <>, you see two parts - the name of the 'packet part and the variable type. For example: ' '<Name(S)> ' 'The name of the packet part is "Name", and the variable type is S. Below is a list 'of all variable types used: ' 'B = Byte (1 byte) 'I = Integer (2 bytes) 'L = Long (4 bytes) 'S = String aka Short String(1 + string length bytes) 'S-EX = StringEX aka Long String (2 + string length bytes) '************************************************************

Private Type typHOSTENT

   hName As Long
   hAliases As Long
   hAddrType As Integer
   hLength As Integer
   hAddrList As Long

End Type

Private Type WSADATA

   wversion As Integer
   wHighVersion As Integer
   szDescription(0 To 255) As Byte
   szSystemStatus(0 To 127) As Byte
   iMaxSockets As Integer
   iMaxUdpDg As Integer
   lpszVendorInfo As Long

End Type

Private Declare Sub apiCopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, ByVal hpvSource As Long, ByVal cbCopy As Long) Private Declare Function apiGetHostByName Lib "wsock32" Alias "gethostbyname" (ByVal HostName As String) As Long Private Declare Function WSACleanup Lib "wsock32" () As Long Private Declare Function WSAStartup Lib "wsock32" (ByVal VersionReq As Long, WSADataReturn As WSADATA) As Long

Private Function IsIP(ByVal IPAddress As String) As Boolean '************************************************************ 'Checks if a string is in a valid IP address format 'More info: http://www.vbgore.com/GameClient.TCP.IsIP '************************************************************ Dim s() As String Dim i As Long

   'If there are no periods, I have no idea what we have...
   If InStr(1, IPAddress, ".") = 0 Then Exit Function
   
   'Split up the string by the periods
   s = Split(IPAddress, ".")
   
   'Confirm we have ubound = 3, since xxx.xxx.xxx.xxx has 4 elements and we start at index 0
   If UBound(s) <> 3 Then Exit Function
   
   'Check that the values are numeric and in a valid range
   For i = 0 To 3
       If Val(s(i)) < 0 Then Exit Function
       If Val(s(i)) > 255 Then Exit Function
   Next i
   
   'Looks like we were passed a valid IP!
   IsIP = True
   

End Function

Public Function GetIPFromHost(ByVal HostName As String) As String '************************************************************ 'Returns the IP address given a host name (such as "www.vbgore.com" to 123.45.6.7) 'More info: http://www.vbgore.com/GameClient.TCP.GetIPFromHost '************************************************************ Dim udtWSAData As WSADATA Dim HostAddress As Long Dim HostInfo As typHOSTENT Dim IPLong As Long Dim IPBytes() As Byte Dim i As Integer

   On Error Resume Next
   
   If WSAStartup(257, udtWSAData) Then
       MsgBox "Error initializing winsock on WSAStartup!"
       GetIPFromHost = HostName
       Exit Function
   End If
   'Make sure a HTTP:// or FTP:// something wasn't added... some people like to do that
   If UCase$(Left$(HostName, 7)) = "HTTP://" Then
       HostName = Right$(HostName, Len(HostName) - 7)
   ElseIf UCase$(Left$(HostName, 6)) = "FTP://" Then
       HostName = Right$(HostName, Len(HostName) - 6)
   End If
   
   'If we were already passed an IP, just abort since we have what we want
   If IsIP(HostName) Then
       GetIPFromHost = HostName
       Exit Function
   End If
   
   'Get the host address
   HostAddress = apiGetHostByName(HostName)
   
   'Failure!
   If HostAddress = 0 Then Exit Function
   
   'Move the memory around to get it in a format we can read
   apiCopyMemory HostInfo, HostAddress, LenB(HostInfo)
   apiCopyMemory IPLong, HostInfo.hAddrList, 4
   
   'Get the number of parts to the IP (will always be 4 as far as I know)
   ReDim IPBytes(1 To HostInfo.hLength)
   'Convert the address, stored in the format of a long, to 4 bytes (just simple long -> byte array conversion)
   apiCopyMemory IPBytes(1), IPLong, HostInfo.hLength
   
   'Add in the periods
   For i = 1 To HostInfo.hLength
       GetIPFromHost = GetIPFromHost & IPBytes(i) & "."
   Next
   
   'Remove the final period
   GetIPFromHost = Left$(GetIPFromHost, Len(GetIPFromHost) - 1)
   
   'Clean up the socket
   WSACleanup
   
   On Error GoTo 0

End Function

Sub InitSocket() '***************************************************************** 'Init the GOREsock socket 'More info: http://www.vbgore.com/GameClient.TCP.InitSocket '*****************************************************************

   'Save the game ini
   Call Var_Write(DataPath & "Game.ini", "INIT", "Name", UserName)
   If Not SavePass Then   'If the password wont be saved, clear it out
       Call Var_Write(DataPath & "Game.ini", "INIT", "Password", "")
   Else
       Call Var_Write(DataPath & "Game.ini", "INIT", "Password", UserPassword)
   End If
   
   'Clear the SoxID
   SoxID = 0
   
   'Clean out the socket so we can make a fresh new connection
   If frmMain.GOREsock.ShutDown <> soxERROR Then
   
       'Set up the socket
       'Leave the GetIPFromHost() wrapper there, this will convert a host name to IP if needed, or leave it as an IP if you pass an IP
       SoxID = frmMain.GOREsock.Connect(GetIPFromHost("127.0.0.1"), 10200)
       
       'If the SoxID = -1, then the connection failed, elsewise, we're good to go! W00t! ^_^
       If SoxID = -1 Then
           MsgBox "Unable to connect to the game server!" & vbCrLf & "Either the server is down or you are not connected to the internet.", vbOKOnly
       Else
           frmMain.GOREsock.SetOption SoxID, soxSO_TCP_NODELAY, True
       End If
   End If

End Sub

Sub Data_User_Trade_Trade(ByRef rBuf As DataBuffer) '************************************************************ 'Begins the trading sequence '<Name(S)><MyIndex(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Trade_Trade '************************************************************ Dim i As Long

   For i = 1 To 9
       TradeTable.Trade1(i).Amount = 0
       TradeTable.Trade1(i).Grh = 0
       TradeTable.Trade1(i).Name = vbNullString
       TradeTable.Trade1(i).Value = 0
       TradeTable.Trade2(i).Amount = 0
       TradeTable.Trade2(i).Grh = 0
       TradeTable.Trade2(i).Name = vbNullString
       TradeTable.Trade2(i).Value = 0
   Next i
   TradeTable.Gold1 = 0
   TradeTable.Gold2 = 0
   TradeTable.User1Accepted = 0
   TradeTable.User2Accepted = 0
   TradeTable.User1Name = vbNullString
   TradeTable.User2Name = vbNullString
   TradeTable.MyIndex = 0
   
   TradeTable.User1Name = rBuf.Get_String
   TradeTable.User2Name = rBuf.Get_String
   TradeTable.MyIndex = rBuf.Get_Byte
   ShowGameWindow(TradeWindow) = 1
   LastClickedWindow = TradeWindow

End Sub

Sub Data_User_Trade_UpdateTrade(ByRef rBuf As DataBuffer) '************************************************************ 'Update something about the trade currently taking place '<UserTableIndex(B)><TableSlot(B)><Amount(L)> (<GrhIndex(L)><ObjName(S)><ObjValue(L)>) 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Trade_UpdateTrade '************************************************************ Dim UserTableIndex As Byte Dim TableSlot As Byte Dim Amount As Long Dim GrhIndex As Long Dim ObjName As String Dim ObjValue As Long

   UserTableIndex = rBuf.Get_Byte
   TableSlot = rBuf.Get_Byte
   Amount = rBuf.Get_Long
   'Update the gold
   If TableSlot = 0 Then
       If TradeTable.MyIndex = UserTableIndex Then
           TradeTable.Gold1 = Amount
       Else
           TradeTable.Gold2 = Amount
       End If
   
   'Update an item
   ElseIf TableSlot <= 9 Then
       GrhIndex = rBuf.Get_Long
       ObjName = rBuf.Get_String
       ObjValue = rBuf.Get_Long
       If TradeTable.MyIndex = UserTableIndex Then
           TradeTable.Trade1(TableSlot).Amount = Amount
           TradeTable.Trade1(TableSlot).Grh = GrhIndex
           TradeTable.Trade1(TableSlot).Name = ObjName
           TradeTable.Trade1(TableSlot).Value = ObjValue
       Else
           TradeTable.Trade2(TableSlot).Amount = Amount
           TradeTable.Trade2(TableSlot).Grh = GrhIndex
           TradeTable.Trade2(TableSlot).Name = ObjName
           TradeTable.Trade2(TableSlot).Value = ObjValue
       End If
   End If

End Sub

Sub Data_User_Bank_UpdateSlot(ByRef rBuf As DataBuffer) '************************************************************ 'Updates a specific bank item '<Slot(B)><GrhIndex(L)> If GrhIndex > 0, <Amount(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Bank_UpdateSlot '************************************************************ Dim GrhIndex As Long Dim Amount As Integer Dim Slot As Byte

   'Get the values
   Slot = rBuf.Get_Byte
   GrhIndex = rBuf.Get_Long
   
   'Check if to get the amount
   If GrhIndex > 0 Then Amount = rBuf.Get_Integer
   'Update the item
   UserBank(Slot).Amount = Amount
   UserBank(Slot).GrhIndex = GrhIndex

End Sub

Sub Data_User_Bank_Open(ByRef rBuf As DataBuffer) '************************************************************ 'Sends the list of bank items 'Loop: <Slot(B)><GrhIndex(L)><Amount(I)> until Slot = 255 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Bank_Open '************************************************************ Dim GrhIndex As Long Dim Amount As Integer Dim Slot As Byte

   'Loop through the items until we get the terminator slot (255)
   Do
       
       'Get the slot
       Slot = rBuf.Get_Byte
       
       'Check if we have acquired the terminator slot
       If Slot = 255 Then Exit Do
       
       'Get the amount and obj index
       GrhIndex = rBuf.Get_Long
       Amount = rBuf.Get_Integer
       
       'Store the values
       UserBank(Slot).Amount = Amount
       UserBank(Slot).GrhIndex = GrhIndex
       
   Loop
   
   'Show the bank window
   ShowGameWindow(BankWindow) = 1
   LastClickedWindow = BankWindow

End Sub

Sub Data_Server_MakeProjectile(ByRef rBuf As DataBuffer) '************************************************************ 'Create a projectile from a ranged weapon '<AttackerIndex(I)><TargetIndex(I)><GrhIndex(L)><Rotate(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MakeProjectile '************************************************************ Dim AttackerIndex As Integer Dim TargetIndex As Integer Dim GrhIndex As Long Dim Rotate As Byte

   AttackerIndex = rBuf.Get_Integer
   TargetIndex = rBuf.Get_Integer
   GrhIndex = rBuf.Get_Long
   Rotate = rBuf.Get_Byte
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(AttackerIndex) Then Exit Sub
   If Not Engine_ValidChar(TargetIndex) Then Exit Sub
   
   'Create the projectile
   Engine_Projectile_Create AttackerIndex, TargetIndex, GrhIndex, Rotate
   

End Sub

Sub Data_User_SetWeaponRange(ByRef rBuf As DataBuffer) '************************************************************ 'Set the range of the current weapon used so we can do client-side ' distance checks before sending the attack to the server '<Range(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_SetWeaponRange '************************************************************

   UserAttackRange = rBuf.Get_Byte

End Sub

Sub Data_Server_SetCharSpeed(ByRef rBuf As DataBuffer) '************************************************************ 'Update a char's speed so we can move them the right speed '<CharIndex(I)><Speed(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_SetCharSpeed '************************************************************ Dim CharIndex As Integer Dim Speed As Byte

   CharIndex = rBuf.Get_Integer
   Speed = rBuf.Get_Byte
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   CharList(CharIndex).Speed = Speed

End Sub

Sub Data_Server_Message(ByRef rBuf As DataBuffer) '************************************************************ 'Server sending a common message to client (reccomended you send ' as many messages as possible via this method to save bandwidth) '<MessageID(B)><...depends on the message> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_Message '************************************************************ Dim MessageID As Byte Dim TempStr As String Dim TempInt As Integer Dim Str1 As String Dim Str2 As String Dim Lng1 As Long Dim Int1 As Integer Dim Int2 As Integer Dim Int3 As Integer Dim Byt1 As Byte

   'Get the message ID
   MessageID = rBuf.Get_Byte
   
   'Check what to do depending on the message ID
   '*** Please refer to the language file for the description of the numbers ***
   Select Case MessageID
       Case 1
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(1), "<npcname>", Str1), FontColor_Info
       Case 2
           Engine_AddToChatTextBuffer Message(2), FontColor_Fight
       Case 3
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(3), "<exp>", Lng1), FontColor_Info
       Case 4
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(4), "<gold>", Lng1), FontColor_Info
       Case 5
           Byt1 = rBuf.Get_Byte
           Engine_AddToChatTextBuffer Replace$(Message(5), "<skill>", Engine_SkillIDtoSkillName(Byt1)), FontColor_Info
       Case 6
           Byt1 = rBuf.Get_Byte
           Engine_AddToChatTextBuffer Replace$(Message(6), "<skill>", Engine_SkillIDtoSkillName(Byt1)), FontColor_Info
       Case 7
           Engine_AddToChatTextBuffer Message(7), FontColor_Quest
       Case 8
           Engine_AddToChatTextBuffer Message(8), FontColor_Quest
       Case 9
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           Int3 = rBuf.Get_Integer
           TempStr = Replace$(Message(9), "<amount>", Int1)
           TempStr = Replace$(TempStr, "<npcname>", Str1)
           Engine_AddToChatTextBuffer TempStr, FontColor_Quest
           Engine_MakeChatBubble Int3, Engine_WordWrap(TempStr, BubbleMaxWidth)
       Case 10
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           Int3 = rBuf.Get_Integer
           TempStr = Replace$(Message(10), "<amount>", Int1)
           TempStr = Replace$(TempStr, "<objname>", Str1)
           Engine_AddToChatTextBuffer TempStr, FontColor_Quest
           Engine_MakeChatBubble Int3, Engine_WordWrap(TempStr, BubbleMaxWidth)
       Case 11
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           Int2 = rBuf.Get_Integer
           Str2 = rBuf.Get_String
           Int3 = rBuf.Get_Integer
           TempStr = Replace$(Message(11), "<npcamount>", Int1)
           TempStr = Replace$(TempStr, "<npcname>", Str1)
           TempStr = Replace$(TempStr, "<objamount>", Int2)
           TempStr = Replace$(TempStr, "<objname>", Str2)
           Engine_AddToChatTextBuffer TempStr, FontColor_Quest
           Engine_MakeChatBubble Int3, Engine_WordWrap(TempStr, BubbleMaxWidth)
       Case 12
           Engine_AddToChatTextBuffer Message(12), FontColor_Quest
       Case 13
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(13), "<name>", Str1), FontColor_Info
       Case 14
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(14), "<cost>", Lng1), FontColor_Info
       Case 15
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(15), "<sender>", Str1), FontColor_Info
       Case 16
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(16), "<receiver>", Str1), FontColor_Info
       Case 17
           Engine_AddToChatTextBuffer Message(17), FontColor_Info
       Case 18
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(18), "<sender>", Str1), FontColor_Info
       Case 19
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(19), "<receiver>", Str1), FontColor_Info
       Case 20
           Engine_AddToChatTextBuffer Message(20), FontColor_Info
       Case 21
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(21), "<cost>", Lng1), FontColor_Info
       Case 22
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(22), "<name>", Str1), FontColor_Info
       Case 23
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(23), "<name>", Str1), FontColor_Info
       Case 24
           Engine_AddToChatTextBuffer Message(24), FontColor_Info
       Case 25
           Engine_AddToChatTextBuffer Message(25), FontColor_Info
       Case 26
           Engine_AddToChatTextBuffer Message(26), FontColor_Info
       Case 27
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           TempStr = Replace$(Message(27), "<amount>", Int1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<name>", Str1), FontColor_Info
       Case 28
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           TempStr = Replace$(Message(28), "<amount>", Int1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<name>", Str1), FontColor_Info
       Case 29
           Engine_AddToChatTextBuffer Message(29), FontColor_Info
       Case 30
           Str1 = rBuf.Get_String
           Str2 = rBuf.Get_String
           TempStr = Replace$(Message(30), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<desc>", Str2), FontColor_Info
       Case 31
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(31), "<name>", Str1), FontColor_Info
       Case 32
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(32), "<name>", Str1), FontColor_Info
       Case 33
           Engine_AddToChatTextBuffer Message(33), FontColor_Info
       Case 34
           Engine_AddToChatTextBuffer Message(34), FontColor_Info
       Case 35
           Byt1 = rBuf.Get_Byte
           Engine_AddToChatTextBuffer Replace$(Message(35), "<amount>", Byt1), FontColor_Info
       Case 36
           Engine_AddToChatTextBuffer Message(36), FontColor_Info
       Case 37
           Engine_AddToChatTextBuffer Message(37), FontColor_Info
       Case 38
           Engine_AddToChatTextBuffer Message(38), FontColor_Info
       Case 39
           Str1 = rBuf.Get_String
           Str2 = rBuf.Get_String
           TempStr = Replace$(Message(39), "<skill>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<name>", Str2), FontColor_Info
       Case 40
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(40), "<name>", Str1), FontColor_Info
       Case 41
           Str1 = rBuf.Get_String
           Int1 = rBuf.Get_Integer
           TempStr = Replace$(Message(41), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<power>", Int1), FontColor_Info
       Case 42
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(42), "<name>", Str1), FontColor_Info
       Case 43
           Str1 = rBuf.Get_String
           Int1 = rBuf.Get_Integer
           TempStr = Replace$(Message(43), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<power>", Int1), FontColor_Info
       Case 44
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(44), "<name>", Str1), FontColor_Info
       Case 45
           Str1 = rBuf.Get_String
           Int1 = rBuf.Get_Integer
           TempStr = Replace$(Message(45), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<power>", Int1), FontColor_Info
       Case 46
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(46), "<name>", Str1), FontColor_Info
       Case 47
           Str1 = rBuf.Get_String
           Int1 = rBuf.Get_Integer
           TempStr = Replace$(Message(47), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<power>", Int1), FontColor_Info
       Case 48
           Engine_AddToChatTextBuffer Message(48), FontColor_Info
       Case 49
           Engine_AddToChatTextBuffer Message(49), FontColor_Info
       Case 50
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(50), "<name>", Str1), FontColor_Info
       Case 51
           Engine_AddToChatTextBuffer Message(51), FontColor_Info
       Case 52
           Str1 = rBuf.Get_String
           Str2 = rBuf.Get_String
           TempStr = Replace$(Message(52), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<message>", Str2), FontColor_Talk
           LastWhisperName = Str1  'Set the name of the last person to whisper us
       Case 53
           Str1 = rBuf.Get_String
           Str2 = rBuf.Get_String
           TempStr = Replace$(Message(53), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<message>", Str2), FontColor_Talk
       Case 54
           Str1 = rBuf.Get_String
           Byt1 = rBuf.Get_Byte
           TempStr = Replace$(Message(54), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<value>", Byt1), FontColor_Info
       Case 55
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(55), "<value>", Lng1), FontColor_Info
       Case 56
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(56), "<name>", Str1), FontColor_Info
       Case 57
           Engine_AddToChatTextBuffer Message(57), FontColor_Info
       Case 58
           Str1 = rBuf.Get_String
           Int1 = rBuf.Get_Integer
           TempStr = Replace$(Message(58), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<amount>", Int1), FontColor_Info
       Case 59
           Str1 = rBuf.Get_String
           Int1 = rBuf.Get_Integer
           Int2 = rBuf.Get_Integer
           TempStr = Replace$(Message(59), "<name>", Str1)
           TempStr = Replace$(TempStr, "<amount>", Int1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<leftover>", Int2), FontColor_Info
       Case 60
           Engine_AddToChatTextBuffer Message(60), FontColor_Info
       Case 61
           Engine_AddToChatTextBuffer Message(61), FontColor_Info
       Case 62
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(62), "<level>", Lng1), FontColor_Info
       Case 63
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           TempStr = Replace$(Message(63), "<amount>", Int1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<name>", Str1), FontColor_Info
       Case 64
           Engine_AddToChatTextBuffer Message(64), FontColor_Info
       Case 65
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           TempStr = Replace$(Message(65), "<amount>", Int1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<name>", Str1), FontColor_Info
       Case 66
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           Int2 = rBuf.Get_Integer
           TempStr = Replace$(Message(66), "<amount>", Int1)
           TempStr = Replace$(TempStr, "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<leftover>", Int2), FontColor_Info
       Case 67
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           Lng1 = rBuf.Get_Long
           TempStr = Replace$(Message(67), "<amount>", Int1)
           TempStr = Replace$(TempStr, "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<cost>", Lng1), FontColor_Info
       Case 68
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(68), "<name>", Str1), FontColor_Info
       Case 69
           Engine_AddToChatTextBuffer Message(69), FontColor_Info
       Case 70
           Engine_AddToChatTextBuffer Message(70), FontColor_Info
       Case 71
           Byt1 = rBuf.Get_Byte
           Engine_AddToChatTextBuffer Replace$(Message(71), "<value>", Byt1), FontColor_Info
       Case 72
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(72), "<name>", Str1), FontColor_Info
       Case 73
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(73), "<name>", Str1), FontColor_Info
       Case 74
           Int1 = rBuf.Get_Integer
           Int2 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           TempStr = Replace$(Message(74), "<amount>", Int1)
           TempStr = Replace$(TempStr, "<total>", Int2)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<name>", Str1), FontColor_Quest
       Case 75
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(75), "<name>", Str1), FontColor_Info
       Case 76
           Str1 = rBuf.Get_String
           Str2 = rBuf.Get_String
           TempInt = rBuf.Get_Integer
           TempStr = Replace$(Message(76), "<name>", Str1)
           TempStr = Replace$(TempStr, "<message>", Str2)
           Engine_AddToChatTextBuffer TempStr, FontColor_Talk
           If TempInt > 0 Then Engine_MakeChatBubble TempInt, Engine_WordWrap(TempStr, BubbleMaxWidth)
       Case 77
           Str1 = rBuf.Get_String
           Str2 = rBuf.Get_String
           TempStr = Replace$(Message(77), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<gm>", Str2), FontColor_Info
       Case 78
           Int1 = rBuf.Get_Integer
           Engine_AddToChatTextBuffer Replace$(Message(78), "<value>", Int1), FontColor_Info
       Case 79
           MsgBox Message(79)
       Case 80
           Str1 = rBuf.Get_String
           MsgBox Replace$(Message(80), "<name>", Str1)
       Case 81
           MsgBox Message(81)
       Case 82
           MsgBox Message(82)
       Case 83
           MsgBox Message(83)
       Case 84
           MsgBox Message(84)
       Case 85
           MsgBox Message(85)
       Case 86
           Str1 = rBuf.Get_String
           Int1 = rBuf.Get_Integer
           TempStr = Replace$(Message(86), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<amount>", Int1), FontColor_Info
       'Case 87 to 93 - these are only used by the client
       Case 94
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(94), "<name>", Str1), FontColor_Info
       Case 95
           Int1 = rBuf.Get_Integer
           Engine_AddToChatTextBuffer Replace$(Message(95), "<index>", Int1), FontColor_Info
       Case 96
           Int1 = rBuf.Get_Integer
           Str1 = rBuf.Get_String
           Lng1 = rBuf.Get_Long
           TempStr = Replace$(Message(96), "<amount>", Int1)
           TempStr = Replace$(TempStr, "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<cost>", Lng1), FontColor_Info
       Case 97
           Engine_AddToChatTextBuffer Message(97), FontColor_Info
       Case 98
           Engine_AddToChatTextBuffer Message(98), FontColor_Info
       Case 99
           Engine_AddToChatTextBuffer Message(99), FontColor_Info
       Case 100
           Str1 = rBuf.Get_String
           TempStr = Replace$(Message(100), "<linebreak>", vbCrLf)
           MsgBox Replace$(TempStr, "<reason>", Str1), vbOKOnly Or vbCritical
           IsUnloading = 1
           Engine_UnloadAllForms
       Case 101
           Engine_AddToChatTextBuffer Message(101), FontColor_Info
       Case 102
           Engine_AddToChatTextBuffer Message(102), FontColor_Info
       Case 106
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(106), "<name>", Str1), FontColor_Group
       Case 107
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(107), "<name>", Str1), FontColor_Group
       Case 108
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(108), "<name>", Str1), FontColor_Group
       Case 109
           Engine_AddToChatTextBuffer Message(109), FontColor_Group
       Case 110
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(110), "<name>", Str1), FontColor_Group
       Case 111
           Engine_AddToChatTextBuffer Message(111), FontColor_Group
       Case 112
           Engine_AddToChatTextBuffer Message(112), FontColor_Group
       Case 113
           Engine_AddToChatTextBuffer Message(113), FontColor_Group
       Case 114
           Engine_AddToChatTextBuffer Message(114), FontColor_Group
       Case 115
           Str1 = rBuf.Get_String
           Int1 = rBuf.Get_Integer
           TempStr = Replace$(Message(115), "<name>", Str1)
           Engine_AddToChatTextBuffer Replace$(TempStr, "<time>", Int1), FontColor_Group
       Case 116
           Engine_AddToChatTextBuffer Message(116), FontColor_Group
       Case 117
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(117), "<amount>", Lng1), FontColor_Info
       Case 118
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(118), "<amount>", Lng1), FontColor_Info
       Case 119
           Engine_AddToChatTextBuffer Message(119), FontColor_Info
       Case 120
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(120), "<amount>", Lng1), FontColor_Info
       Case 121
           Engine_AddToChatTextBuffer Message(121), FontColor_Info
       Case 123
           Engine_AddToChatTextBuffer Message(123), FontColor_Group
       Case 125
           Engine_AddToChatTextBuffer Message(125), FontColor_Info
       Case 127
           Engine_AddToChatTextBuffer Message(127), FontColor_Info
       Case 128
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(128), "<name>", Str1), FontColor_Info
       Case 129
           Byt1 = rBuf.Get_Byte
           If Byt1 <= QuestInfoUBound Then
               Str1 = QuestInfo(Byt1).Name
               QuestInfo(Byt1).Desc = vbNullString
               QuestInfo(Byt1).Name = vbNullString
               Lng1 = QuestInfoUBound
               Do
                   If Lng1 = 0 Then Exit Do
                   If QuestInfo(Lng1).Name <> vbNullString Then Exit Do
                   Lng1 = Lng1 - 1
               Loop
               If Lng1 = 0 Then
                   Erase QuestInfo
                   QuestInfoUBound = 0
               Else
                   ReDim Preserve QuestInfo(1 To Lng1)
                   QuestInfoUBound = Lng1
               End If
               If Str1 <> vbNullString Then
                   Engine_AddToChatTextBuffer Replace$(Message(129), "<name>", Str1), FontColor_Quest
               End If
           End If
       Case 130
           Engine_AddToChatTextBuffer Message(130), FontColor_Info
       Case 131
           Engine_AddToChatTextBuffer Message(131), FontColor_Info
       Case 132
           Engine_AddToChatTextBuffer Message(132), FontColor_Info
       Case 134
           Str1 = rBuf.Get_String
           Engine_AddToChatTextBuffer Replace$(Message(134), "<name>", Str1), FontColor_Quest
       Case 137
           Engine_AddToChatTextBuffer Message(137), FontColor_Info
       Case 138
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(138), "<amount>", Lng1), FontColor_Info
       Case 139
           Lng1 = rBuf.Get_Long
           Engine_AddToChatTextBuffer Replace$(Message(139), "<amount>", Lng1), FontColor_Info
   End Select

End Sub

Sub Data_Server_Connect() '************************************************************ 'Server is telling the client they have successfully logged in '<> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_Connect '************************************************************

   'Set the socket state
   SocketOpen = 1
   If EngineRun = False Then
   
       'Load user config
       Game_Config_Load
   
       'Unload the connect form
       Unload frmConnect
   
       'Load main form
       Load frmMain
       frmMain.Visible = True
       frmMain.Show
       frmMain.SetFocus
       Input_Keys_ClearQueue
       DoEvents
           
       'Load the engine
       Engine_Init_TileEngine
   
       'Get the device
       frmMain.Show
       frmMain.SetFocus
       DoEvents
       DIDevice.Acquire
       
       Unload frmNew
       Unload frmConnect
   
   End If
   
   'Send the data
   Data_Send

End Sub

Sub Data_Server_Disconnect() '************************************************************ 'Forces the client to disconnect from the server '<> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_Disconnect '************************************************************

   IsUnloading = 1

End Sub

Sub Data_Comm_Talk(ByRef rBuf As DataBuffer) '************************************************************ 'Send data to chat buffer '<Text(S)><FontColorID(B)>(<CharIndex(I)>) 'More info: http://www.vbgore.com/GameClient.TCP.Data_Comm_Talk '************************************************************ Dim CharIndex As Integer Dim TempStr As String Dim TempLng As Long Dim TempByte As Byte

   'Get the text
   TempStr = rBuf.Get_String
   TempByte = rBuf.Get_Byte
   'Filter the temp string
   TempStr = Game_FilterString(TempStr)
   
   'See if we have to make a bubble
   If TempByte And DataCode.Comm_UseBubble Then
       
       'We need a char index
       CharIndex = rBuf.Get_Integer
       
   End If
   
   'Now that we have all the values, check if it is a valid string
   If Not Game_ValidString(TempStr) Then Exit Sub
   
   'Split up the string for our chat bubble and assign it to the character
   If CharIndex > 0 Then
       If CharIndex <= LastChar Then
           Engine_MakeChatBubble CharIndex, Engine_WordWrap(TempStr, BubbleMaxWidth)
       End If
   End If
   
   'Get the color
   Select Case TempByte
       Case DataCode.Comm_FontType_Fight
           TempLng = FontColor_Fight
       Case DataCode.Comm_FontType_Info
           TempLng = FontColor_Info
       Case DataCode.Comm_FontType_Quest
           TempLng = FontColor_Quest
       Case DataCode.Comm_FontType_Talk
           TempLng = FontColor_Talk
       Case DataCode.Comm_FontType_Group
           TempLng = FontColor_Group
       Case Else
           TempLng = FontColor_Talk
   End Select
   
   'Add the text in the text box
   Engine_AddToChatTextBuffer TempStr, TempLng

End Sub

Sub Data_Map_LoadMap(ByRef rBuf As DataBuffer) '************************************************************ 'Load the map the server told us to load '<MapNum(I)><ServerSideVersion(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Map_LoadMap '************************************************************ Dim FileNum As Byte Dim MapNumInt As Integer Dim SSV As Integer Dim TempInt As Integer

   'Clear the target character
   TargetCharIndex = 0
   MapNumInt = rBuf.Get_Integer
   SSV = rBuf.Get_Integer
   If Engine_FileExist(MapPath & MapNumInt & ".map", vbNormal) Then  'Get Version Num
       FileNum = FreeFile
       Open MapPath & MapNumInt & ".map" For Binary As #FileNum
           Seek #FileNum, 1
           Get #FileNum, , TempInt
       Close #FileNum
       If TempInt = SSV Then   'Correct Version
           Game_Map_Switch MapNumInt
           sndBuf.Put_Byte DataCode.Map_DoneLoadingMap 'Tell the server we are done loading map
       Else
           'Not correct version
           MsgBox Message(105), vbOKOnly Or vbCritical
           EngineRun = False
           IsUnloading = 1
       End If
   Else
       'Didn't find map
       MsgBox Message(105), vbOKOnly Or vbCritical
       EngineRun = False
       IsUnloading = 1
   End If

End Sub

Sub Data_Map_SendName(ByRef rBuf As DataBuffer) '************************************************************ 'Set the map name and weather '<Name(S)><Weather(B)><Music(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Map_SendName '************************************************************ Dim Music As Byte

   MapInfo.Name = rBuf.Get_String
   MapInfo.Weather = rBuf.Get_Byte
   
   'Change the music file if we need to
   Music = rBuf.Get_Byte
   If MapInfo.Music <> Music Then
       Music_Stop 1
       If Music <> 0 Then
           MapInfo.Music = Music
           Music_Load MusicPath & Music & ".mp3", 1
           Music_Play 1
           Music_Volume 86, 1
       End If
   End If
   

End Sub

Sub Data_Send() '************************************************************ 'Send data buffer to the server 'More info: http://www.vbgore.com/GameClient.TCP.Data_Send '************************************************************

   'Check that we have data to send
   If SocketOpen = 0 Then DoEvents
   If sndBuf.HasBuffer Then
       If SocketOpen = 0 Then DoEvents
       'Send the data
       frmMain.GOREsock.SendData SoxID, sndBuf.Get_Buffer
       
       'Clear the buffer, get it ready for next use
       sndBuf.Clear
 
   End If

End Sub

Sub Data_Server_ChangeCharType(ByRef rBuf As DataBuffer) '************************************************************ 'Change a character by the character index '<CharIndex(I)><CharType(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_ChangeCharType '************************************************************ Dim CharIndex As Integer Dim CharType As Byte

   CharIndex = rBuf.Get_Integer
   CharType = rBuf.Get_Byte
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   'Change the character's type
   CharList(CharIndex).CharType = CharType

End Sub

Sub Data_Server_ChangeChar(ByRef rBuf As DataBuffer) '************************************************************ 'Change a character by the character index '<CharIndex(I)><Flags(B)>(<Body(I)><Head(I)><Weapon(I)><Hair(I)><Wings(I)>) 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_ChangeChar '************************************************************ Dim flags As Byte Dim CharIndex As Integer Dim CharBody As Integer Dim CharHead As Integer Dim CharWeapon As Integer Dim CharHair As Integer Dim CharWings As Integer Dim DontSetData As Boolean

   'Get the character index we are changing
   CharIndex = rBuf.Get_Integer
   
   'Get the flags on what data we need to get
   flags = rBuf.Get_Byte
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then DontSetData = True
   
   'Get the data needed
   If flags And 1 Then
       CharBody = rBuf.Get_Integer
       If Not DontSetData Then CharList(CharIndex).Body = BodyData(CharBody)
   End If
   If flags And 2 Then
       CharHead = rBuf.Get_Integer
       If Not DontSetData Then CharList(CharIndex).Head = HeadData(CharHead)
   End If
   If flags And 4 Then
       CharWeapon = rBuf.Get_Integer
       If Not DontSetData Then CharList(CharIndex).Weapon = WeaponData(CharWeapon)
   End If
   If flags And 8 Then
       CharHair = rBuf.Get_Integer
       If Not DontSetData Then CharList(CharIndex).Hair = HairData(CharHair)
   End If
   If flags And 16 Then
       CharWings = rBuf.Get_Integer
       If Not DontSetData Then CharList(CharIndex).Wings = WingData(CharWings)
   End If
   

End Sub

Sub Data_Server_CharHP(ByRef rBuf As DataBuffer) '************************************************************ 'Set the character HP '<HP(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_CharHP '************************************************************ Dim CharIndex As Integer Dim HP As Byte

   HP = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   CharList(CharIndex).HealthPercent = HP

End Sub

Sub Data_Server_CharMP(ByRef rBuf As DataBuffer) '************************************************************ 'Set the character MP '<MP(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_CharMP '************************************************************ Dim CharIndex As Integer Dim MP As Byte

   MP = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   CharList(CharIndex).ManaPercent = MP

End Sub

Sub Data_Server_EraseChar(ByRef rBuf As DataBuffer) '************************************************************ 'Erase a character by the character index '<CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_EraseChar '************************************************************

   Engine_Char_Erase rBuf.Get_Integer

End Sub

Sub Data_Server_EraseObject(ByRef rBuf As DataBuffer) '************************************************************ 'Erase an object on the object layer '<X(B)><Y(B)><Grh(L)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_EraseObject '************************************************************ Dim j As Integer Dim X As Byte Dim Y As Byte Dim Grh As Long

   X = rBuf.Get_Byte
   Y = rBuf.Get_Byte
   Grh = rBuf.Get_Long
   'Loop through until we find the object on (X,Y) then kill it
   For j = 1 To LastObj
       If OBJList(j).Pos.X = X Then
           If OBJList(j).Pos.Y = Y Then
               If OBJList(j).Grh.GrhIndex = Grh Then
                   Engine_OBJ_Erase j
                   Exit Sub
               End If
           End If
       End If
   Next j

End Sub

Sub Data_Server_IconBlessed(ByRef rBuf As DataBuffer) '************************************************************ 'Hide/show blessed icon '<State(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_IconBlessed '************************************************************ Dim State As Byte Dim CharIndex As Integer

   State = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   CharList(CharIndex).CharStatus.Blessed = State

End Sub

Sub Data_Server_IconCursed(ByRef rBuf As DataBuffer) '************************************************************ 'Hide/show cursed icon '<State(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_IconCursed '************************************************************ Dim State As Byte Dim CharIndex As Integer

   State = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   CharList(CharIndex).CharStatus.Cursed = State

End Sub

Sub Data_Server_IconIronSkin(ByRef rBuf As DataBuffer) '************************************************************ 'Hide/show ironskinned icon '<State(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_IconIronSkin '************************************************************ Dim State As Byte Dim CharIndex As Integer

   State = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   CharList(CharIndex).CharStatus.IronSkinned = State

End Sub

Sub Data_Server_IconProtected(ByRef rBuf As DataBuffer) '************************************************************ 'Hide/show protected icon '<State(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_IconProtected '************************************************************ Dim State As Byte Dim CharIndex As Integer

   State = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   CharList(CharIndex).CharStatus.Protected = State

End Sub

Sub Data_Server_IconSpellExhaustion(ByRef rBuf As DataBuffer) '************************************************************ 'Hide/show spell exhaustion icon '<State(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_IconSpellExhaustion '************************************************************ Dim State As Byte Dim CharIndex As Integer

   State = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   CharList(CharIndex).CharStatus.Exhausted = State

End Sub

Sub Data_Server_IconStrengthened(ByRef rBuf As DataBuffer) '************************************************************ 'Hide/show strengthened icon '<State(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_IconStrengthened '************************************************************ Dim State As Byte Dim CharIndex As Integer

   State = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   CharList(CharIndex).CharStatus.Strengthened = State

End Sub

Sub Data_Server_IconWarCursed(ByRef rBuf As DataBuffer) '************************************************************ 'Hide/show warcursed icon '<State(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_IconWarCursed '************************************************************ Dim State As Byte Dim CharIndex As Integer

   State = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   CharList(CharIndex).CharStatus.WarCursed = State

End Sub

Sub Data_Server_Mailbox(ByRef rBuf As DataBuffer) '************************************************************ 'Recieve the list of messages from a mailbox 'Loop: <New(B)><WriterName(S)><Date(S)><Subject(S)>...<EndFlag(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_Mailbox '************************************************************ Dim NewB As Byte Dim WName As String Dim SDate As String Dim Subj As String

   ShowGameWindow(MailboxWindow) = 1
   
   SelMessage = 0
   LastClickedWindow = MailboxWindow
   MailboxListBuffer = vbNullString
   Do
       NewB = rBuf.Get_Byte
       If NewB = 255 Then Exit Do  'If 1 or 0, it is a message, if 255, it is the EndFlag
       WName = rBuf.Get_String
       SDate = rBuf.Get_String
       Subj = rBuf.Get_String
       MailboxListBuffer = MailboxListBuffer & IIf(NewB, "New - ", "Old - ") & Subj & " - " & WName & " - " & SDate & vbCrLf
   Loop

End Sub

Sub Data_Server_MailItemRemove(ByRef rBuf As DataBuffer) '************************************************************ 'Remove item from mailbox '<ItemIndex(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MailItemRemove '************************************************************ Dim ItemIndex As Byte

   ItemIndex = rBuf.Get_Byte
   ReadMailData.Obj(ItemIndex) = 0

End Sub

Sub Data_Server_MailObjUpdate(ByRef rBuf As DataBuffer) '************************************************************ 'Updates the objects in a mail message '<NumObjs(B)> Loop: <ObjGrhIndex(L)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MailObjUpdate '************************************************************ Dim NumObjs As Byte Dim X As Byte

   'Clear the current objects
   For X = 1 To MaxMailObjs
       ReadMailData.Obj(X) = 0
       ReadMailData.ObjName(X) = 0
       ReadMailData.ObjAmount(X) = 0
   Next X
   
   'Get the number of objects
   NumObjs = rBuf.Get_Byte
   
   'Get the mail objects
   For X = 1 To NumObjs
       ReadMailData.Obj(X) = rBuf.Get_Long
       ReadMailData.ObjName(X) = rBuf.Get_String
       ReadMailData.ObjAmount(X) = rBuf.Get_Integer
   Next X

End Sub

Sub Data_Server_MailMessage(ByRef rBuf As DataBuffer) '************************************************************ 'Recieve message that was requested to be read '<Message(S-EX)><Subject(S)><WriterName(S)><NumObjs(B)> Loop: <ObjGrhIndex(L)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MailMessage '************************************************************ Dim NumObjs As Byte Dim i As Long

   'Clear the current objects
   For i = 1 To MaxMailObjs
       ReadMailData.Obj(i) = 0
       ReadMailData.ObjName(i) = 0
       ReadMailData.ObjAmount(i) = 0
   Next i
   
   'Show the correct windows
   ShowGameWindow(MailboxWindow) = 0
   ShowGameWindow(ViewMessageWindow) = 1
   LastClickedWindow = ViewMessageWindow
   
   'Get the data
   ReadMailData.Message = rBuf.Get_StringEX
   ReadMailData.Message = Engine_WordWrap(ReadMailData.Message, GameWindow.ViewMessage.Message.Width)
   ReadMailData.Subject = rBuf.Get_String
   ReadMailData.WriterName = rBuf.Get_String
   NumObjs = rBuf.Get_Byte
   For i = 1 To NumObjs
       ReadMailData.Obj(i) = rBuf.Get_Long
       ReadMailData.ObjName(i) = rBuf.Get_String
       ReadMailData.ObjAmount(i) = rBuf.Get_Integer
   Next i

End Sub

Sub Data_Server_MakeCharCached(ByRef rBuf As DataBuffer) '************************************************************ 'Create a character and set their information '<Flags(I)><Body(I)><Head(I)><Heading(B)><CharIndex(I)><X(B)><Y(B)><Speed(B)><Name(S)><Weapon(I)><Hair(I)><Wings(I)> ' <HP%(B)><MP%(B)><ChatID(B)><CharType(B)> (<OwnerCharIndex(I)>) 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MakeCharCached '************************************************************ Dim flags As Integer Dim Body As Integer Dim Head As Integer Dim Heading As Byte Dim CharIndex As Integer Dim X As Byte Dim Y As Byte Dim Speed As Byte Dim Name As String Dim Weapon As Integer Dim Hair As Integer Dim Wings As Integer Dim HP As Byte Dim MP As Byte Dim ChatID As Byte Dim CharType As Byte Dim OwnerChar As Integer

   'Retrieve all the information
   flags = rBuf.Get_Integer
   If flags And 1 Then Body = rBuf.Get_Integer Else Body = PacketCache.Server_MakeChar.Body
   If flags And 2 Then Head = rBuf.Get_Integer Else Head = PacketCache.Server_MakeChar.Head
   If flags And 4 Then Heading = rBuf.Get_Byte Else Heading = PacketCache.Server_MakeChar.Heading
   CharIndex = rBuf.Get_Integer
   If flags And 8 Then X = rBuf.Get_Byte Else X = PacketCache.Server_MakeChar.X
   If flags And 16 Then Y = rBuf.Get_Byte Else Y = PacketCache.Server_MakeChar.Y
   If flags And 32 Then Speed = rBuf.Get_Byte Else Speed = PacketCache.Server_MakeChar.Speed
   If flags And 64 Then Name = rBuf.Get_String Else Name = PacketCache.Server_MakeChar.Name
   If flags And 128 Then Weapon = rBuf.Get_Integer Else Weapon = PacketCache.Server_MakeChar.Weapon
   If flags And 256 Then Hair = rBuf.Get_Integer Else Hair = PacketCache.Server_MakeChar.Hair
   If flags And 512 Then Wings = rBuf.Get_Integer Else Wings = PacketCache.Server_MakeChar.Wings
   If flags And 1024 Then HP = rBuf.Get_Byte Else HP = PacketCache.Server_MakeChar.HP
   If flags And 2048 Then MP = rBuf.Get_Byte Else MP = PacketCache.Server_MakeChar.MP
   If flags And 4096 Then ChatID = rBuf.Get_Byte Else ChatID = PacketCache.Server_MakeChar.ChatID
   If flags And 8192 Then CharType = rBuf.Get_Byte Else CharType = PacketCache.Server_MakeChar.CharType
   
   'Check for the owner char index if the char is a slave NPC
   If CharType = ClientCharType_Slave Then OwnerChar = rBuf.Get_Integer
   
   'Store the new values for the cache
   With PacketCache.Server_MakeChar
       .Body = Body
       .Head = Head
       .Heading = Heading
       .X = X
       .Y = Y
       .Speed = Speed
       .Name = Name
       .Weapon = Weapon
       .Hair = Hair
       .Wings = Wings
       .HP = HP
       .MP = MP
       .ChatID = ChatID
       .CharType = CharType
   End With
 
   'Create the character
   Engine_Char_Make CharIndex, Body, Head, Heading, X, Y, Speed, Name, Weapon, Hair, Wings, ChatID, CharType, HP, MP
   'Apply the owner index value
   CharList(CharIndex).OwnerChar = OwnerChar

End Sub

Sub Data_Server_MakeChar(ByRef rBuf As DataBuffer) '************************************************************ 'Create a character and set their information '<Body(I)><Head(I)><Heading(B)><CharIndex(I)><X(B)><Y(B)><Speed(B)><Name(S)><Weapon(I)><Hair(I)><Wings(I)> ' <HP%(B)><MP%(B)><ChatID(B)><CharType(B)> (<OwnerCharIndex(I)>) 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MakeChar '************************************************************ Dim Body As Integer Dim Head As Integer Dim Heading As Byte Dim CharIndex As Integer Dim X As Byte Dim Y As Byte Dim Speed As Byte Dim Name As String Dim Weapon As Integer Dim Hair As Integer Dim Wings As Integer Dim HP As Byte Dim MP As Byte Dim ChatID As Byte Dim CharType As Byte Dim OwnerChar As Integer

   'Retrieve all the information
   Body = rBuf.Get_Integer
   Head = rBuf.Get_Integer
   Heading = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   X = rBuf.Get_Byte
   Y = rBuf.Get_Byte
   Speed = rBuf.Get_Byte
   Name = rBuf.Get_String
   Weapon = rBuf.Get_Integer
   Hair = rBuf.Get_Integer
   Wings = rBuf.Get_Integer
   HP = rBuf.Get_Byte
   MP = rBuf.Get_Byte
   ChatID = rBuf.Get_Byte
   CharType = rBuf.Get_Byte
   
   'Check for the owner char index if the char is a slave NPC
   If CharType = ClientCharType_Slave Then OwnerChar = rBuf.Get_Integer
   
   'Create the character
   Engine_Char_Make CharIndex, Body, Head, Heading, X, Y, Speed, Name, Weapon, Hair, Wings, ChatID, CharType, HP, MP
   'Apply the owner index value
   CharList(CharIndex).OwnerChar = OwnerChar

End Sub

Sub Data_Server_MakeObject(ByRef rBuf As DataBuffer) '************************************************************ 'Create an object on the object layer '<GrhIndex(L)><X(B)><Y(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MakeObject '************************************************************ Dim GrhIndex As Long Dim X As Byte Dim Y As Byte

   'Get the values
   GrhIndex = rBuf.Get_Long
   X = rBuf.Get_Byte
   Y = rBuf.Get_Byte
   'Create the object
   If GrhIndex > 0 Then Engine_OBJ_Create GrhIndex, X, Y

End Sub

Sub Data_Server_MoveChar(ByRef rBuf As DataBuffer) '************************************************************ 'Move a character '<CharIndex(I)><X(B)><Y(B)><Heading(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MoveChar '************************************************************ Dim CharIndex As Integer Dim X As Integer Dim Y As Integer Dim nX As Integer Dim nY As Integer Dim Heading As Byte Dim Running As Byte

   CharIndex = rBuf.Get_Integer
   X = rBuf.Get_Byte
   Y = rBuf.Get_Byte
   Heading = rBuf.Get_Byte
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   'Check if running
   If Heading > 128 Then
       Heading = Heading Xor 128
       Running = 1
   End If
   
   'Make sure the char is the right starting position
   Select Case Heading
       Case NORTH: nX = 0: nY = -1
       Case EAST: nX = 1: nY = 0
       Case SOUTH: nX = 0: nY = 1
       Case WEST: nX = -1: nY = 0
       Case NORTHEAST: nX = 1: nY = -1
       Case SOUTHEAST: nX = 1: nY = 1
       Case SOUTHWEST: nX = -1: nY = 1
       Case NORTHWEST: nX = -1: nY = -1
   End Select
   CharList(CharIndex).Pos.X = X - nX
   CharList(CharIndex).Pos.Y = Y - nY
   
   'Move the character
   Engine_Char_Move_ByPos CharIndex, X, Y, Running

End Sub

Sub Data_Server_PlaySound(ByRef rBuf As DataBuffer) '************************************************************ 'Play a wave file '<WaveNum(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_PlaySound '************************************************************ Dim WaveNum As Byte

   WaveNum = rBuf.Get_Byte
   
   'Check that we are using sounds
   If UseSfx = 0 Then Exit Sub
   
   'Create the buffer if needed
   If SoundBufferTimer(WaveNum) < timeGetTime Then
       If DSBuffer(WaveNum) Is Nothing Then Sound_Set DSBuffer(WaveNum), WaveNum
   End If
   
   'Update the timer
   SoundBufferTimer(WaveNum) = timeGetTime + SoundBufferTimerMax
   Sound_Play DSBuffer(WaveNum), DSBPLAY_DEFAULT

End Sub

Sub Data_Server_PlaySound3D(ByRef rBuf As DataBuffer) '************************************************************ 'Play a wave file with 3D effect '<WaveNum(B)><X(B)><Y(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_PlaySound3D '************************************************************ Dim WaveNum As Byte Dim X As Integer Dim Y As Integer

   WaveNum = rBuf.Get_Byte
   X = rBuf.Get_Byte
   Y = rBuf.Get_Byte
   
   Sound_Play3D WaveNum, X, Y

End Sub

Sub Data_Server_SetCharDamage(ByRef rBuf As DataBuffer) '************************************************************ 'Damage a character and display it '<CharIndex(I)><Damage(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_SetCharDamage '************************************************************ Dim CharIndex As Integer Dim Damage As Integer

   CharIndex = rBuf.Get_Integer
   Damage = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   'Create the blood (if damage)
   If Damage > 0 Then Engine_Blood_Create CharList(CharIndex).Pos.X, CharList(CharIndex).Pos.Y
   'Create the damage
   Engine_Damage_Create CharList(CharIndex).Pos.X, CharList(CharIndex).Pos.Y, Damage
   
   'Aggressive face
   If Damage > 0 Then
       CharList(CharIndex).Aggressive = 1
       CharList(CharIndex).AggressiveCounter = timeGetTime + AGGRESSIVEFACETIME
   End If
   

End Sub

Sub Data_Server_SetUserPosition(ByRef rBuf As DataBuffer) '************************************************************ 'Set the user's position '<X(B)><Y(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_SetUserPosition '************************************************************ Dim X As Byte Dim Y As Byte

   'Get the position
   X = rBuf.Get_Byte
   Y = rBuf.Get_Byte
   'Check for a valid range
   If X < 1 Then Exit Sub
   If X > MapInfo.Width Then Exit Sub
   If Y < 1 Then Exit Sub
   If Y > MapInfo.Height Then Exit Sub
   'Check for a valid UserCharIndex
   If UserCharIndex <= 0 Or UserCharIndex > LastChar Then
   
       'We have an invalid user char index, so we must have the wrong one - request an update on the right one
       sndBuf.Put_Byte DataCode.User_RequestUserCharIndex
       Exit Sub
       
   End If
   'Check if the position is even different
   If X <> UserPos.X Or Y <> UserPos.Y Then
   
       'Update the user's position
       UserPos.X = X
       UserPos.Y = Y
       CharList(UserCharIndex).Pos = UserPos
       'If there is a targeted char, check if the path is valid
       If TargetCharIndex > 0 Then
           If TargetCharIndex <= LastChar Then
               On Error Resume Next    'Sometimes something strange will cause this to fail when a target dies - just ignore it
                   ClearPathToTarget = Engine_ClearPath(CharList(UserCharIndex).Pos.X, CharList(UserCharIndex).Pos.Y, CharList(TargetCharIndex).Pos.X, CharList(TargetCharIndex).Pos.Y)
               On Error GoTo 0
           End If
       End If
       
   End If

End Sub

Sub Data_Server_UserCharIndex(ByRef rBuf As DataBuffer) '************************************************************ 'Set the user character index '<CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_UserCharIndex '************************************************************

   'Retrieve the index of the user's character
   UserCharIndex = rBuf.Get_Integer
   UserPos = CharList(UserCharIndex).Pos
   
   'Update the map-bound sound effects
   Sound_UpdateMap

End Sub

Sub Data_Combo_SlashSoundRotateDamage(ByRef rBuf As DataBuffer) '************************************************************ 'Combines slash, 3d sound, damage and rotation packets together '<AttackerIndex(I)><TargetIndex(I)><SlashGrh(L)><Sfx(B)><Damage(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Combo_SlashSoundRotateDamage '************************************************************ Dim AttackerIndex As Integer Dim TargetIndex As Integer Dim SlashGrh As Long Dim Sfx As Byte Dim Damage As Integer Dim NewHeading As Byte Dim Angle As Integer

   AttackerIndex = rBuf.Get_Integer
   TargetIndex = rBuf.Get_Integer
   SlashGrh = rBuf.Get_Long
   Sfx = rBuf.Get_Byte
   Damage = rBuf.Get_Integer
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(AttackerIndex) Then Exit Sub
   If Not Engine_ValidChar(TargetIndex) Then Exit Sub
   
   'Rotate the AttackerIndex to face TargetIndex
   NewHeading = Engine_FindDirection(CharList(AttackerIndex).Pos, CharList(TargetIndex).Pos)
   CharList(AttackerIndex).HeadHeading = NewHeading
   CharList(AttackerIndex).Heading = NewHeading
   
   'Get the new heading
   Select Case CharList(AttackerIndex).Heading
       Case NORTH
           Angle = 0
       Case NORTHEAST
           Angle = 45
       Case EAST
           Angle = 90
       Case SOUTHEAST
           Angle = 135
       Case SOUTH
           Angle = 180
       Case SOUTHWEST
           Angle = 225
       Case WEST
           Angle = 270
       Case NORTHWEST
           Angle = 315
   End Select
   'Create the effect
   Engine_Effect_Create CharList(AttackerIndex).Pos.X, CharList(AttackerIndex).Pos.Y, SlashGrh, Angle, 150, 0
   
   'Play the sound
   Sound_Play3D Sfx, CharList(AttackerIndex).Pos.X, CharList(AttackerIndex).Pos.Y
   
   'Create the blood (if damage)
   If Damage > 0 Then Engine_Blood_Create CharList(TargetIndex).Pos.X, CharList(TargetIndex).Pos.Y
   'Create the damage
   Engine_Damage_Create CharList(TargetIndex).Pos.X, CharList(TargetIndex).Pos.Y, Damage
   
   'Start the attack animation
   CharList(AttackerIndex).Body.Attack(CharList(AttackerIndex).Heading).Started = 1
   CharList(AttackerIndex).Body.Attack(CharList(AttackerIndex).Heading).FrameCounter = 1
   CharList(AttackerIndex).Body.Attack(CharList(AttackerIndex).Heading).LastCount = timeGetTime
   CharList(AttackerIndex).Weapon.Attack(CharList(AttackerIndex).Heading).FrameCounter = 1
   CharList(AttackerIndex).ActionIndex = 2
   
   'Aggressive face
   If Damage > 0 Then
       CharList(TargetIndex).Aggressive = 1
       CharList(TargetIndex).AggressiveCounter = timeGetTime + AGGRESSIVEFACETIME
   End If

End Sub

Sub Data_Combo_ProjectileSoundRotateDamage(ByRef rBuf As DataBuffer) '************************************************************ 'Combines projectile, 3d sound, damage and rotation packets together '<AttackerIndex(I)><TargetIndex(I)><ProjectileGrh(L)><RotateSpeed(B)><Sfx(B)><Damage(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Combo_ProjectileSoundRotateDamage '************************************************************ Dim AttackerIndex As Integer Dim TargetIndex As Integer Dim GrhIndex As Long Dim RotateSpeed As Byte Dim Sfx As Byte Dim NewHeading As Byte Dim Damage As Integer

   AttackerIndex = rBuf.Get_Integer
   TargetIndex = rBuf.Get_Integer
   GrhIndex = rBuf.Get_Long
   RotateSpeed = rBuf.Get_Byte
   Sfx = rBuf.Get_Byte
   Damage = rBuf.Get_Integer
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(AttackerIndex) Then Exit Sub
   If Not Engine_ValidChar(TargetIndex) Then Exit Sub
   
   'Rotate the AttackerIndex to face TargetIndex
   NewHeading = Engine_FindDirection(CharList(AttackerIndex).Pos, CharList(TargetIndex).Pos)
   CharList(AttackerIndex).HeadHeading = NewHeading
   CharList(AttackerIndex).Heading = NewHeading
   
   'Create the projectile
   Engine_Projectile_Create AttackerIndex, TargetIndex, GrhIndex, RotateSpeed
   'Play the sound
   Sound_Play3D Sfx, CharList(AttackerIndex).Pos.X, CharList(AttackerIndex).Pos.Y
   
   'Create the blood (if damage)
   If Damage > 0 Then Engine_Blood_Create CharList(TargetIndex).Pos.X, CharList(TargetIndex).Pos.Y
   'Start the attack animation
   CharList(AttackerIndex).Body.Attack(CharList(AttackerIndex).Heading).Started = 1
   CharList(AttackerIndex).Body.Attack(CharList(AttackerIndex).Heading).FrameCounter = 1
   CharList(AttackerIndex).Body.Attack(CharList(AttackerIndex).Heading).LastCount = timeGetTime
   CharList(AttackerIndex).Weapon.Attack(CharList(AttackerIndex).Heading).FrameCounter = 1
   CharList(AttackerIndex).ActionIndex = 2
   'Create the damage
   Engine_Damage_Create CharList(TargetIndex).Pos.X, CharList(TargetIndex).Pos.Y, Damage
   
   'Aggressive face
   If Damage > 0 Then
       CharList(TargetIndex).Aggressive = 1
       CharList(TargetIndex).AggressiveCounter = timeGetTime + AGGRESSIVEFACETIME
   End If

End Sub

Sub Data_Combo_SoundRotateDamage(ByRef rBuf As DataBuffer) '************************************************************ 'Combines sound playing, damage and rotation packets together '<AttackerIndex(I)><TargetIndex(I)><Sfx(B)><Damage(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Combo_SoundRotateDamage '************************************************************ Dim AttackerIndex As Integer Dim TargetIndex As Integer Dim Damage As Integer Dim Sfx As Byte Dim NewHeading As Byte

   AttackerIndex = rBuf.Get_Integer
   TargetIndex = rBuf.Get_Integer
   Sfx = rBuf.Get_Byte
   Damage = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(AttackerIndex) Then Exit Sub
   If Not Engine_ValidChar(TargetIndex) Then Exit Sub
   'Rotate the AttackerIndex to face TargetIndex
   NewHeading = Engine_FindDirection(CharList(AttackerIndex).Pos, CharList(TargetIndex).Pos)
   CharList(AttackerIndex).HeadHeading = NewHeading
   CharList(AttackerIndex).Heading = NewHeading
   
   'Play the sound
   Sound_Play3D Sfx, CharList(AttackerIndex).Pos.X, CharList(AttackerIndex).Pos.Y
   
   'Start the attack animation
   CharList(AttackerIndex).Body.Attack(CharList(AttackerIndex).Heading).Started = 1
   CharList(AttackerIndex).Body.Attack(CharList(AttackerIndex).Heading).FrameCounter = 1
   CharList(AttackerIndex).Body.Attack(CharList(AttackerIndex).Heading).LastCount = timeGetTime
   CharList(AttackerIndex).Weapon.Attack(CharList(AttackerIndex).Heading).FrameCounter = 1
   CharList(AttackerIndex).ActionIndex = 2
   
   'Create the blood (if damage)
   If Damage > 0 Then Engine_Blood_Create CharList(TargetIndex).Pos.X, CharList(TargetIndex).Pos.Y
   'Create the damage
   Engine_Damage_Create CharList(TargetIndex).Pos.X, CharList(TargetIndex).Pos.Y, Damage
   
   'Aggressive face
   If Damage > 0 Then
       CharList(TargetIndex).Aggressive = 1
       CharList(TargetIndex).AggressiveCounter = timeGetTime + AGGRESSIVEFACETIME
   End If

End Sub

Sub Data_User_Attack(ByRef rBuf As DataBuffer) '************************************************************ 'Change character animation to attack animation '<CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Attack '************************************************************ Dim CharIndex As Integer

   CharIndex = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   'Start the attack animation
   CharList(CharIndex).Body.Attack(CharList(CharIndex).Heading).Started = 1
   CharList(CharIndex).Body.Attack(CharList(CharIndex).Heading).FrameCounter = 1
   CharList(CharIndex).Body.Attack(CharList(CharIndex).Heading).LastCount = timeGetTime
   CharList(CharIndex).Weapon.Attack(CharList(CharIndex).Heading).FrameCounter = 1
   CharList(CharIndex).ActionIndex = 2

End Sub

Sub Data_User_BaseStat(ByRef rBuf As DataBuffer) '************************************************************ 'Update base stat '<StatID(B)><Value(L)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_BaseStat '************************************************************ Dim StatID As Byte

   StatID = rBuf.Get_Byte
   BaseStats(StatID) = rBuf.Get_Long

End Sub

Sub Data_User_Blink(ByRef rBuf As DataBuffer) '************************************************************ 'Make a character blink '<CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Blink '************************************************************ Dim CharIndex As Integer

   CharIndex = rBuf.Get_Integer
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   CharList(CharIndex).StartBlinkTimer = 0
   CharList(CharIndex).BlinkTimer = 0

End Sub

Sub Data_User_CastSkill(ByRef rBuf As DataBuffer) '************************************************************ 'User casted a skill '<SkillID(B)> (Rest depends on the SkillID) 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_CastSkill '************************************************************ Dim CasterIndex As Integer Dim TargetIndex As Integer Dim TempIndex As Integer Dim SkillID As Byte Dim X As Long Dim Y As Long

   SkillID = rBuf.Get_Byte
   
   Select Case SkillID
   Case SkID.Heal
   
       CasterIndex = rBuf.Get_Integer
       TargetIndex = rBuf.Get_Integer
       If Not Engine_ValidChar(CasterIndex) Then Exit Sub
       If Not Engine_ValidChar(TargetIndex) Then Exit Sub
       'Set the position
       X = Engine_TPtoSPX(CharList(CasterIndex).Pos.X) + 16
       Y = Engine_TPtoSPY(CharList(CasterIndex).Pos.Y)
       'If not casted on self, bind to character
       If TargetIndex <> CasterIndex Then
           TempIndex = Effect_Heal_Begin(X, Y, 3, 120, 1)
           Effect(TempIndex).BindToChar = TargetIndex
           Effect(TempIndex).BindSpeed = 7
       Else
           TempIndex = Effect_Heal_Begin(X, Y, 3, 120, 0)
       End If
   Case SkID.Protection
   
       CasterIndex = rBuf.Get_Integer
       TargetIndex = rBuf.Get_Integer
       If Not Engine_ValidChar(CasterIndex) Then Exit Sub
       If Not Engine_ValidChar(TargetIndex) Then Exit Sub
       'Create the effect at (not bound to) the target character
       X = Engine_TPtoSPX(CharList(TargetIndex).Pos.X) + 16
       Y = Engine_TPtoSPY(CharList(TargetIndex).Pos.Y)
       TempIndex = Effect_Protection_Begin(X, Y, 11, 120, 40, 15)
       Effect(TempIndex).BindToChar = TargetIndex
       Effect(TempIndex).BindSpeed = 25
   Case SkID.Strengthen
   
       CasterIndex = rBuf.Get_Integer
       TargetIndex = rBuf.Get_Integer
       If Not Engine_ValidChar(CasterIndex) Then Exit Sub
       If Not Engine_ValidChar(TargetIndex) Then Exit Sub
       'Create the effect at (not bound to) the target character
       X = Engine_TPtoSPX(CharList(TargetIndex).Pos.X) + 16
       Y = Engine_TPtoSPY(CharList(TargetIndex).Pos.Y)
       TempIndex = Effect_Strengthen_Begin(X, Y, 12, 120, 40, 15)
       Effect(TempIndex).BindToChar = TargetIndex
       Effect(TempIndex).BindSpeed = 25
   Case SkID.Bless
   
       CasterIndex = rBuf.Get_Integer
       TargetIndex = rBuf.Get_Integer
       If Not Engine_ValidChar(CasterIndex) Then Exit Sub
       If Not Engine_ValidChar(TargetIndex) Then Exit Sub
       'Create the effect
       X = Engine_TPtoSPX(CharList(TargetIndex).Pos.X) + 16
       Y = Engine_TPtoSPY(CharList(TargetIndex).Pos.Y)
       TempIndex = Effect_Bless_Begin(X, Y, 3, 120, 40, 15)
       Effect(TempIndex).BindToChar = TargetIndex
       Effect(TempIndex).BindSpeed = 25
       
   Case SkID.SummonBandit
   
       TargetIndex = rBuf.Get_Integer
       If Not Engine_ValidChar(TargetIndex) Then Exit Sub
       X = Engine_TPtoSPX(CharList(TargetIndex).Pos.X) + 16
       Y = Engine_TPtoSPY(CharList(TargetIndex).Pos.Y)
       
       'Create the effect
       TempIndex = Effect_Summon_Begin(X, Y, 1, 500, 0)
       Effect(TempIndex).BindToChar = TargetIndex
       Effect(TempIndex).BindSpeed = 25
   Case SkID.SpikeField
   
       CasterIndex = rBuf.Get_Integer
       If Not Engine_ValidChar(CasterIndex) Then Exit Sub
       'Create the spike field depending on the direction the user is facing
       X = CharList(CasterIndex).Pos.X
       Y = CharList(CasterIndex).Pos.Y
       If CharList(CasterIndex).HeadHeading = NORTH Or CharList(CasterIndex).HeadHeading = NORTHEAST Then
           Engine_Effect_Create X - 1, Y + 1, 59
           Engine_Effect_Create X, Y + 1, 59
           Engine_Effect_Create X + 1, Y + 1, 59
           Engine_Effect_Create X - 2, Y, 59, , , , 0.5
           Engine_Effect_Create X - 1, Y, 59, , , , 0.5
           Engine_Effect_Create X, Y, 59, , , , 0.5
           Engine_Effect_Create X + 1, Y, 59, , , , 0.5
           Engine_Effect_Create X + 2, Y, 59, , , , 0.5
           Engine_Effect_Create X - 2, Y - 1, 59, , , , 1
           Engine_Effect_Create X - 1, Y - 1, 59, , , , 1
           Engine_Effect_Create X, Y - 1, 59, , , , 1
           Engine_Effect_Create X + 1, Y - 1, 59, , , , 1
           Engine_Effect_Create X + 2, Y - 1, 59, , , , 1
           Engine_Effect_Create X - 2, Y - 2, 59, , , , 1.5
           Engine_Effect_Create X - 1, Y - 2, 59, , , , 1.5
           Engine_Effect_Create X, Y - 2, 59, , , , 1.5
           Engine_Effect_Create X + 1, Y - 2, 59, , , , 1.5
           Engine_Effect_Create X + 2, Y - 2, 59, , , , 1.5
           Engine_Effect_Create X - 1, Y - 3, 59, , , , 2
           Engine_Effect_Create X, Y - 3, 59, , , , 2
           Engine_Effect_Create X + 1, Y - 3, 59, , , , 2
           Engine_Effect_Create X, Y - 4, 59, , , , 2.5
       ElseIf CharList(CasterIndex).HeadHeading = EAST Or CharList(CasterIndex).HeadHeading = SOUTHEAST Then
           Engine_Effect_Create X - 1, Y - 1, 59
           Engine_Effect_Create X - 1, Y, 59
           Engine_Effect_Create X - 1, Y + 1, 59
           Engine_Effect_Create X, Y - 2, 59, , , , 0.5
           Engine_Effect_Create X, Y - 1, 59, , , , 0.5
           Engine_Effect_Create X, Y, 59, , , , 0.5
           Engine_Effect_Create X, Y + 1, 59, , , , 0.5
           Engine_Effect_Create X, Y + 2, 59, , , , 0.5
           Engine_Effect_Create X + 1, Y - 2, 59, , , , 1
           Engine_Effect_Create X + 1, Y - 1, 59, , , , 1
           Engine_Effect_Create X + 1, Y, 59, , , , 1
           Engine_Effect_Create X + 1, Y + 1, 59, , , , 1
           Engine_Effect_Create X + 1, Y + 2, 59, , , , 1
           Engine_Effect_Create X + 2, Y - 2, 59, , , , 1.5
           Engine_Effect_Create X + 2, Y - 1, 59, , , , 1.5
           Engine_Effect_Create X + 2, Y, 59, , , , 1.5
           Engine_Effect_Create X + 2, Y + 1, 59, , , , 1.5
           Engine_Effect_Create X + 2, Y + 2, 59, , , , 1.5
           Engine_Effect_Create X + 3, Y - 1, 59, , , , 2
           Engine_Effect_Create X + 3, Y, 59, , , , 2
           Engine_Effect_Create X + 3, Y + 1, 59, , , , 2
           Engine_Effect_Create X + 4, Y, 59, , , , 2.5
       ElseIf CharList(CasterIndex).HeadHeading = SOUTH Or CharList(CasterIndex).HeadHeading = SOUTHWEST Then
           Engine_Effect_Create X - 1, Y - 1, 59
           Engine_Effect_Create X, Y - 1, 59
           Engine_Effect_Create X + 1, Y - 1, 59
           Engine_Effect_Create X - 2, Y, 59, , , , 0.5
           Engine_Effect_Create X - 1, Y, 59, , , , 0.5
           Engine_Effect_Create X, Y, 59, , , , 0.5
           Engine_Effect_Create X + 1, Y, 59, , , , 0.5
           Engine_Effect_Create X + 2, Y, 59, , , , 0.5
           Engine_Effect_Create X - 2, Y + 1, 59, , , , 1
           Engine_Effect_Create X - 1, Y + 1, 59, , , , 1
           Engine_Effect_Create X, Y + 1, 59, , , , 1
           Engine_Effect_Create X + 1, Y + 1, 59, , , , 1
           Engine_Effect_Create X + 2, Y + 1, 59, , , , 1
           Engine_Effect_Create X - 2, Y + 2, 59, , , , 1.5
           Engine_Effect_Create X - 1, Y + 2, 59, , , , 1.5
           Engine_Effect_Create X, Y + 2, 59, , , , 1.5
           Engine_Effect_Create X + 1, Y + 2, 59, , , , 1.5
           Engine_Effect_Create X + 2, Y + 2, 59, , , , 1.5
           Engine_Effect_Create X - 1, Y + 3, 59, , , , 2
           Engine_Effect_Create X, Y + 3, 59, , , , 2
           Engine_Effect_Create X + 1, Y + 3, 59, , , , 2
           Engine_Effect_Create X, Y + 4, 59, , , , 2.5
       ElseIf CharList(CasterIndex).HeadHeading = WEST Or CharList(CasterIndex).HeadHeading = NORTHWEST Then
           Engine_Effect_Create X + 1, Y - 1, 59
           Engine_Effect_Create X + 1, Y, 59
           Engine_Effect_Create X + 1, Y + 1, 59
           Engine_Effect_Create X, Y - 2, 59, , , , 0.5
           Engine_Effect_Create X, Y - 1, 59, , , , 0.5
           Engine_Effect_Create X, Y, 59, , , , 0.5
           Engine_Effect_Create X, Y + 1, 59, , , , 0.5
           Engine_Effect_Create X, Y + 2, 59, , , , 0.5
           Engine_Effect_Create X - 1, Y - 2, 59, , , , 1
           Engine_Effect_Create X - 1, Y - 1, 59, , , , 1
           Engine_Effect_Create X - 1, Y, 59, , , , 1
           Engine_Effect_Create X - 1, Y + 1, 59, , , , 1
           Engine_Effect_Create X - 1, Y + 2, 59, , , , 1
           Engine_Effect_Create X - 2, Y - 2, 59, , , , 1.5
           Engine_Effect_Create X - 2, Y - 1, 59, , , , 1.5
           Engine_Effect_Create X - 2, Y, 59, , , , 1.5
           Engine_Effect_Create X - 2, Y + 1, 59, , , , 1.5
           Engine_Effect_Create X - 2, Y + 2, 59, , , , 1.5
           Engine_Effect_Create X - 3, Y - 1, 59, , , , 2
           Engine_Effect_Create X - 3, Y, 59, , , , 2
           Engine_Effect_Create X - 3, Y + 1, 59, , , , 2
           Engine_Effect_Create X - 4, Y, 59, , , , 2.5
       End If
   End Select

End Sub

Sub Data_Server_MakeEffect(ByRef rBuf As DataBuffer) '************************************************************ 'Create an effect on the effects layer '<X(B)><Y(B)><GrhIndex(L)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MakeEffect '************************************************************ Dim X As Byte Dim Y As Byte Dim GrhIndex As Long

   'Get the values
   X = rBuf.Get_Byte
   Y = rBuf.Get_Byte
   GrhIndex = rBuf.Get_Long
   'Create the effect
   Engine_Effect_Create X, Y, GrhIndex, 0, 0, 1
   

End Sub

Sub Data_Server_MakeSlash(ByRef rBuf As DataBuffer) '************************************************************ 'Create a slash effect on the effects layer '<CharIndex(I)><GrhIndex(L)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_MakeSlash '************************************************************ Dim CharIndex As Integer Dim GrhIndex As Long Dim Angle As Single

   'Get the values
   CharIndex = rBuf.Get_Integer
   GrhIndex = rBuf.Get_Long
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   'Get the new heading
   Select Case CharList(CharIndex).Heading
       Case NORTH
           Angle = 0
       Case NORTHEAST
           Angle = 45
       Case EAST
           Angle = 90
       Case SOUTHEAST
           Angle = 135
       Case SOUTH
           Angle = 180
       Case SOUTHWEST
           Angle = 225
       Case WEST
           Angle = 270
       Case NORTHWEST
           Angle = 315
   End Select
   'Create the effect
   Engine_Effect_Create CharList(CharIndex).Pos.X, CharList(CharIndex).Pos.Y, GrhIndex, Angle, 150, 0
   

End Sub

Sub Data_User_Emote(ByRef rBuf As DataBuffer) '************************************************************ 'A character uses an emoticon '<EmoticonIndex(B)><CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Emote '************************************************************ Dim EmoticonIndex As Byte Dim CharIndex As Integer

   EmoticonIndex = rBuf.Get_Byte
   CharIndex = rBuf.Get_Integer
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   'Reset the fade value
   CharList(CharIndex).EmoFade = 0
   CharList(CharIndex).EmoDir = 1
   'Set the user's emoticon Grh by the emoticon index
   'Grh values are pulled directly from Grh1.raw - refer to that file
   Select Case EmoticonIndex
   Case EmoID.Dots: Engine_Init_Grh CharList(CharIndex).Emoticon, 78
   Case EmoID.Exclimation: Engine_Init_Grh CharList(CharIndex).Emoticon, 81
   Case EmoID.Question: Engine_Init_Grh CharList(CharIndex).Emoticon, 84
   Case EmoID.Surprised: Engine_Init_Grh CharList(CharIndex).Emoticon, 87
   Case EmoID.Heart: Engine_Init_Grh CharList(CharIndex).Emoticon, 90
   Case EmoID.Hearts: Engine_Init_Grh CharList(CharIndex).Emoticon, 93
   Case EmoID.HeartBroken: Engine_Init_Grh CharList(CharIndex).Emoticon, 96
   Case EmoID.Utensils: Engine_Init_Grh CharList(CharIndex).Emoticon, 99
   Case EmoID.Meat: Engine_Init_Grh CharList(CharIndex).Emoticon, 102
   Case EmoID.ExcliQuestion: Engine_Init_Grh CharList(CharIndex).Emoticon, 105
   End Select

End Sub

Sub Data_User_KnownSkills(ByRef rBuf As DataBuffer) '************************************************************ 'Retrieve known skills list '<KnowSkillList()(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_KnownSkills '************************************************************ Dim KnowSkillList() As Long 'Note that each byte holds 8 skills Dim Index As Long 'Which KnowSkillList array index to use Dim X As Byte Dim Y As Byte Dim i As Byte

   'Retrieve the skill list
   ReDim KnowSkillList(1 To NumBytesForSkills)
   For i = 1 To NumBytesForSkills
       KnowSkillList(i) = rBuf.Get_Byte
   Next i
   
   'Clear the skill list size
   SkillListSize = 0
   'Set the values
   For i = 1 To NumSkills
       
       'Find the index to use
       Index = Int((i - 1) / 8) + 1
   
       'Check if the skill is known
       If KnowSkillList(Index) And (2 ^ (i - ((Index - 1) * 8) - 1)) Then
           'Update the SkillList position and size
           SkillListSize = SkillListSize + 1
           ReDim Preserve SkillList(1 To SkillListSize)
           'Set that the user knows the skill
           UserKnowSkill(i) = 1
           'Update position for skill list
           X = X + 1
           If X > SkillListWidth Then
               X = 1
               Y = Y + 1
           End If
           'Set the skill list ID and Position
           SkillList(SkillListSize).SkillID = i
           SkillList(SkillListSize).X = SkillListX - (X * 32)
           SkillList(SkillListSize).Y = SkillListY - (Y * 32)
       Else
       
           'User does not know the skill
           UserKnowSkill(i) = 0
           
       End If
   Next i

End Sub

Sub Data_User_LookLeft(ByRef rBuf As DataBuffer) '************************************************************ 'Make a character look to the specified direction (Used for LookLeft and LookRight) '<CharIndex(I)><Heading(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_LookLeft '************************************************************ Dim CharIndex As Integer Dim Heading As Byte

   CharIndex = rBuf.Get_Integer
   Heading = rBuf.Get_Byte
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   CharList(CharIndex).HeadHeading = Heading

End Sub

Sub Data_User_ModStat(ByRef rBuf As DataBuffer) '************************************************************ 'Update mod stat '<StatID(B)><Value(L)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_ModStat '************************************************************ Dim StatID As Byte

   StatID = rBuf.Get_Byte
   ModStats(StatID) = rBuf.Get_Long

End Sub

Sub Data_User_Rotate(ByRef rBuf As DataBuffer) '************************************************************ 'Rotate a character by their CharIndex - works like it does in ' ChangeChar, but used to save ourselves a little bandwidth :) '<CharIndex(I)><Heading(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Rotate '************************************************************ Dim Heading As Byte Dim CharIndex As Integer

   CharIndex = rBuf.Get_Integer
   Heading = rBuf.Get_Byte
   
   'If the char doesn't exist, request to create it
   If Not Engine_ValidChar(CharIndex) Then Exit Sub
   
   CharList(CharIndex).Heading = Heading
   CharList(CharIndex).HeadHeading = CharList(CharIndex).Heading

End Sub

Sub Data_User_SetInventorySlot(ByRef rBuf As DataBuffer) '************************************************************ 'Set an inventory slot's information 'The information in the () is only sent if the ObjIndex <> 0 '<Slot(B)><OBJIndex(L)>(<OBJName(S)><OBJAmount(L)><Equipted(B)><GrhIndex(L)>) 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_SetInventorySlot '************************************************************ Dim Slot As Byte

   'Get the slot
   Slot = rBuf.Get_Byte
   'Start gathering the data
   UserInventory(Slot).ObjIndex = rBuf.Get_Long
   
   'If the object index = 0, then we are deleting a slot, so the rest is null
   If UserInventory(Slot).ObjIndex = 0 Then
       UserInventory(Slot).Name = "(None)"
       UserInventory(Slot).Amount = 0
       UserInventory(Slot).Equipped = 0
       UserInventory(Slot).GrhIndex = 0
       UserInventory(Slot).Value = 0
   Else
       'Index <> 0, so we have to get the information
       UserInventory(Slot).Name = rBuf.Get_String
       UserInventory(Slot).Amount = rBuf.Get_Long
       UserInventory(Slot).Equipped = rBuf.Get_Byte
       UserInventory(Slot).GrhIndex = rBuf.Get_Long
       UserInventory(Slot).Value = rBuf.Get_Long
   End If

End Sub

Sub Data_User_Target(ByRef rBuf As DataBuffer) '************************************************************ 'User targets a character '<CharIndex(I)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Target '************************************************************

   TargetCharIndex = rBuf.Get_Integer
   
   'Check for a valid UserCharIndex
   If UserCharIndex <= 0 Or UserCharIndex > LastChar Then
   
       'We have an invalid user char index, so we must have the wrong one - request an update on the right one
       sndBuf.Put_Byte DataCode.User_RequestUserCharIndex
       Exit Sub
       
   End If
   
   'Check if the path to the targeted character is valid (if any)
   If TargetCharIndex > 0 Then ClearPathToTarget = Engine_ClearPath(CharList(UserCharIndex).Pos.X, CharList(UserCharIndex).Pos.Y, CharList(TargetCharIndex).Pos.X, CharList(TargetCharIndex).Pos.Y)

End Sub

Sub Data_User_ChangeServer(ByRef rBuf As DataBuffer) '************************************************************ 'Changes a user to a different server '<Port(I)><IP(S)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_ChangeServer '************************************************************ Dim Port As Integer Dim IP As String

   'Get the values
   Port = rBuf.Get_Integer
   IP = rBuf.Get_String
   'Clean out the socket so we can make a fresh new connection
   If SocketOpen = 1 Then
       SocketOpen = 0
       frmMain.GOREsock.Shut SoxID
   End If
   
   'Set the variables to move to the new server
   SocketMoveToIP = IP
   SocketMoveToPort = Port
   
   'Clear the map
   CurMap = 0

End Sub

Sub Data_User_Trade_StartNPCTrade(ByRef rBuf As DataBuffer) '************************************************************ 'Start trading with a NPC '<NPCName(S)><NumVendItems(I)> Loop: <GrhIndex(L)><Name(S)><Price(L)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Trade_StartNPCTrade '************************************************************ Dim NPCName As String Dim NumItems As Integer Dim Item As Integer

   NPCName = rBuf.Get_String
   NumItems = rBuf.Get_Integer
   ReDim NPCTradeItems(1 To NumItems)
   NPCTradeItemArraySize = NumItems
   For Item = 1 To NumItems
       NPCTradeItems(Item).GrhIndex = rBuf.Get_Long
       NPCTradeItems(Item).Name = rBuf.Get_String
       NPCTradeItems(Item).Value = rBuf.Get_Long
   Next Item
   ShowGameWindow(ShopWindow) = 1
   LastClickedWindow = ShopWindow

End Sub

Sub Data_User_Trade_Accept(ByRef rBuf As DataBuffer) '************************************************************ 'One of the users of the trade has pressed the accept button '<UserTableIndex(B)> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Trade_Accept '************************************************************ Dim UserTableIndex As Byte

   UserTableIndex = rBuf.Get_Byte
   
   'Find which name to high-light
   If UserTableIndex = 1 Then
       If TradeTable.MyIndex = 1 Then TradeTable.User1Accepted = 1 Else TradeTable.User2Accepted = 1
   Else
       If TradeTable.MyIndex = 2 Then TradeTable.User1Accepted = 1 Else TradeTable.User2Accepted = 1
   End If

End Sub

Sub Data_User_Trade_Cancel() '************************************************************ 'Trade table was closed or canceled '<> 'More info: http://www.vbgore.com/GameClient.TCP.Data_User_Trade_Cancel '************************************************************ Dim i As Long

   ShowGameWindow(TradeWindow) = 0
   If LastClickedWindow = TradeWindow Then LastClickedWindow = 0
   For i = 1 To 9
       TradeTable.Trade1(i).Amount = 0
       TradeTable.Trade1(i).Grh = 0
       TradeTable.Trade1(i).Name = vbNullString
       TradeTable.Trade1(i).Value = 0
       TradeTable.Trade2(i).Amount = 0
       TradeTable.Trade2(i).Grh = 0
       TradeTable.Trade2(i).Name = vbNullString
       TradeTable.Trade2(i).Value = 0
   Next i
   TradeTable.Gold1 = 0
   TradeTable.Gold2 = 0
   TradeTable.User1Accepted = 0
   TradeTable.User2Accepted = 0
   TradeTable.User1Name = vbNullString
   TradeTable.User2Name = vbNullString
   TradeTable.MyIndex = 0

End Sub

Sub Data_Server_SendQuestInfo(ByRef rBuf As DataBuffer) '************************************************************ 'Server sent the information on a quest '<QuestID(B)><Name(S)>(<Description(S-EX)>) 'More info: http://www.vbgore.com/GameClient.TCP.Data_Server_SendQuestInfo '************************************************************ Dim QuestID As Byte Dim Name As String Dim Desc As String Dim i As Long Dim Changed As Byte

   'Get the variables
   QuestID = rBuf.Get_Byte
   Name = rBuf.Get_String
   If LenB(Name) <> 0 Then Desc = rBuf.Get_StringEX    'Only get the desc if the name exists
   'Resize the questinfo array if needed
   If QuestID > QuestInfoUBound Then
       QuestInfoUBound = QuestID
       ReDim Preserve QuestInfo(1 To QuestInfoUBound)
   End If
   
   'Store the information
   QuestInfo(QuestID).Name = Name
   QuestInfo(QuestID).Desc = Desc
   'Loop through the quests, remove any unused slots on the end
   If QuestInfoUBound > 1 Then
       For i = QuestInfoUBound To 1 Step -1
           If QuestInfo(i).Name = vbNullString Then
               QuestInfoUBound = QuestInfoUBound - 1
               Changed = 1
           Else
               'Exit on the first section of information
               Exit For
           End If
       Next i
       If Changed Then
           If QuestInfoUBound > 0 Then
               ReDim Preserve QuestInfo(1 To QuestInfoUBound)
           Else
               Erase QuestInfo
           End If
       End If
   Else
       If QuestInfo(1).Name = vbNullString Then
           Erase QuestInfo
           QuestInfoUBound = 0
       End If
   End If
   

End Sub </vb>

TileEngine

<vb> Option Explicit

Public Const ShadowColor As Long = 1677721600 'ARGB 100/0/0/0 Public Const HealthColor As Long = -1761673216 'ARGB 150/255/0/0 Public Const ManaColor As Long = -1778384641 'ARGB 150/0/0/255

Public ParticleOffsetX As Long Public ParticleOffsetY As Long Public LastOffsetX As Integer 'The last offset values stored, used to get the offset difference Public LastOffsetY As Integer ' so the particle engine can adjust weather particles accordingly

Public EnterText As Boolean 'If the text buffer is used (the user is typing a message) Public EnterTextBuffer As String 'The text in the text buffer Public EnterTextBufferWidth As Long 'Width of the text buffer

Public AlternateRender As Byte Public AlternateRenderDefault As Byte Public AlternateRenderMap As Byte Public AlternateRenderText As Byte

'Describes a transformable lit vertex Private Const FVF As Long = D3DFVF_XYZRHW Or D3DFVF_TEX1 Or D3DFVF_DIFFUSE Public Type TLVERTEX

   X As Single
   Y As Single
   Z As Single
   Rhw As Single
   Color As Long
   tU As Single
   tV As Single

End Type

'The size of a FVF vertex Public Const FVF_Size As Long = 28

'********** CONSTANTS *********** 'Keep window in the game screen - dont let them move outside of the window bounds Public Const WindowsInScreen As Boolean = True

'Screen resolution and information (resolution must be identical to the values on the server!) Public ReverseSound As Integer 'Reverse the left and right speakers Public TextureCompress As Long 'Compress textures, saving lots of RAM at an insignifcant CPU usage cost (may reduce graphic quality!) Public DisableChatBubbles As Byte 'If chat bubbles are drawn or not - chat bubbles can be a huge FPS drainer Public Bit32 As Byte 'If 32-bit format is used (0 = 16-bit) Public UseVSync As Byte 'If vertical synchronization copy is used Public Windowed As Boolean 'If the screen is windowed or fullscreen Public Const ScreenWidth As Long = 800 'Keep this identical to the value on the server! Public Const ScreenHeight As Long = 600 'Keep this identical to the value on the server! Private Const BufferWidth As Long = 1024 'If ScreenWidth is <= 1024, this will = 1024, else set it as 2048 Private Const BufferHeight As Long = 1024 'Same as the BufferWidth, but with the ScreenHeight

'Heading constants Public Const NORTH As Byte = 1 Public Const EAST As Byte = 2 Public Const SOUTH As Byte = 3 Public Const WEST As Byte = 4 Public Const NORTHEAST As Byte = 5 Public Const SOUTHEAST As Byte = 6 Public Const SOUTHWEST As Byte = 7 Public Const NORTHWEST As Byte = 8

'Font colors Public Const FontColor_Talk As Long = -1 Public Const FontColor_Info As Long = -16711936 Public Const FontColor_Fight As Long = -65536 Public Const FontColor_Quest As Long = -256 Public Const FontColor_Group As Long = -16711681 Private Const ChatTextBufferSize As Integer = 200 Public Const DamageDisplayTime As Integer = 2000 Public Const MouseSpeed As Single = 1.5

'********** MUSIC *********** Public Const Music_MaxVolume As Long = 100 Public Const Music_MaxBalance As Long = 100 Public Const Music_MaxSpeed As Long = 226 Public Const NumMusicBuffers As Long = 1 Public DirectShow_Event(1 To NumMusicBuffers) As IMediaEvent Public DirectShow_Control(1 To NumMusicBuffers) As IMediaControl Public DirectShow_Position(1 To NumMusicBuffers) As IMediaPosition Public DirectShow_Audio(1 To NumMusicBuffers) As IBasicAudio

'********** Custom Fonts ************

'Point API Public Type POINTAPI

   X As Long
   Y As Long

End Type

'vbGORE Font Header Private Type CharVA

   Vertex(0 To 3) As TLVERTEX

End Type Private Type VFH

   BitmapWidth As Long         'Size of the bitmap itself
   BitmapHeight As Long
   CellWidth As Long           'Size of the cells (area for each character)
   CellHeight As Long
   BaseCharOffset As Byte      'The character we start from
   CharWidth(0 To 255) As Byte 'The actual factual width of each character
   CharVA(0 To 255) As CharVA

End Type

Private Type CustomFont

   HeaderInfo As VFH           'Holds the header information
   Texture As Direct3DTexture8 'Holds the texture of the text
   RowPitch As Integer         'Number of characters per row
   RowFactor As Single         'Percentage of the texture width each character takes
   ColFactor As Single         'Percentage of the texture height each character takes
   CharHeight As Byte          'Height to use for the text - easiest to start with CellHeight value, and keep lowering until you get a good value
   TextureSize As POINTAPI     'Size of the texture

End Type

Public Const Font_Default_TextureNum As Long = -1 'The texture number used to represent this font - only used for AlternateRendering - keep negative to prevent interfering with game textures Public Font_Default As CustomFont 'Describes our custom font "default"

'********** TYPES ***********

'Text buffer Type ChatTextBuffer

   Text As String
   Color As Long

End Type

Private ChatTextBuffer(1 To ChatTextBufferSize) As ChatTextBuffer

'Holds a position on a 2d grid Public Type Position

   X As Long
   Y As Long

End Type

'Holds a position on a 2d grid in floating variables (singles) Public Type FloatPos

   X As Single
   Y As Single

End Type

'Holds a world position Private Type WorldPos

   X As Byte
   Y As Byte

End Type

'Holds data about where a png can be found, 'How big it is and animation info Public Type GrhData

   SX As Integer
   SY As Integer
   FileNum As Long
   pixelWidth As Integer
   pixelHeight As Integer
   TileWidth As Single
   TileHeight As Single
   NumFrames As Byte
   Frames() As Long
   Speed As Single

End Type

'Points to a grhData and keeps animation info Public Type Grh

   GrhIndex As Long
   LastCount As Long
   FrameCounter As Single
   Started As Byte

End Type

'Bodies list Public Type BodyData

   Walk(1 To 8) As Grh
   Attack(1 To 8) As Grh
   HeadOffset As Position

End Type

'Wings list Public Type WingData

   Walk(1 To 8) As Grh
   Attack(1 To 8) As Grh

End Type

'Weapons list Public Type WeaponData

   Walk(1 To 8) As Grh
   Attack(1 To 8) As Grh

End Type

'Heads list Public Type HeadData

   Head(1 To 8) As Grh
   Blink(1 To 8) As Grh
   AgrHead(1 To 8) As Grh
   AgrBlink(1 To 8) As Grh

End Type

'Hair list Public Type HairData

   Hair(1 To 8) As Grh

End Type

'Hold info about the character's status Public Type CharStatus

   Cursed As Byte
   WarCursed As Byte
   Blessed As Byte
   Protected As Byte
   Strengthened As Byte
   IronSkinned As Byte
   Exhausted As Byte

End Type

'Hold info about a character Public Type Char

   Active As Byte
   Heading As Byte
   HeadHeading As Byte
   CharType As Byte
   OwnerChar As Integer        'If CharType = Slave then this is the index of the owner (used for summoned NPCs to display on the mini-map)
   Pos As Position             'Tile position on the map
   RealPos As Position         'Position on the game screen
   Body As BodyData
   Head As HeadData
   Weapon As WeaponData
   Hair As HairData
   Wings As WingData
   Moving As Byte
   Speed As Byte
   Running As Byte
   Aggressive As Byte
   AggressiveCounter As Long
   MoveOffset As FloatPos
   BlinkTimer As Single        'The length of the actual blinking
   StartBlinkTimer As Single   'How long until a blink starts
   ScrollDirectionX As Integer
   ScrollDirectionY As Integer
   BubbleStr As String
   BubbleTime As Long
   Name As String
   NameOffset As Integer       'Used to acquire the center position for the name
   ActionIndex As Byte
   HealthPercent As Byte
   ManaPercent As Byte
   CharStatus As CharStatus
   Emoticon As Grh
   EmoFade As Single
   EmoDir As Byte      'Direction the fading is going - 0 = Stopped, 1 = Up, 2 = Down
   NPCChatIndex As Byte
   NPCChatLine As Byte
   NPCChatDelay As Long

End Type

'Holds info about each tile position Public Type MapBlock

   BlockedAttack As Byte
   Graphic(1 To 6) As Grh
   Light(1 To 24) As Long
   Shadow(1 To 6) As Byte
   Sign As Integer
   Blocked As Byte
   Warp As Byte
   Sfx As DirectSoundSecondaryBuffer8

End Type

'Hold info about each map Public Type MapInfo

   Name As String
   Weather As Byte
   Music As Byte
   Width As Byte
   Height As Byte

End Type

'Describes the return from a texture init Private Type D3DXIMAGE_INFO_A

   Width As Long
   Height As Long
   Depth As Long
   MipLevels As Long
   Format As CONST_D3DFORMAT
   ResourceType As CONST_D3DRESOURCETYPE
   ImageFileFormat As Long

End Type

'Describes a layer bound to tile position but not in the map array (to save memory) Private Type FloatSurface

   Pos As WorldPos
   Offset As Position
   Grh As Grh

End Type

'Describes the effects layer Private Type EffectSurface

   Pos As WorldPos
   Grh As Grh
   Angle As Single
   Time As Long
   Animated As Byte

End Type

'Describes the damage counters Public Type DamageTxt

   Pos As FloatPos
   Value As String
   Counter As Single
   Width As Integer

End Type

'********** Public VARS ***********

'User status vars Public CurMap As Integer 'Current map loaded Public UserMoving As Boolean Public UserPos As Position 'Holds current user pos Private AddtoUserPos As Position 'For moving user Public UserCharIndex As Integer Public EngineRun As Boolean Private FPS As Long Private FramesPerSecCounter As Long Private FPSLastCheck As Long Private SaveLastCheck As Long

'How many tiles the engine "looks ahead" when drawing the screen Public TileBufferSize As Integer Public TileBufferOffset As Long 'Used to calculate offset value in certain cases

'Main view size size in tiles Public Const WindowTileWidth As Integer = ScreenWidth \ 32 Public Const WindowTileHeight As Integer = ScreenHeight \ 32

'Tile size in pixels Public Const TilePixelHeight As Integer = 32 Public Const TilePixelWidth As Integer = 32

'Number of pixels the engine scrolls per frame. MUST divide evenly into pixels per tile Public Const ScrollPixelsPerFrameX As Integer = 4 Public Const ScrollPixelsPerFrameY As Integer = 4

'Totals Private NumBodies As Integer 'Number of bodies Private NumHeads As Integer 'Number of heads Private NumHairs As Integer 'Number of hairs Private NumWeapons As Integer 'Number of weapons Private NumGrhs As Long 'Number of grhs Private NumWings As Integer 'Number of wings Public NumSfx As Integer 'Number of sound effects Public NumGrhFiles As Integer 'Number of pngs Public LastChar As Integer 'Last character Public LastObj As Integer 'Last object Public LastBlood As Integer 'Last blood splatter index used Public LastEffect As Integer 'Last effect index used Public LastDamage As Integer 'Last damage counter text index used Public LastProjectile As Integer 'Last projectile index used

'Screen positioning Public minY As Integer 'Start Y pos on current screen + tilebuffer Public maxY As Integer 'End Y pos on current screen Public minX As Integer 'Start X pos on current screen Public maxX As Integer 'End X pos on current screen Public ScreenMinY As Integer 'Start Y pos on current screen Public ScreenMaxY As Integer 'End Y pos on current screen Public ScreenMinX As Integer 'Start X pos on current screen Public ScreenMaxX As Integer 'End X pos on current screen Public LastTileX As Integer Public LastTileY As Integer

'********** GAME WINDOWS *********** Public Const SkillListX As Integer = 750 'Position where the skill list where appear Public Const SkillListY As Integer = 525 ' (indicates the bottom-right corner) Public Const SkillListWidth As Integer = 5 'How many skills wide the skill popup list is Public Const GUIColorValue As Long = -1090519041 'ARGB 190/255/255/255

'Important: Windows are ordered by priority, where 1 = highest! Public Const AmountWindow As Byte = 1 Public Const MenuWindow As Byte = 2 Public Const NPCChatWindow As Byte = 3 Public Const TradeWindow As Byte = 4 Public Const WriteMessageWindow As Byte = 5 Public Const ViewMessageWindow As Byte = 6 Public Const MailboxWindow As Byte = 7 Public Const InventoryWindow As Byte = 8 Public Const ShopWindow As Byte = 9 Public Const BankWindow As Byte = 10 Public Const StatWindow As Byte = 11 Public Const ChatWindow As Byte = 12 Public Const QuickBarWindow As Byte = 13 Public Const NumGameWindows As Byte = 13

Public Const MaxMailObjs As Byte = 10

Public SelGameWindow As Byte 'The selected game window (mouse is down, not last-clicked) Public SelMessage As Byte 'The selected message in the mailbox Public LastClickedWindow As Byte 'The last game window to be clicked Public ShowGameWindow(1 To NumGameWindows) As Byte 'What game windows are visible Public MailboxListBuffer As String 'Holds the list of text for the mailbox Public AmountWindowValue As String 'How much of the item will be dropped from the amount window Public AmountWindowItemIndex As Byte 'Index of the item to be dropped/sold/sent when the amount window pops up Public AmountWindowUsage As Byte 'The usage combination for the amount window (as defined with below constants) Public DrawSkillList As Byte 'If the skills list is to be drawn Public QuickBarSetSlot As Byte 'What slot on the quickbar was clicked to be set Public DragSourceWindow As Byte 'The window the item was dragged from Public DragItemSlot As Byte 'Holds what slot an item is being dragged from in the inventory

'AmountWindowUsage constants Public Const AW_Drop As Byte = 0 Public Const AW_InvToShop As Byte = 2 Public Const AW_InvToBank As Byte = 3 Public Const AW_InvToMail As Byte = 4 Public Const AW_ShopToInv As Byte = 5 Public Const AW_BankToInv As Byte = 6 Public Const AW_InvToTrade As Byte = 7

Private Type QuickBarIDData

   Type As Byte    'Type of information in the quick bar (Item, Skill, etc)
   ID As Byte      'The ID of whatever is being held (Item = Inventory Slot, Skill = SkillID)

End Type Public QuickBarID(1 To 12) As QuickBarIDData Public Const QuickBarType_Skill As Byte = 1 Public Const QuickBarType_Item As Byte = 2

Private Type SkillListData

   SkillID As Byte
   X As Long
   Y As Long

End Type Public SkillList() As SkillListData Public SkillListSize As Byte

Private Type RMailData 'The mail data for the message being read

   Subject As String
   WriterName As String
   Message As String
   Obj(1 To MaxMailObjs) As Integer
   ObjName(1 To MaxMailObjs) As String
   ObjAmount(1 To MaxMailObjs) As Integer

End Type

Public ReadMailData As RMailData

Private Type WMailData 'The mail data for the message being written

   Subject As String
   RecieverName As String
   Message As String
   ObjIndex(1 To MaxMailObjs) As Integer
   ObjAmount(1 To MaxMailObjs) As Integer

End Type

Public WriteMailData As WMailData

Public Enum WriteMailSelectedControl

   wmFrom = 1
   wmSubject = 2
   wmMessage = 3

End Enum

  1. If False Then

Private From, Subject, Message

  1. End If

Public WMSelCon As WriteMailSelectedControl

Private Type Rectangle 'A normal little rectangle

   X As Integer
   Y As Integer
   Width As Integer
   Height As Integer

End Type

Private Type WindowMessage 'Write/Read message window

   Screen As Rectangle
   From As Rectangle
   Subject As Rectangle
   Message As Rectangle
   Image(1 To MaxMailObjs) As Rectangle
   SkinGrh As Grh

End Type

Private Type WindowQuickBar 'Quick bar window

   Screen As Rectangle
   Image(1 To 12) As Rectangle
   SkinGrh As Grh

End Type

Private Type WindowInventory 'User inventory window

   Screen As Rectangle
   Image(1 To MAX_INVENTORY_SLOTS) As Rectangle
   SkinGrh As Grh

End Type

Private Type WindowMailbox 'Mailbox window

   Screen As Rectangle
   WriteLbl As Rectangle
   DeleteLbl As Rectangle
   ReadLbl As Rectangle
   List As Rectangle
   SkinGrh As Grh

End Type

Private Type WindowAmount 'Amount window

   Screen As Rectangle
   Value As Rectangle
   SkinGrh As Grh

End Type

Private Type ChatWindow 'Chat buffer/input window

   Screen As Rectangle
   Text As Rectangle
   SkinGrh As Grh

End Type

Private Type WindowMenu

   Screen As Rectangle
   QuitLbl As Rectangle
   SkinGrh As Grh

End Type

Private Type StatWindow

   Screen As Rectangle
   AddStr As Rectangle
   AddAgi As Rectangle
   AddMag As Rectangle
   Str As Rectangle
   Agi As Rectangle
   Mag As Rectangle
   Points As Rectangle
   Dmg As Rectangle
   DEF As Rectangle
   Gold As Rectangle
   AddGrh As Grh
   SkinGrh As Grh

End Type

Private Type WindowNPCChat

   Screen As Rectangle
   NumAnswers As Byte
   Answer() As Rectangle
   SkinGrh As Grh

End Type

'Info about the trade window Public Type TradeWindow

   Screen As Rectangle
   User1Name As Rectangle
   User2Name As Rectangle
   Trade1(1 To 9) As Rectangle
   Trade2(1 To 9) As Rectangle
   Gold1 As Rectangle
   Gold2 As Rectangle
   Accept As Rectangle
   Trade As Rectangle
   Cancel As Rectangle
   SkinGrh As Grh

End Type

Public Type GameWindow 'List of all the different game windows

   QuickBar As WindowQuickBar
   Inventory As WindowInventory
   Shop As WindowInventory
   Mailbox As WindowMailbox
   ViewMessage As WindowMessage
   WriteMessage As WindowMessage
   Amount As WindowAmount
   Menu As WindowMenu
   ChatWindow As ChatWindow
   StatWindow As StatWindow
   Bank As WindowInventory
   NPCChat As WindowNPCChat
   Trade As TradeWindow

End Type

Public GameWindow As GameWindow

'********** Direct X *********** Public Const SurfaceTimerMax As Long = 300000 'How long a texture stays in memory unused (miliseconds) Public SurfaceDB() As Direct3DTexture8 'The list of all the textures Public SurfaceTimer() As Long 'How long until the surface unloads Public LastTexture As Long 'The last texture used Public D3DWindow As D3DPRESENT_PARAMETERS 'Describes the viewport and used to restore when in fullscreen Public UsedCreateFlags As CONST_D3DCREATEFLAGS 'The flags we used to create the device when it first succeeded Public DispMode As D3DDISPLAYMODE 'Describes the display mode

'Texture for particle effects - this is handled differently then the rest of the graphics Public ParticleTexture(1 To 12) As Direct3DTexture8

'DirectX 8 Objects Public DX As DirectX8 Public D3D As Direct3D8 Public D3DX As D3DX8 Public D3DDevice As Direct3DDevice8

'Used for alternate rendering only Private Sprite As D3DXSprite Private SpriteBegun As Byte Private SpriteScaleVector As D3DVECTOR2

'Motion-bluring information Public UseMotionBlur As Byte 'If motion blur is enabled or not Public BlurIntensity As Single Public BlurTexture As Direct3DTexture8 Public BlurSurf As Direct3DSurface8 Public BlurStencil As Direct3DSurface8 Public DeviceStencil As Direct3DSurface8 Public DeviceBuffer As Direct3DSurface8 Public BlurTA(0 To 3) As TLVERTEX

'Chat vertex buffer (only kept in memory if using alternate rendering) Private ChatVA() As TLVERTEX

'Chat vertex buffer information Private ChatArrayUbound As Long Private ChatVB As Direct3DVertexBuffer8

'Projectile information Public Type Projectile

   X As Single
   Y As Single
   tX As Single
   tY As Single
   RotateSpeed As Byte
   Rotate As Single
   Grh As Grh

End Type

'Texture information Public Type TexInfo

   X As Long
   Y As Long

End Type

'Used to hold the graphic layers in a quick-to-draw format Public Type Tile

   TileX As Byte
   TileY As Byte
   PixelPosX As Integer
   PixelPosY As Integer

End Type Public Type TileLayer

   Tile() As Tile
   NumTiles As Integer

End Type Public TileLayer(1 To 6) As TileLayer

'********** WEATHER *********** Public Type LightType

   Light(1 To 24) As Long

End Type Public SaveLightBuffer() As LightType Public WeatherEffectIndex As Integer 'Index returned by the weather effect initialization Public WeatherDoLightning As Byte 'Are we using lightning? >1 = Yes, 0 = No Public WeatherFogX1 As Single 'Fog 1 position Public WeatherFogY1 As Single 'Fog 1 position Public WeatherFogX2 As Single 'Fog 2 position Public WeatherFogY2 As Single 'Fog 2 position Public WeatherDoFog As Byte 'Are we using fog? >1 = Yes, 0 = No Public WeatherFogCount As Byte 'How many fog effects there are Public LightningTimer As Single 'How long until our next lightning bolt strikes Public FlashTimer As Single 'How long until the flash goes away (being > 0 states flash is happening) Public WeatherSfx1 As DirectSoundSecondaryBuffer8 'Weather buffers - dont add more unless you need more for Public WeatherSfx2 As DirectSoundSecondaryBuffer8 ' one weather effect (ie rain, wind, lightning)

'********** Public ARRAYS *********** Public GrhData() As GrhData 'Holds data for the graphic structure Public SurfaceSize() As TexInfo 'Holds the size of the surfaces for SurfaceDB() Public BodyData() As BodyData 'Holds data about body structure Public HeadData() As HeadData 'Holds data about head structure Public HairData() As HairData 'Holds data about hair structure Public WeaponData() As WeaponData 'Holds data about weapon structure Public WingData() As WingData 'Holds data about wing structure Public MapData() As MapBlock 'Holds map data for current map Public MapInfo As MapInfo 'Holds map info for current map Public CharList() As Char 'Holds info about all characters on the map Public OBJList() As FloatSurface 'Holds info about all objects on the map Public BloodList() As FloatSurface 'Holds info about all the active blood splatters Public EffectList() As EffectSurface 'Holds info about all the active effects of all types Public ProjectileList() As Projectile 'Holds info about all the active projectiles (arrows, ninja stars, bullets, etc) Public DamageList() As DamageTxt 'Holds info on the damage displays

'FPS Public EndTime As Long Public ElapsedTime As Single Public TickPerFrame As Single Public Const EngineBaseSpeed As Single = 0.011 Public OffsetCounterX As Single Public OffsetCounterY As Single

Private NotFirstRender As Boolean

Public ShownText As String

'Weather information Public LastWeather As Byte Public UseWeather As Byte

'Mini-map tiles Public Type MiniMapTile

   X As Single         'X and Y index of the tile (using the tile position, not pixel position)
   Y As Single
   Color As Long       'The color of the tile

End Type Public MiniMapVBSize As Long 'Size of the vertex buffer (number of verticies, or Tiles x 8) Public MiniMapVB As Direct3DVertexBuffer8 'Holds the information needed to render the mini-map (not including characters) Public ShowMiniMap As Byte

'********** OUTSIDE FUNCTIONS *********** Public Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As Long) As Integer

Sub Engine_MakeChatBubble(ByVal CharIndex As Integer, ByVal Text As String) '************************************************************ 'Adds text to a chat bubble 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_MakeChatBubble '************************************************************

   If DisableChatBubbles Then Exit Sub
   If LenB(Text) = 0 Then Exit Sub 'No text passed
   CharList(CharIndex).BubbleStr = Text
   CharList(CharIndex).BubbleTime = 5000
   

End Sub

Public Function Engine_TPtoSPX(ByVal X As Byte) As Long '************************************************************ 'Tile Position to Screen Position 'Takes the tile position and returns the pixel location on the screen 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_TPtoSPX '************************************************************

   Engine_TPtoSPX = Engine_PixelPosX(X - minX) + OffsetCounterX - 288 + TileBufferOffset

End Function

Public Function Engine_TPtoSPY(ByVal Y As Byte) As Long '************************************************************ 'Tile Position to Screen Position 'Takes the tile position and returns the pixel location on the screen 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_TPtoSPY '************************************************************

   Engine_TPtoSPY = Engine_PixelPosY(Y - minY) + OffsetCounterY - 288 + TileBufferOffset

End Function

Public Sub Engine_AddToChatTextBuffer(ByVal Text As String, ByVal Color As Long) '************************************************************ 'Adds text to the chat text buffer 'Buffer is order from bottom to top 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_AddToChatTextBuffer '************************************************************ Dim TempSplit() As String Dim TSLoop As Long Dim LastSpace As Long Dim Size As Long Dim i As Long Dim b As Long

   'Check if there are any line breaks - if so, we will support them
   TempSplit = Split(Text, vbCrLf)
   
   For TSLoop = 0 To UBound(TempSplit)
   
       'Clear the values for the new line
       Size = 0
       b = 1
       LastSpace = 1
       
       'Loop through all the characters
       For i = 1 To Len(TempSplit(TSLoop))
       
           'If it is a space, store it so we can easily break at it
           Select Case Mid$(TempSplit(TSLoop), i, 1)
               Case " ": LastSpace = i
               Case "_": LastSpace = i
               Case "-": LastSpace = i
           End Select
           
           'Add up the size - Do not count the "|" character (high-lighter)!
           If Not Mid$(TempSplit(TSLoop), i, 1) = "|" Then
               Size = Size + Font_Default.HeaderInfo.CharWidth(Asc(Mid$(TempSplit(TSLoop), i, 1)))
           End If
           
           'Check for too large of a size
           If Size > GameWindow.ChatWindow.Text.Width Then
               
               'Check if the last space was too far back
               If i - LastSpace > 10 Then
                   
                   'Too far away to the last space, so break at the last character
                   Engine_AddToChatTextBuffer2 Trim$(Mid$(TempSplit(TSLoop), b, (i - 1) - b)), Color
                   b = i - 1
                   Size = 0
               
               Else
               
                   'Break at the last space to preserve the word
                   Engine_AddToChatTextBuffer2 Trim$(Mid$(TempSplit(TSLoop), b, LastSpace - b)), Color
                   b = LastSpace + 1
                   
                   'Count all the words we ignored (the ones that weren't printed, but are before "i")
                   Size = Engine_GetTextWidth(Font_Default, Mid$(TempSplit(TSLoop), LastSpace, i - LastSpace))

               End If
               
           End If
           
           'This handles the remainder
           If i = Len(TempSplit(TSLoop)) Then
               If b <> i Then Engine_AddToChatTextBuffer2 Mid$(TempSplit(TSLoop), b, i), Color
           End If
           
       Next i
       
   Next TSLoop
   
   'Only update if we have set up the text (that way we can add to the buffer before it is even made)
   If Font_Default.RowPitch = 0 Then Exit Sub
   'Update the array
   Engine_UpdateChatArray

End Sub

Private Sub Engine_AddToChatTextBuffer2(ByVal Text As String, ByVal Color As Long) '************************************************************ 'Actually adds the text to the buffer 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_AddToChatTextBuffer2 '************************************************************ Dim LoopC As Long

   'Move all other text up
   For LoopC = (ChatTextBufferSize - 1) To 1 Step -1
       ChatTextBuffer(LoopC + 1) = ChatTextBuffer(LoopC)
   Next LoopC
   
   'Set the values
   ChatTextBuffer(1).Text = Text
   ChatTextBuffer(1).Color = Color

End Sub

Public Sub Engine_UpdateChatArray() '************************************************************ 'Update the array representing the text in the chat buffer 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_UpdateChatArray '************************************************************ Dim Chunk As Integer Dim Count As Integer Dim LoopC As Byte Dim Ascii As Byte Dim Row As Long Dim Pos As Long Dim u As Single Dim v As Single Dim X As Single Dim Y As Single Dim Y2 As Single Dim i As Long Dim j As Long Dim Size As Integer Dim KeyPhrase As Byte Dim ResetColor As Byte Dim TempColor As Long

   On Error Resume Next
   'Set the position
   If ChatBufferChunk <= 1 Then ChatBufferChunk = 1
   Chunk = 12
   
   'Get the number of characters in all the visible buffer
   Size = 0
   For LoopC = (Chunk * ChatBufferChunk) - 11 To Chunk * ChatBufferChunk
       If LoopC > ChatTextBufferSize Then Exit For
       Size = Size + Len(ChatTextBuffer(LoopC).Text)
       
       'Remove the "|"'s from the count
       For i = 1 To Size
           If Mid$(ChatTextBuffer(LoopC).Text, i, 1) = "|" Then j = j + 1
       Next i
       
   Next LoopC
   Size = Size - j
   ChatArrayUbound = Size * 6 - 1
   If ChatArrayUbound < 0 Then Exit Sub
   ReDim ChatVA(0 To ChatArrayUbound) 'Size our array to fix the 6 verticies of each character
   'Set the base position
   X = GameWindow.ChatWindow.Screen.X + GameWindow.ChatWindow.Text.X
   Y = GameWindow.ChatWindow.Screen.Y + GameWindow.ChatWindow.Text.X 'We assume the border is the same size on all sides
   'Loop through each buffer string
   For LoopC = (Chunk * ChatBufferChunk) - 11 To Chunk * ChatBufferChunk
       If LoopC > ChatTextBufferSize Then Exit For
       If ChatBufferChunk * Chunk > ChatTextBufferSize Then ChatBufferChunk = ChatBufferChunk - 1
       
       'Set the temp color
       TempColor = ChatTextBuffer(LoopC).Color
       
       'Set the Y position to be used
       Y2 = Y - (LoopC * 10) + (Chunk * ChatBufferChunk * 10)
       
       'Loop through each line if there are line breaks (vbCrLf)
       Count = 0   'Counts the offset value we are on
       If LenB(ChatTextBuffer(LoopC).Text) <> 0 Then  'Dont bother with empty strings
           
           'Loop through the characters
           For j = 1 To Len(ChatTextBuffer(LoopC).Text)
           
               'Convert the character to the ascii value
               Ascii = Asc(Mid$(ChatTextBuffer(LoopC).Text, j, 1))
               
               'Check for a key phrase
               If Ascii = 124 Then
                   KeyPhrase = (Not KeyPhrase)
                   If KeyPhrase Then TempColor = D3DColorARGB(255, 255, 0, 0) Else ResetColor = 1
               Else
               
                   'tU and tV value (basically tU = BitmapXPosition / BitmapWidth, and height for tV)
                   Row = (Ascii - Font_Default.HeaderInfo.BaseCharOffset) \ Font_Default.RowPitch
                   u = ((Ascii - Font_Default.HeaderInfo.BaseCharOffset) - (Row * Font_Default.RowPitch)) * Font_Default.ColFactor
                   v = Row * Font_Default.RowFactor
                   'Set up the verticies
                   '    4____5
                   ' 1|\\    |  1 = 4
                   '  | \\   |  3 = 6
                   '  |  \\  |
                   '  |   \\ |
                   ' 2|____\\|
                   '       3 6
                   
                   'Triangle 1
                   With ChatVA(0 + (6 * Pos))   'Top-left corner
                       .Color = TempColor
                       .X = X + Count
                       .Y = Y2
                       .tU = u
                       .tV = v
                       .Rhw = 1
                   End With
                   With ChatVA(1 + (6 * Pos))   'Bottom-left corner
                       .Color = TempColor
                       .X = X + Count
                       .Y = Y2 + Font_Default.HeaderInfo.CellHeight
                       .tU = u
                       .tV = v + Font_Default.RowFactor
                       .Rhw = 1
                   End With
                   With ChatVA(2 + (6 * Pos))   'Bottom-right corner
                       .Color = TempColor
                       .X = X + Count + Font_Default.HeaderInfo.CellWidth
                       .Y = Y2 + Font_Default.HeaderInfo.CellHeight
                       .tU = u + Font_Default.ColFactor
                       .tV = v + Font_Default.RowFactor
                       .Rhw = 1
                   End With
                   
                   'Triangle 2 (only one new verticy is needed)
                   ChatVA(3 + (6 * Pos)) = ChatVA(0 + (6 * Pos)) 'Top-left corner
                   With ChatVA(4 + (6 * Pos))   'Top-right corner
                       .Color = TempColor
                       .X = X + Count + Font_Default.HeaderInfo.CellWidth
                       .Y = Y2
                       .tU = u + Font_Default.ColFactor
                       .tV = v
                       .Rhw = 1
                   End With
                   ChatVA(5 + (6 * Pos)) = ChatVA(2 + (6 * Pos))
                   'Update the character we are on
                   Pos = Pos + 1
   
                   'Shift over the the position to render the next character
                   Count = Count + Font_Default.HeaderInfo.CharWidth(Ascii)
               End If
               
               'Check to reset the color
               If ResetColor Then
                   ResetColor = 0
                   TempColor = ChatTextBuffer(LoopC).Color
               End If
               
           Next j
           
       End If
   Next LoopC
   
   On Error GoTo 0
   'Check what rendering method we're using
   If AlternateRenderText = 0 Then
   
       'Set the vertex array to the vertex buffer
       If Pos <= 0 Then Pos = 1
       If Not D3DDevice Is Nothing Then   'Make sure the D3DDevice exists - this will only return false if we received messages before it had time to load
           Set ChatVB = D3DDevice.CreateVertexBuffer(FVF_Size * Pos * 6, 0, FVF, D3DPOOL_MANAGED)
           D3DVertexBuffer8SetData ChatVB, 0, FVF_Size * Pos * 6, 0, ChatVA(0)
       End If
       Erase ChatVA()
       
   End If
   

End Sub

Public Sub Engine_Blood_Create(ByVal X As Integer, ByVal Y As Integer) '***************************************************************** 'Create a blood splatter 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Blood_Create '***************************************************************** Dim BloodIndex As Integer

   'Get the next open blood slot
   Do
       BloodIndex = BloodIndex + 1
       'Update LastBlood if we go over the size of the current array
       If BloodIndex > LastBlood Then
           LastBlood = BloodIndex
           ReDim Preserve BloodList(1 To LastBlood)
           Exit Do
       End If
   Loop While BloodList(BloodIndex).Grh.GrhIndex > 0
   'Fill in the values
   BloodList(BloodIndex).Pos.X = X
   BloodList(BloodIndex).Pos.Y = Y
   Engine_Init_Grh BloodList(BloodIndex).Grh, 21

End Sub

Public Sub Engine_Blood_Erase(ByVal BloodIndex As Integer) '***************************************************************** 'Erase a blood splatter 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Blood_Erase '*****************************************************************

   'Clear the selected index
   BloodList(BloodIndex).Grh.FrameCounter = 0
   BloodList(BloodIndex).Grh.GrhIndex = 0
   BloodList(BloodIndex).Pos.X = 0
   BloodList(BloodIndex).Pos.Y = 0
   'Update LastBlood
   If BloodIndex = LastBlood Then
       Do Until BloodList(LastBlood).Grh.GrhIndex > 1
           'Move down one splatter
           LastBlood = LastBlood - 1
           If LastBlood = 0 Then
               Erase BloodList
               Exit Sub
           Else
               'We still have blood, resize the array to end at the last used slot
               ReDim Preserve BloodList(1 To LastBlood)
           End If
       Loop
   End If

End Sub

Sub Engine_ChangeHeading(ByVal Direction As Byte) '***************************************************************** 'Face user in appropriate direction 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_ChangeHeading '*****************************************************************

   'Check for a valid UserCharIndex
   If UserCharIndex <= 0 Or UserCharIndex > LastChar Then
   
       'We have an invalid user char index, so we must have the wrong one - request an update on the right one
       sndBuf.Put_Byte DataCode.User_RequestUserCharIndex
       Exit Sub
       
   End If
   
   'Only rotate if the user is not already facing that direction
   If CharList(UserCharIndex).Heading <> Direction Then
       sndBuf.Allocate 2
       sndBuf.Put_Byte DataCode.User_Rotate
       sndBuf.Put_Byte Direction
   End If

End Sub

Sub Engine_Char_Erase(ByVal CharIndex As Integer) '***************************************************************** 'Erases a character from CharList and map 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Char_Erase '*****************************************************************

   'Check for targeted character
   If TargetCharIndex = CharIndex Then TargetCharIndex = 0
   If CharIndex = 0 Then Exit Sub
   If CharIndex > LastChar Then Exit Sub
   
   'Make inactive
   CharList(CharIndex).Active = 0
   'Update LastChar
   If CharIndex = LastChar Then
       Do Until CharList(LastChar).Active = 1
           LastChar = LastChar - 1
           If LastChar = 0 Then
               Exit Do
           Else
               ReDim Preserve CharList(1 To LastChar)
           End If
       Loop
   End If

End Sub

Function Engine_UserIsFacingChar() As Boolean '***************************************************************** 'Checks if the user is facing a character - used to check if a character ' is at a tile before making a melee attack 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_UserIsFacingChar '***************************************************************** Dim i As Long Dim X As Long Dim Y As Long Dim AddX As Long Dim AddY As Long

   'Get the co-ordinates of the tile the user is facing
   Select Case CharList(UserCharIndex).Heading
   Case NORTH
       AddY = -1
   Case EAST
       AddX = 1
   Case SOUTH
       AddY = 1
   Case WEST
       AddX = -1
   Case NORTHEAST
       AddY = -1
       AddX = 1
   Case SOUTHEAST
       AddY = 1
       AddX = 1
   Case SOUTHWEST
       AddY = 1
       AddX = -1
   Case NORTHWEST
       AddY = -1
       AddX = -1
   End Select
   X = CharList(UserCharIndex).Pos.X + AddX
   Y = CharList(UserCharIndex).Pos.Y + AddY
   
   'Make sure the tile is valid
   If X <= 0 Then Exit Function
   If Y <= 0 Then Exit Function
   If X > MapInfo.Width Then Exit Function
   If Y > MapInfo.Height Then Exit Function
   
   'Loop through all the characters
   For i = 1 To LastChar
       If i <> UserCharIndex Then
           
           'Check if the character is located at the tile
           If CharList(i).Pos.X = X Then
               If CharList(i).Pos.Y = Y Then
                   
                   'We have an character here!
                   Engine_UserIsFacingChar = True
                   Exit Function
                   
               End If
           End If
           
       End If
   Next i

End Function

Sub Engine_Char_Make(ByVal CharIndex As Integer, ByVal Body As Integer, ByVal Head As Integer, ByVal Heading As Byte, ByVal X As Integer, ByVal Y As Integer, ByVal Speed As Byte, ByVal Name As String, ByVal Weapon As Integer, ByVal Hair As Integer, ByVal Wings As Integer, ByVal ChatID As Byte, ByVal CharType As Byte, Optional ByVal HP As Byte = 100, Optional ByVal MP As Byte = 100) '***************************************************************** 'Makes a new character and puts it on the map 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Char_Make '***************************************************************** Dim EmptyChar As Char

   'Update LastChar
   If CharIndex > LastChar Then
       LastChar = CharIndex
       ReDim Preserve CharList(1 To LastChar)
   End If
   'Clear the character
   CharList(CharIndex) = EmptyChar
   'Set the apperances
   CharList(CharIndex).Body = BodyData(Body)
   CharList(CharIndex).Head = HeadData(Head)
   CharList(CharIndex).Hair = HairData(Hair)
   CharList(CharIndex).Weapon = WeaponData(Weapon)
   CharList(CharIndex).Wings = WingData(Wings)
   CharList(CharIndex).Heading = Heading
   CharList(CharIndex).HeadHeading = Heading
   CharList(CharIndex).HealthPercent = HP
   CharList(CharIndex).ManaPercent = MP
   CharList(CharIndex).Speed = Speed
   CharList(CharIndex).NPCChatIndex = ChatID
   CharList(CharIndex).CharType = CharType
   
   'Update position
   CharList(CharIndex).Pos.X = X
   CharList(CharIndex).Pos.Y = Y
   'Make active
   CharList(CharIndex).Active = 1
   
   'Calculate the name length so we can center the name above the head
   CharList(CharIndex).Name = Name
   CharList(CharIndex).NameOffset = Engine_GetTextWidth(Font_Default, Name) * 0.5
   'Set action index
   CharList(CharIndex).ActionIndex = 0

End Sub

Sub Engine_Char_Move_ByHead(ByVal CharIndex As Integer, ByVal nHeading As Byte, ByVal Running As Byte) '***************************************************************** 'Starts the movement of a character in nHeading direction 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Char_Move_ByHead '***************************************************************** Dim AddX As Integer Dim AddY As Integer Dim X As Integer Dim Y As Integer Dim nX As Integer Dim nY As Integer

'Check for a valid CharIndex

   If CharIndex <= 0 Then Exit Sub
   X = CharList(CharIndex).Pos.X
   Y = CharList(CharIndex).Pos.Y
   'Figure out which way to move
   Select Case nHeading
   Case NORTH
       AddY = -1
   Case EAST
       AddX = 1
   Case SOUTH
       AddY = 1
   Case WEST
       AddX = -1
   Case NORTHEAST
       AddY = -1
       AddX = 1
   Case SOUTHEAST
       AddY = 1
       AddX = 1
   Case SOUTHWEST
       AddY = 1
       AddX = -1
   Case NORTHWEST
       AddY = -1
       AddX = -1
   End Select
   'Update the character position and settings
   nX = X + AddX
   nY = Y + AddY
   CharList(CharIndex).Pos.X = nX
   CharList(CharIndex).Pos.Y = nY
   CharList(CharIndex).MoveOffset.X = -(TilePixelWidth * AddX)
   CharList(CharIndex).MoveOffset.Y = -(TilePixelHeight * AddY)
   CharList(CharIndex).Moving = 1
   CharList(CharIndex).Heading = nHeading
   CharList(CharIndex).HeadHeading = nHeading
   CharList(CharIndex).ScrollDirectionX = AddX
   CharList(CharIndex).ScrollDirectionY = AddY
   CharList(CharIndex).ActionIndex = 1
   CharList(CharIndex).Running = Running

End Sub

Sub Engine_Char_Move_ByPos(ByVal CharIndex As Integer, ByVal nX As Integer, ByVal nY As Integer, ByVal Running As Byte) '***************************************************************** 'Starts the movement of a character to nX,nY 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Char_Move_ByPos '***************************************************************** Dim X As Integer Dim Y As Integer Dim AddX As Integer Dim AddY As Integer Dim nHeading As Byte

   X = CharList(CharIndex).Pos.X
   Y = CharList(CharIndex).Pos.Y
   AddX = nX - X
   AddY = nY - Y
   'Figure out the direction the character is going
   If Sgn(AddX) = 1 Then nHeading = EAST
   If Sgn(AddX) = -1 Then nHeading = WEST
   If Sgn(AddY) = -1 Then nHeading = NORTH
   If Sgn(AddY) = 1 Then nHeading = SOUTH
   If Sgn(AddX) = 1 And Sgn(AddY) = -1 Then
       nHeading = NORTHEAST
   End If
   If Sgn(AddX) = 1 And Sgn(AddY) = 1 Then
       nHeading = SOUTHEAST
   End If
   If Sgn(AddX) = -1 And Sgn(AddY) = 1 Then
       nHeading = SOUTHWEST
   End If
   If Sgn(AddX) = -1 And Sgn(AddY) = -1 Then
       nHeading = NORTHWEST
   End If
   'Update the character position and settings
   CharList(CharIndex).Running = Running
   CharList(CharIndex).Pos.X = nX
   CharList(CharIndex).Pos.Y = nY
   CharList(CharIndex).MoveOffset.X = -1 * (TilePixelWidth * AddX)
   CharList(CharIndex).MoveOffset.Y = -1 * (TilePixelHeight * AddY)
   CharList(CharIndex).Moving = 1
   CharList(CharIndex).Heading = nHeading
   CharList(CharIndex).HeadHeading = nHeading
   CharList(CharIndex).ScrollDirectionX = Sgn(AddX)
   CharList(CharIndex).ScrollDirectionY = Sgn(AddY)
   CharList(CharIndex).ActionIndex = 1
   
   'If the targeted character move, re-check if the path is blocked
   If TargetCharIndex > 0 Then
       If CharIndex = UserCharIndex Or CharIndex = TargetCharIndex Then
           ClearPathToTarget = Engine_ClearPath(CharList(UserCharIndex).Pos.X, CharList(UserCharIndex).Pos.Y, CharList(CharIndex).Pos.X, CharList(CharIndex).Pos.Y)
       End If
   End If

End Sub

Sub Engine_ConvertCPtoTP(ByVal cx As Integer, ByVal cy As Integer, ByRef tX As Integer, ByRef tY As Integer) '****************************************** 'Converts where the user clicks in the main window 'to a tile position 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_ConvertCPtoTP '******************************************

   tX = UserPos.X + cx \ TilePixelWidth - WindowTileWidth \ 2
   tY = UserPos.Y + cy \ TilePixelHeight - WindowTileHeight \ 2

End Sub

Public Sub Engine_Damage_Create(ByVal X As Integer, ByVal Y As Integer, ByVal Value As Integer) '***************************************************************** 'Create damage text 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Damage_Create '***************************************************************** Dim DamageIndex As Integer

'Get the next open damage slot

   Do
       DamageIndex = DamageIndex + 1
       'Update LastDamage if we go over the size of the current array
       If DamageIndex > LastDamage Then
           LastDamage = DamageIndex
           ReDim Preserve DamageList(1 To LastDamage)
           Exit Do
       End If
   Loop While DamageList(DamageIndex).Counter > 0
   'Set the values
   If Value < 1 Then DamageList(DamageIndex).Value = "Miss" Else DamageList(DamageIndex).Value = Value
   DamageList(DamageIndex).Counter = DamageDisplayTime
   DamageList(DamageIndex).Width = Engine_GetTextWidth(Font_Default, DamageList(DamageIndex).Value)
   DamageList(DamageIndex).Pos.X = X
   DamageList(DamageIndex).Pos.Y = Y

End Sub

Public Sub Engine_Damage_Erase(ByVal DamageIndex As Integer) '***************************************************************** 'Erase damage text 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Damage_Erase '*****************************************************************

   'Clear the selected index
   DamageList(DamageIndex).Counter = 0
   DamageList(DamageIndex).Value = vbNullString
   DamageList(DamageIndex).Width = 0
   'Update LastDamage
   If DamageIndex = LastDamage Then
       Do Until DamageList(LastDamage).Counter > 0
           'Move down one splatter
           LastDamage = LastDamage - 1
           If LastDamage = 0 Then
               Erase DamageList
               Exit Sub
           Else
               'We still have damage text, resize the array to end at the last used slot
               ReDim Preserve DamageList(1 To LastDamage)
           End If
       Loop
   End If

End Sub

Public Sub Engine_Projectile_Create(ByVal AttackerIndex As Integer, ByVal TargetIndex As Integer, ByVal GrhIndex As Long, ByVal Rotation As Byte) '***************************************************************** 'Creates a projectile for a ranged weapon 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Projectile_Create '***************************************************************** Dim ProjectileIndex As Integer

   If AttackerIndex = 0 Then Exit Sub
   If TargetIndex = 0 Then Exit Sub
   If AttackerIndex > UBound(CharList) Then Exit Sub
   If TargetIndex > UBound(CharList) Then Exit Sub
   'Get the next open projectile slot
   Do
       ProjectileIndex = ProjectileIndex + 1
       
       'Update LastProjectile if we go over the size of the current array
       If ProjectileIndex > LastProjectile Then
           LastProjectile = ProjectileIndex
           ReDim Preserve ProjectileList(1 To LastProjectile)
           Exit Do
       End If
       
   Loop While ProjectileList(ProjectileIndex).Grh.GrhIndex > 0
   
   'Figure out the initial rotation value
   ProjectileList(ProjectileIndex).Rotate = Engine_GetAngle(CharList(AttackerIndex).Pos.X, CharList(AttackerIndex).Pos.Y, CharList(TargetIndex).Pos.X, CharList(TargetIndex).Pos.Y)
   
   'Fill in the values
   ProjectileList(ProjectileIndex).tX = CharList(TargetIndex).Pos.X * 32
   ProjectileList(ProjectileIndex).tY = CharList(TargetIndex).Pos.Y * 32
   ProjectileList(ProjectileIndex).RotateSpeed = Rotation
   ProjectileList(ProjectileIndex).X = CharList(AttackerIndex).Pos.X * 32
   ProjectileList(ProjectileIndex).Y = CharList(AttackerIndex).Pos.Y * 32
   Engine_Init_Grh ProjectileList(ProjectileIndex).Grh, GrhIndex
   

End Sub

Public Sub Engine_Effect_Create(ByVal X As Integer, ByVal Y As Integer, ByVal GrhIndex As Long, Optional ByVal Angle As Single = 0, Optional ByVal Time As Long = 0, Optional ByVal Animated As Byte = 1, Optional ByVal DelayFrames As Single = 0) '***************************************************************** 'Creates an effect layer for spells and such 'Life is only used if the effect is looped 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Effect_Create '***************************************************************** Dim EffectIndex As Integer

   'Get the next open effect slot
   Do
       EffectIndex = EffectIndex + 1
       'Update LastEffect if we go over the size of the current array
       If EffectIndex > LastEffect Then
           LastEffect = EffectIndex
           ReDim Preserve EffectList(1 To LastEffect)
           Exit Do
       End If
   Loop While EffectList(EffectIndex).Grh.GrhIndex > 0
   'Fill in the values
   If Time > 0 Then EffectList(EffectIndex).Time = timeGetTime + Time Else EffectList(EffectIndex).Time = 0
   EffectList(EffectIndex).Animated = Animated
   EffectList(EffectIndex).Angle = Angle
   EffectList(EffectIndex).Pos.X = X
   EffectList(EffectIndex).Pos.Y = Y
   Engine_Init_Grh EffectList(EffectIndex).Grh, GrhIndex
   EffectList(EffectIndex).Grh.FrameCounter = 1 - DelayFrames
   

End Sub

Public Sub Engine_Projectile_Erase(ByVal ProjectileIndex As Integer) '***************************************************************** 'Erase a projectile by the projectile index 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Projectile_Erase '*****************************************************************

   'Clear the selected index
   ProjectileList(ProjectileIndex).Grh.FrameCounter = 0
   ProjectileList(ProjectileIndex).Grh.GrhIndex = 0
   ProjectileList(ProjectileIndex).X = 0
   ProjectileList(ProjectileIndex).Y = 0
   ProjectileList(ProjectileIndex).tX = 0
   ProjectileList(ProjectileIndex).tY = 0
   ProjectileList(ProjectileIndex).Rotate = 0
   ProjectileList(ProjectileIndex).RotateSpeed = 0

   'Update LastProjectile
   If ProjectileIndex = LastProjectile Then
       Do Until ProjectileList(ProjectileIndex).Grh.GrhIndex > 1
           'Move down one projectile
           LastProjectile = LastProjectile - 1
           If LastProjectile = 0 Then Exit Do
       Loop
       If ProjectileIndex <> LastProjectile Then
           'We still have projectiles, resize the array to end at the last used slot
           If LastProjectile > 0 Then
               ReDim Preserve ProjectileList(1 To LastProjectile)
           Else
               Erase ProjectileList
           End If
       End If
   End If

End Sub

Public Sub Engine_Effect_Erase(ByVal EffectIndex As Integer) '***************************************************************** 'Erase an effect by the effect index 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Effect_Erase '*****************************************************************

   'Clear the selected index
   ZeroMemory EffectList(EffectIndex), LenB(EffectList(EffectIndex))
   'Update LastEffect
   If EffectIndex = LastEffect Then
       Do Until EffectList(LastEffect).Grh.GrhIndex > 1
           'Move down one effect
           LastEffect = LastEffect - 1
           If LastEffect = 0 Then
               Erase EffectList
               Exit Sub
           Else
               'We still have effects, resize the array to end at the last used slot
               ReDim Preserve EffectList(1 To LastEffect)
           End If
       Loop
   End If

End Sub

Private Function Engine_ElapsedTime() As Long '************************************************************** 'Gets the time that past since the last call 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_ElapsedTime '************************************************************** Dim Start_Time As Long

   'Get current time
   Start_Time = timeGetTime
   'Calculate elapsed time
   Engine_ElapsedTime = Start_Time - EndTime
   'Get next end time
   EndTime = Start_Time

End Function

Function Engine_FileExist(File As String, FileType As VbFileAttribute) As Boolean '***************************************************************** 'Checks to see if a file exists 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_FileExist '***************************************************************** On Error GoTo ErrOut

   If LenB(Dir$(File, FileType)) <> 0 Then Engine_FileExist = True

Exit Function

'An error will most likely be caused by invalid filenames (those that do not follow the file name rules) ErrOut:

   Engine_FileExist = False
   

End Function

Public Function Engine_GetAngle(ByVal CenterX As Integer, ByVal CenterY As Integer, ByVal TargetX As Integer, ByVal TargetY As Integer) As Single '************************************************************ 'Gets the angle between two points in a 2d plane 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_GetAngle '************************************************************ Dim SideA As Single Dim SideC As Single

   On Error GoTo ErrOut
   'Check for horizontal lines (90 or 270 degrees)
   If CenterY = TargetY Then
       'Check for going right (90 degrees)
       If CenterX < TargetX Then
           Engine_GetAngle = 90
           'Check for going left (270 degrees)
       Else
           Engine_GetAngle = 270
       End If
       'Exit the function
       Exit Function
   End If
   'Check for horizontal lines (360 or 180 degrees)
   If CenterX = TargetX Then
       'Check for going up (360 degrees)
       If CenterY > TargetY Then
           Engine_GetAngle = 360
           'Check for going down (180 degrees)
       Else
           Engine_GetAngle = 180
       End If
       'Exit the function
       Exit Function
   End If
   'Calculate Side C
   SideC = Sqr(Abs(TargetX - CenterX) ^ 2 + Abs(TargetY - CenterY) ^ 2)
   'Side B = CenterY
   'Calculate Side A
   SideA = Sqr(Abs(TargetX - CenterX) ^ 2 + TargetY ^ 2)
   'Calculate the angle
   Engine_GetAngle = (SideA ^ 2 - CenterY ^ 2 - SideC ^ 2) / (CenterY * SideC * -2)
   Engine_GetAngle = (Atn(-Engine_GetAngle / Sqr(-Engine_GetAngle * Engine_GetAngle + 1)) + 1.5708) * 57.29583
   'If the angle is >180, subtract from 360
   If TargetX < CenterX Then Engine_GetAngle = 360 - Engine_GetAngle
   'Exit function

Exit Function

   'Check for error

ErrOut:

   'Return a 0 saying there was an error
   Engine_GetAngle = 0

Exit Function

End Function

Public Function Engine_GetTextWidth(ByRef UseFont As CustomFont, ByVal Text As String) As Integer '*************************************************** 'Returns the width of text 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_GetTextWidth '*************************************************** Dim i As Integer

   'Make sure we have text
   If LenB(Text) = 0 Then Exit Function
   
   'Loop through the text
   For i = 1 To Len(Text)
       
       'Add up the stored character widths
       Engine_GetTextWidth = Engine_GetTextWidth + UseFont.HeaderInfo.CharWidth(Asc(Mid$(Text, i, 1)))
       
   Next i

End Function

Sub Engine_Init_Signs(ByVal Language As String) '***************************************************************** 'Loads the sign messages 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_Signs '***************************************************************** Dim NumSigns As Integer Dim LoopC As Integer Dim s As String

   'Get the number of signs
   NumSigns = Val(Var_Get(SignsPath & "_numsigns.ini", "MAIN", "NumSigns"))
   If NumSigns = 0 Then Exit Sub
   ReDim Signs(1 To NumSigns)
   
   'Grab the English text first
   For LoopC = 1 To NumSigns
       Signs(LoopC) = Trim$(Var_Get(SignsPath & "english.ini", "SIGNS", LoopC))
   Next LoopC
   
   'If we're not using English, grab the foreign language, this way any missing is still presented as English
   If LCase$(Language) <> "english" Then
       For LoopC = 1 To NumSigns
           s = Trim$(Var_Get(SignsPath & LCase$(Language) & ".ini", "SIGNS", LoopC))
           If s <> vbNullString Then Signs(LoopC) = s
       Next LoopC
   End If
   

End Sub

Function Engine_Init_Messages(ByVal Language As String) As String '***************************************************************** 'Loads the game messages 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_Messages '***************************************************************** Dim LoopC As Byte Dim s As String

   'Make sure we are working in lowercase (since all our files are in lowercase)
   Language = LCase$(Language)
   
   'Check for a redirection flag (will return nothing if the flag doesn't exist)
   Do  'This "Do" will allow us to do redirections to redirections, even though we shouldn't even do that
       s = Var_Get(MessagePath & Language & ".ini", "REDIRECT", "TO")
       If LenB(s) <> 0 Then
           If Engine_FileExist(MessagePath & LCase$(s) & ".ini", vbNormal) = False Then
               MsgBox "Invalid language redirection! Could not load system messages!" & vbCrLf & _
                       "Language '" & Language & "' redirected to '" & LCase$(s) & "', which could not be found!", vbOKOnly
               Exit Function
           End If
           Language = LCase$(s)
       Else
       
           'No redirection was found, so move on
           Exit Do
           
       End If
   Loop
   
   Engine_Init_Messages = Language
   'Get the number of messages
   NumMessages = CByte(Var_Get(MessagePath & "_nummessages.ini", "MAIN", "NumMessages"))
   
   'Check for a valid number of messages
   If NumMessages = 0 Then
       MsgBox "Error loading message count!", vbOKOnly
       Exit Function
   End If
   
   'Resize our message array to hold all the messages
   ReDim Message(1 To NumMessages)
   
   'Loop through every message and find the message string
   For LoopC = 1 To NumMessages
       Message(LoopC) = Var_Get(MessagePath & Language & ".ini", "MAIN", CStr(LoopC))
       
       'If the message wasn't found, resort to the primary language, English, since that should hold all messages
       If LCase$(Language) <> "english" Then   'Make sure we're not already using English
           If LenB(Trim$(Message(LoopC))) = 0 Then
               Message(LoopC) = Var_Get(MessagePath & "english.ini", "MAIN", CStr(LoopC))
           End If
       End If
       
   Next LoopC
   
   'Load the NPC chat messages
   Engine_Init_NPCChat Language
   

End Function

Sub Engine_Init_BodyData() '***************************************************************** 'Loads Body.dat 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_BodyData '***************************************************************** Dim LoopC As Long Dim j As Long

'Get number of bodies

   NumBodies = CLng(Var_Get(DataPath & "Body.dat", "INIT", "NumBodies"))
   
   'Resize array
   ReDim BodyData(0 To NumBodies) As BodyData
   
   'Fill list
   For LoopC = 1 To NumBodies
       For j = 1 To 8
           Engine_Init_Grh BodyData(LoopC).Walk(j), CLng(Var_Get(DataPath & "Body.dat", LoopC, j)), 0
           Engine_Init_Grh BodyData(LoopC).Attack(j), CLng(Var_Get(DataPath & "Body.dat", LoopC, "a" & j)), 1
       Next j
       BodyData(LoopC).HeadOffset.X = CLng(Var_Get(DataPath & "Body.dat", LoopC, "HeadOffsetX"))
       BodyData(LoopC).HeadOffset.Y = CLng(Var_Get(DataPath & "Body.dat", LoopC, "HeadOffsetY"))
   Next LoopC

End Sub

Sub Engine_Init_WingData() '***************************************************************** 'Loads Wing.dat 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_WingData '***************************************************************** Dim LoopC As Long Dim j As Long

   'Get number of wings
   NumWings = CLng(Var_Get(DataPath & "Wing.dat", "INIT", "NumWings"))
   
   'Resize array
   ReDim WingData(0 To NumWings) As WingData
   
   'Fill list
   For LoopC = 1 To NumWings
       For j = 1 To 8
           Engine_Init_Grh WingData(LoopC).Walk(j), CLng(Var_Get(DataPath & "Wing.dat", LoopC, j)), 0
           Engine_Init_Grh WingData(LoopC).Attack(j), CLng(Var_Get(DataPath & "Wing.dat", LoopC, "a" & j)), 1
       Next j
   Next LoopC

End Sub

Private Function Engine_Init_D3DDevice(D3DCREATEFLAGS As CONST_D3DCREATEFLAGS) As Boolean '************************************************************ 'Initialize the Direct3D Device - start off trying with the 'best settings and move to the worst until one works 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_D3DDevice '************************************************************

   'When there is an error, destroy the D3D device and get ready to make a new one
   On Error GoTo ErrOut
   'Retrieve current display mode
   D3D.GetAdapterDisplayMode D3DADAPTER_DEFAULT, DispMode
   'Set format for windowed mode
   If Windowed Then
       D3DWindow.Windowed = 1  'State that using windowed mode
       D3DWindow.SwapEffect = D3DSWAPEFFECT_COPY
       D3DWindow.BackBufferFormat = DispMode.Format    'Use format just retrieved
   Else
       If Bit32 = 1 Then DispMode.Format = D3DFMT_X8R8G8B8 Else DispMode.Format = D3DFMT_R5G6B5
       If UseVSync = 1 Then D3DWindow.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC Else D3DWindow.SwapEffect = D3DSWAPEFFECT_COPY
       DispMode.Width = ScreenWidth
       DispMode.Height = ScreenHeight
       D3DWindow.BackBufferCount = 1
       D3DWindow.BackBufferFormat = DispMode.Format
       D3DWindow.BackBufferWidth = ScreenWidth
       D3DWindow.BackBufferHeight = ScreenHeight
       D3DWindow.hDeviceWindow = frmMain.hwnd
   End If
   If UseMotionBlur Then
       D3DWindow.EnableAutoDepthStencil = 1
       D3DWindow.AutoDepthStencilFormat = D3DFMT_D16
   End If
   
   'Make sure the form is the correct side
   frmMain.Width = ScreenWidth * Screen.TwipsPerPixelX
   frmMain.Height = ScreenHeight * Screen.TwipsPerPixelY
   
   'Set the D3DDevices
   If Not D3DDevice Is Nothing Then Set D3DDevice = Nothing
   Set D3DDevice = D3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, frmMain.hwnd, D3DCREATEFLAGS, D3DWindow)
   
   'Store the create flags
   UsedCreateFlags = D3DCREATEFLAGS
   'Everything was successful
   Engine_Init_D3DDevice = True
   
   'Force the main form to refresh - vital for widescreen! Remove and find out why if you dare... >:D
   frmMain.Show
   frmMain.Refresh
   DoEvents

Exit Function

ErrOut:

   'Destroy the D3DDevice so it can be remade
   Set D3DDevice = Nothing
   'Return a failure
   Engine_Init_D3DDevice = False

End Function

Sub Engine_Init_Grh(ByRef Grh As Grh, ByVal GrhIndex As Long, Optional ByVal Started As Byte = 2) '***************************************************************** 'Sets up a grh. MUST be done before rendering 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_Grh '*****************************************************************

   If GrhIndex <= 0 Then Exit Sub
   Grh.GrhIndex = GrhIndex
   If Started = 2 Then
       If GrhData(Grh.GrhIndex).NumFrames > 1 Then
           Grh.Started = 1
       Else
           Grh.Started = 0
       End If
   Else
       'Make sure the graphic can be started
       If GrhData(Grh.GrhIndex).NumFrames = 1 Then
           Started = 0
       End If
       Grh.Started = Started
   End If
   Grh.LastCount = timeGetTime
   Grh.FrameCounter = 1

End Sub

Sub Engine_Init_GrhData() '***************************************************************** 'Loads Grh.dat 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_GrhData '***************************************************************** Dim FileNum As Byte Dim Grh As Long Dim Frame As Long

   'Get Number of Graphics
   NumGrhs = CLng(Var_Get(DataPath & "Grh.ini", "INIT", "NumGrhs"))
   
   'Resize arrays
   ReDim GrhData(1 To NumGrhs) As GrhData
   
   'Open files
   FileNum = FreeFile
   Open DataPath & "Grh.dat" For Binary As #FileNum
   Seek #FileNum, 1
   
   'Fill Grh List
   Get #FileNum, , Grh
   
   Do Until Grh <= 0
   
       'Get number of frames
       Get #FileNum, , GrhData(Grh).NumFrames
       If GrhData(Grh).NumFrames <= 0 Then GoTo ErrorHandler
       
       If GrhData(Grh).NumFrames > 1 Then
       
           'Read a animation GRH set
           ReDim GrhData(Grh).Frames(1 To GrhData(Grh).NumFrames)
           For Frame = 1 To GrhData(Grh).NumFrames
               Get #FileNum, , GrhData(Grh).Frames(Frame)
               If GrhData(Grh).Frames(Frame) <= 0 Then
                   GoTo ErrorHandler
               End If
           Next Frame
           
           Get #FileNum, , GrhData(Grh).Speed
           GrhData(Grh).Speed = GrhData(Grh).Speed * 0.075 * EngineBaseSpeed
           If GrhData(Grh).Speed <= 0 Then GoTo ErrorHandler
           
           'Compute width and height
           GrhData(Grh).pixelHeight = GrhData(GrhData(Grh).Frames(1)).pixelHeight
           If GrhData(Grh).pixelHeight <= 0 Then GoTo ErrorHandler
           GrhData(Grh).pixelWidth = GrhData(GrhData(Grh).Frames(1)).pixelWidth
           If GrhData(Grh).pixelWidth <= 0 Then GoTo ErrorHandler
           GrhData(Grh).TileWidth = GrhData(GrhData(Grh).Frames(1)).TileWidth
           If GrhData(Grh).TileWidth <= 0 Then GoTo ErrorHandler
           GrhData(Grh).TileHeight = GrhData(GrhData(Grh).Frames(1)).TileHeight
           If GrhData(Grh).TileHeight <= 0 Then GoTo ErrorHandler
           
       Else
       
           'Read in normal GRH data
           ReDim GrhData(Grh).Frames(1 To 1)
           Get #FileNum, , GrhData(Grh).FileNum
           If GrhData(Grh).FileNum <= 0 Then GoTo ErrorHandler
           Get #FileNum, , GrhData(Grh).SX
           If GrhData(Grh).SX < 0 Then GoTo ErrorHandler
           Get #FileNum, , GrhData(Grh).SY
           If GrhData(Grh).SY < 0 Then GoTo ErrorHandler
           Get #FileNum, , GrhData(Grh).pixelWidth
           If GrhData(Grh).pixelWidth <= 0 Then GoTo ErrorHandler
           Get #FileNum, , GrhData(Grh).pixelHeight
           If GrhData(Grh).pixelHeight <= 0 Then GoTo ErrorHandler
           
           'Compute width and height
           GrhData(Grh).TileWidth = GrhData(Grh).pixelWidth / TilePixelHeight
           GrhData(Grh).TileHeight = GrhData(Grh).pixelHeight / TilePixelWidth
           GrhData(Grh).Frames(1) = Grh
       End If
       'Get Next Grh Number
       Get #FileNum, , Grh
       
   Loop
   
   Close #FileNum

Exit Sub

ErrorHandler:

   Close #FileNum
   MsgBox "Error while loading the Grh.dat! Stopped at GRH number: " & Grh
   IsUnloading = 1

End Sub

Public Sub Engine_Init_GUI(Optional ByVal LoadCustomPos As Byte = 1) '************************************************************ 'Load skin GUI data 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_GUI '************************************************************ Dim ImageOffsetX As Long Dim ImageOffsetY As Long Dim ImageSpaceX As Long Dim ImageSpaceY As Long Dim LoopC As Long Dim s As String 'Stores the path to our master skins file (.ini) Dim t As String 'Stores the path to our custom window positions file (.dat) Dim X As Long Dim Y As Long

   s = DataPath & "Skins\" & CurrentSkin & ".ini"
   t = DataPath & "Skins\" & CurrentSkin & ".dat"
   
   'Load Quickbar
   With GameWindow.QuickBar
       If LoadCustomPos Then
           .Screen.X = Val(Var_Get(t, "QUICKBAR", "ScreenX"))
           .Screen.Y = Val(Var_Get(t, "QUICKBAR", "ScreenY"))
       Else
           .Screen.X = Val(Var_Get(s, "QUICKBAR", "ScreenX"))
           .Screen.Y = Val(Var_Get(s, "QUICKBAR", "ScreenY"))
       End If
       .Screen.Width = Val(Var_Get(s, "QUICKBAR", "ScreenWidth"))
       .Screen.Height = Val(Var_Get(s, "QUICKBAR", "ScreenHeight"))
       Engine_Init_Grh .SkinGrh, Val(Var_Get(s, "QUICKBAR", "Grh"))
   End With
   For LoopC = 1 To 12
       With GameWindow.QuickBar.Image(LoopC)
           .X = Val(Var_Get(s, "QUICKBAR", "Image" & LoopC & "X"))
           .Y = Val(Var_Get(s, "QUICKBAR", "Image" & LoopC & "Y"))
           .Width = 32
           .Height = 32
       End With
   Next LoopC
   
   'Load stats window
   With GameWindow.StatWindow
       If LoadCustomPos Then
           .Screen.X = Val(Var_Get(t, "STATWINDOW", "ScreenX"))
           .Screen.Y = Val(Var_Get(t, "STATWINDOW", "ScreenY"))
       Else
           .Screen.X = Val(Var_Get(s, "STATWINDOW", "ScreenX"))
           .Screen.Y = Val(Var_Get(s, "STATWINDOW", "ScreenY"))
       End If
       .Screen.Width = Val(Var_Get(s, "STATWINDOW", "ScreenWidth"))
       .Screen.Height = Val(Var_Get(s, "STATWINDOW", "ScreenHeight"))
       .AddStr.X = Val(Var_Get(s, "STATWINDOW", "AddStrX"))
       .AddStr.Y = Val(Var_Get(s, "STATWINDOW", "AddStrY"))
       .AddStr.Width = Val(Var_Get(s, "STATWINDOW", "AddStrWidth"))
       .AddStr.Height = Val(Var_Get(s, "STATWINDOW", "AddStrHeight"))
       .AddAgi.X = Val(Var_Get(s, "STATWINDOW", "AddAgiX"))
       .AddAgi.Y = Val(Var_Get(s, "STATWINDOW", "AddAgiY"))
       .AddAgi.Width = Val(Var_Get(s, "STATWINDOW", "AddAgiWidth"))
       .AddAgi.Height = Val(Var_Get(s, "STATWINDOW", "AddAgiHeight"))
       .AddMag.X = Val(Var_Get(s, "STATWINDOW", "AddMagX"))
       .AddMag.Y = Val(Var_Get(s, "STATWINDOW", "AddMagY"))
       .AddMag.Width = Val(Var_Get(s, "STATWINDOW", "AddMagWidth"))
       .AddMag.Height = Val(Var_Get(s, "STATWINDOW", "AddMagHeight"))
       .Str.X = Val(Var_Get(s, "STATWINDOW", "StrX"))
       .Str.Y = Val(Var_Get(s, "STATWINDOW", "StrY"))
       .Agi.X = Val(Var_Get(s, "STATWINDOW", "AgiX"))
       .Agi.Y = Val(Var_Get(s, "STATWINDOW", "AgiY"))
       .Mag.X = Val(Var_Get(s, "STATWINDOW", "MagX"))
       .Mag.Y = Val(Var_Get(s, "STATWINDOW", "MagY"))
       .Gold.X = Val(Var_Get(s, "STATWINDOW", "GoldX"))
       .Gold.Y = Val(Var_Get(s, "STATWINDOW", "GoldY"))
       .DEF.X = Val(Var_Get(s, "STATWINDOW", "DefX"))
       .DEF.Y = Val(Var_Get(s, "STATWINDOW", "DefY"))
       .Dmg.X = Val(Var_Get(s, "STATWINDOW", "DmgX"))
       .Dmg.Y = Val(Var_Get(s, "STATWINDOW", "DmgY"))
       .Points.X = Val(Var_Get(s, "STATWINDOW", "PointsX"))
       .Points.Y = Val(Var_Get(s, "STATWINDOW", "PointsY"))
       Engine_Init_Grh .AddGrh, Val(Var_Get(s, "STATWINDOW", "AddGrh"))
       Engine_Init_Grh .SkinGrh, Val(Var_Get(s, "STATWINDOW", "Grh"))
   End With
   
   'Load chat window
   With GameWindow.ChatWindow
       If LoadCustomPos Then
           .Screen.X = Val(Var_Get(t, "CHATWINDOW", "ScreenX"))
           .Screen.Y = Val(Var_Get(t, "CHATWINDOW", "ScreenY"))
       Else
           .Screen.X = Val(Var_Get(s, "CHATWINDOW", "ScreenX"))
           .Screen.Y = Val(Var_Get(s, "CHATWINDOW", "ScreenY"))
       End If
       .Screen.Width = Val(Var_Get(s, "CHATWINDOW", "ScreenWidth"))
       .Screen.Height = Val(Var_Get(s, "CHATWINDOW", "ScreenHeight"))
       .Text.X = Val(Var_Get(s, "CHATWINDOW", "ChatX"))
       .Text.Y = Val(Var_Get(s, "CHATWINDOW", "ChatY"))
       .Text.Width = Val(Var_Get(s, "CHATWINDOW", "ChatWidth"))
       .Text.Height = Val(Var_Get(s, "CHATWINDOW", "ChatHeight"))
       Engine_Init_Grh .SkinGrh, Val(Var_Get(s, "CHATWINDOW", "Grh"))
   End With
   'Load Inventory
   With GameWindow.Inventory
       If LoadCustomPos Then
           .Screen.X = Val(Var_Get(t, "INVENTORY", "ScreenX"))
           .Screen.Y = Val(Var_Get(t, "INVENTORY", "ScreenY"))
       Else
           .Screen.X = Val(Var_Get(s, "INVENTORY", "ScreenX"))
           .Screen.Y = Val(Var_Get(s, "INVENTORY", "ScreenY"))
       End If
       .Screen.Width = Val(Var_Get(s, "INVENTORY", "ScreenWidth"))
       .Screen.Height = Val(Var_Get(s, "INVENTORY", "ScreenHeight"))
       Engine_Init_Grh .SkinGrh, Val(Var_Get(s, "INVENTORY", "Grh"))
   End With
   ImageOffsetX = Val(Var_Get(s, "INVENTORY", "ImageOffsetX"))
   ImageOffsetY = Val(Var_Get(s, "INVENTORY", "ImageOffsetY"))
   ImageSpaceX = Val(Var_Get(s, "INVENTORY", "ImageSpaceX"))
   ImageSpaceY = Val(Var_Get(s, "INVENTORY", "ImageSpaceY"))
   For LoopC = 1 To MAX_INVENTORY_SLOTS
       With GameWindow.Inventory.Image(LoopC)
           .X = ImageOffsetX + ((ImageSpaceX + 32) * (((LoopC - 1) Mod 7)))
           .Y = ImageOffsetY + ((ImageSpaceY + 32) * ((LoopC - 1) \ 7))
           .Width = 32
           .Height = 32
       End With
   Next LoopC
   'Load Shop window
   GameWindow.Shop = GameWindow.Inventory
   With GameWindow.Shop
       If LoadCustomPos Then
           .Screen.X = Val(Var_Get(t, "SHOP", "ScreenX"))
           .Screen.Y = Val(Var_Get(t, "SHOP", "ScreenY"))
       Else
           .Screen.X = Val(Var_Get(s, "SHOP", "ScreenX"))
           .Screen.Y = Val(Var_Get(s, "SHOP", "ScreenY"))
       End If
       Engine_Init_Grh .SkinGrh, Val(Var_Get(s, "SHOP", "Grh"))
   End With
   
   'Load bank window
   GameWindow.Bank = GameWindow.Inventory
   With GameWindow.Bank
       If LoadCustomPos Then
           .Screen.X = Val(Var_Get(t, "BANK", "ScreenX"))
           .Screen.Y = Val(Var_Get(t, "BANK", "ScreenY"))
       Else
           .Screen.X = Val(Var_Get(s, "BANK", "ScreenX"))
           .Screen.Y = Val(Var_Get(s, "BANK", "ScreenY"))
       End If
       Engine_Init_Grh .SkinGrh, Val(Var_Get(s, "BANK", "Grh"))
   End With
   'Load Mailbox window
   With GameWindow.Mailbox.Screen
       If LoadCustomPos Then
           .X = Val(Var_Get(t, "MAILBOX", "ScreenX"))
           .Y = Val(Var_Get(t, "MAILBOX", "ScreenY"))
       Else
           .X = Val(Var_Get(s, "MAILBOX", "ScreenX"))
           .Y = Val(Var_Get(s, "MAILBOX", "ScreenY"))
       End If
       .Width = Val(Var_Get(s, "MAILBOX", "ScreenWidth"))
       .Height = Val(Var_Get(s, "MAILBOX", "ScreenHeight"))
   End With
   Engine_Init_Grh GameWindow.Mailbox.SkinGrh, Val(Var_Get(s, "MAILBOX", "Grh"))
   With GameWindow.Mailbox.WriteLbl
       .X = Val(Var_Get(s, "MAILBOX", "WriteMessageX"))
       .Y = Val(Var_Get(s, "MAILBOX", "WriteMessageY"))
       .Width = Val(Var_Get(s, "MAILBOX", "WriteMessageWidth"))
       .Height = Val(Var_Get(s, "MAILBOX", "WriteMessageHeight"))
   End With
   With GameWindow.Mailbox.DeleteLbl
       .X = Val(Var_Get(s, "MAILBOX", "DeleteMessageX"))
       .Y = Val(Var_Get(s, "MAILBOX", "DeleteMessageY"))
       .Width = Val(Var_Get(s, "MAILBOX", "DeleteMessageWidth"))
       .Height = Val(Var_Get(s, "MAILBOX", "DeleteMessageHeight"))
   End With
   With GameWindow.Mailbox.ReadLbl
       .X = Val(Var_Get(s, "MAILBOX", "ReadMessageX"))
       .Y = Val(Var_Get(s, "MAILBOX", "ReadMessageY"))
       .Width = Val(Var_Get(s, "MAILBOX", "ReadMessageWidth"))
       .Height = Val(Var_Get(s, "MAILBOX", "ReadMessageHeight"))
   End With
   With GameWindow.Mailbox.List
       .X = Val(Var_Get(s, "MAILBOX", "ListX"))
       .Y = Val(Var_Get(s, "MAILBOX", "ListY"))
       .Width = Val(Var_Get(s, "MAILBOX", "ListWidth"))
       .Height = Val(Var_Get(s, "MAILBOX", "ListHeight"))
   End With
   'Load View Message window
   With GameWindow.ViewMessage.Screen
       If LoadCustomPos Then
           .X = Val(Var_Get(t, "VIEWMESSAGE", "ScreenX"))
           .Y = Val(Var_Get(t, "VIEWMESSAGE", "ScreenY"))
       Else
           .X = Val(Var_Get(s, "VIEWMESSAGE", "ScreenX"))
           .Y = Val(Var_Get(s, "VIEWMESSAGE", "ScreenY"))
       End If
       .Width = Val(Var_Get(s, "VIEWMESSAGE", "ScreenWidth"))
       .Height = Val(Var_Get(s, "VIEWMESSAGE", "ScreenHeight"))
   End With
   Engine_Init_Grh GameWindow.ViewMessage.SkinGrh, Val(Var_Get(s, "VIEWMESSAGE", "Grh"))
   With GameWindow.ViewMessage.From
       .X = Val(Var_Get(s, "VIEWMESSAGE", "FromX"))
       .Y = Val(Var_Get(s, "VIEWMESSAGE", "FromY"))
       .Width = Val(Var_Get(s, "VIEWMESSAGE", "FromWidth"))
       .Height = Val(Var_Get(s, "VIEWMESSAGE", "FromHeight"))
   End With
   With GameWindow.ViewMessage.Subject
       .X = Val(Var_Get(s, "VIEWMESSAGE", "SubjectX"))
       .Y = Val(Var_Get(s, "VIEWMESSAGE", "SubjectY"))
       .Width = Val(Var_Get(s, "VIEWMESSAGE", "SubjectWidth"))
       .Height = Val(Var_Get(s, "VIEWMESSAGE", "SubjectHeight"))
   End With
   With GameWindow.ViewMessage.Message
       .X = Val(Var_Get(s, "VIEWMESSAGE", "MessageX"))
       .Y = Val(Var_Get(s, "VIEWMESSAGE", "MessageY"))
       .Width = Val(Var_Get(s, "VIEWMESSAGE", "MessageWidth"))
       .Height = Val(Var_Get(s, "VIEWMESSAGE", "MessageHeight"))
   End With
   ImageOffsetX = Val(Var_Get(s, "VIEWMESSAGE", "ImageOffsetX"))
   ImageOffsetY = Val(Var_Get(s, "VIEWMESSAGE", "ImageOffsetY"))
   ImageSpaceX = Val(Var_Get(s, "VIEWMESSAGE", "ImageSpaceX"))
   For LoopC = 1 To MaxMailObjs
       With GameWindow.ViewMessage.Image(LoopC)
           .X = ImageOffsetX + ((LoopC - 1) * (ImageSpaceX + 32))
           .Y = ImageOffsetY
           .Width = 32
           .Height = 32
       End With
   Next LoopC
   'Load Write Message window
   GameWindow.WriteMessage = GameWindow.ViewMessage
   With GameWindow.WriteMessage.Screen
       If LoadCustomPos Then
           .X = Val(Var_Get(t, "WRITEMESSAGE", "ScreenX"))
           .Y = Val(Var_Get(t, "WRITEMESSAGE", "ScreenY"))
       Else
           .X = Val(Var_Get(s, "WRITEMESSAGE", "ScreenX"))
           .Y = Val(Var_Get(s, "WRITEMESSAGE", "ScreenY"))
       End If
   End With
   Engine_Init_Grh GameWindow.WriteMessage.SkinGrh, Val(Var_Get(s, "WRITEMESSAGE", "Grh"))
   'Load Amount window
   With GameWindow.Amount.Screen
       If LoadCustomPos Then
           .X = Val(Var_Get(t, "AMOUNT", "ScreenX"))
           .Y = Val(Var_Get(t, "AMOUNT", "ScreenY"))
       Else
           .X = Val(Var_Get(s, "AMOUNT", "ScreenX"))
           .Y = Val(Var_Get(s, "AMOUNT", "ScreenY"))
       End If
       .Width = Val(Var_Get(s, "AMOUNT", "ScreenWidth"))
       .Height = Val(Var_Get(s, "AMOUNT", "ScreenHeight"))
   End With
   Engine_Init_Grh GameWindow.Amount.SkinGrh, Val(Var_Get(s, "AMOUNT", "Grh"))
   With GameWindow.Amount.Value
       .X = Val(Var_Get(s, "AMOUNT", "ValueX"))
       .Y = Val(Var_Get(s, "AMOUNT", "ValueY"))
       .Width = Val(Var_Get(s, "AMOUNT", "ValueWidth"))
       .Height = Val(Var_Get(s, "AMOUNT", "ValueHeight"))
   End With
   'Load Menu Window
   With GameWindow.Menu.Screen
       If LoadCustomPos Then
           .X = Val(Var_Get(t, "MENU", "ScreenX"))
           .Y = Val(Var_Get(t, "MENU", "ScreenY"))
       Else
           .X = Val(Var_Get(s, "MENU", "ScreenX"))
           .Y = Val(Var_Get(s, "MENU", "ScreenY"))
       End If
       .Width = Val(Var_Get(s, "MENU", "ScreenWidth"))
       .Height = Val(Var_Get(s, "MENU", "ScreenHeight"))
   End With
   Engine_Init_Grh GameWindow.Menu.SkinGrh, Val(Var_Get(s, "MENU", "Grh"))
   With GameWindow.Menu.QuitLbl
       .X = Val(Var_Get(s, "MENU", "QuitX"))
       .Y = Val(Var_Get(s, "MENU", "QuitY"))
       .Width = Val(Var_Get(s, "MENU", "QuitWidth"))
       .Height = Val(Var_Get(s, "MENU", "QuitHeight"))
   End With
   
   'Load the NPC Chat window
   With GameWindow.NPCChat.Screen
       .X = Val(Var_Get(s, "NPCCHAT", "ScreenX"))
       .Y = Val(Var_Get(s, "NPCCHAT", "ScreenY"))
       .Width = Val(Var_Get(s, "NPCCHAT", "ScreenWidth"))
       .Height = Val(Var_Get(s, "NPCCHAT", "ScreenHeight"))
   End With
   Engine_Init_Grh GameWindow.NPCChat.SkinGrh, Val(Var_Get(s, "NPCCHAT", "Grh"))
   
   'Load the trade window
   With GameWindow.Trade
       .Screen.X = Val(Var_Get(s, "TRADE", "ScreenX"))
       .Screen.Y = Val(Var_Get(s, "TRADE", "ScreenY"))
       .Screen.Width = Val(Var_Get(s, "TRADE", "ScreenWidth"))
       .Screen.Height = Val(Var_Get(s, "TRADE", "ScreenHeight"))
       
       .User1Name.X = Val(Var_Get(s, "TRADE", "User1NameX"))
       .User1Name.Y = Val(Var_Get(s, "TRADE", "User1NameY"))
       
       .User2Name.X = Val(Var_Get(s, "TRADE", "User2NameX"))
       .User2Name.Y = Val(Var_Get(s, "TRADE", "User2NameY"))
       
       .Accept.X = Val(Var_Get(s, "TRADE", "AcceptX"))
       .Accept.Y = Val(Var_Get(s, "TRADE", "AcceptY"))
       .Accept.Width = Val(Var_Get(s, "TRADE", "AcceptWidth"))
       .Accept.Height = Val(Var_Get(s, "TRADE", "AcceptHeight"))
       
       .Trade.X = Val(Var_Get(s, "TRADE", "TradeX"))
       .Trade.Y = Val(Var_Get(s, "TRADE", "TradeY"))
       .Trade.Width = Val(Var_Get(s, "TRADE", "TradeWidth"))
       .Trade.Height = Val(Var_Get(s, "TRADE", "TradeHeight"))
       
       .Cancel.X = Val(Var_Get(s, "TRADE", "CancelX"))
       .Cancel.Y = Val(Var_Get(s, "TRADE", "CancelY"))
       .Cancel.Width = Val(Var_Get(s, "TRADE", "CancelWidth"))
       .Cancel.Height = Val(Var_Get(s, "TRADE", "CancelHeight"))
       
       .Gold1.X = Val(Var_Get(s, "TRADE", "Gold1X"))
       .Gold1.Y = Val(Var_Get(s, "TRADE", "gold1Y"))
       
       .Gold2.X = Val(Var_Get(s, "TRADE", "Gold2X"))
       .Gold2.Y = Val(Var_Get(s, "TRADE", "gold2Y"))
       
       ImageOffsetX = Val(Var_Get(s, "TRADE", "Sec1X"))
       ImageOffsetY = Val(Var_Get(s, "TRADE", "Sec1Y"))
       ImageSpaceX = Val(Var_Get(s, "TRADE", "DividerSize"))
       X = 0
       Y = 0
       
       For LoopC = 1 To 9
           .Trade1(LoopC).X = ImageOffsetX + (X * (ImageSpaceX + 32))
           .Trade1(LoopC).Y = ImageOffsetY + (Y * (ImageSpaceX + 32))
           .Trade1(LoopC).Width = 32
           .Trade1(LoopC).Height = 32
           X = X + 1
           If X = 3 Then
               X = 0
               Y = Y + 1
           End If
       Next LoopC
       ImageOffsetX = Val(Var_Get(s, "TRADE", "Sec2X"))
       ImageOffsetY = Val(Var_Get(s, "TRADE", "Sec2Y"))
       X = 0
       Y = 0
       For LoopC = 1 To 9
           .Trade2(LoopC).X = ImageOffsetX + (X * (ImageSpaceX + 32))
           .Trade2(LoopC).Y = ImageOffsetY + (Y * (ImageSpaceX + 32))
           .Trade2(LoopC).Width = 32
           .Trade2(LoopC).Height = 32
           X = X + 1
           If X = 3 Then
               X = 0
               Y = Y + 1
           End If
       Next LoopC
   
   
   End With
   Engine_Init_Grh GameWindow.Trade.SkinGrh, Val(Var_Get(s, "TRADE", "Grh"))
   
   'Reset text position
   If CurMap > 0 Then Engine_UpdateChatArray

End Sub

Sub Engine_Init_HairData() '***************************************************************** 'Loads Hair.dat 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_HairData '***************************************************************** Dim LoopC As Long Dim i As Integer

   'Get Number of hairs
   NumHairs = CLng(Var_Get(DataPath & "Hair.dat", "INIT", "NumHairs"))
   
   'Resize array
   ReDim HairData(0 To NumHairs) As HairData
   
   'Fill List
   For LoopC = 1 To NumHairs
       For i = 1 To 8
           Engine_Init_Grh HairData(LoopC).Hair(i), CLng(Var_Get(DataPath & "Hair.dat", Str$(LoopC), Str$(i))), 0
       Next i
   Next LoopC

End Sub

Sub Engine_Init_HeadData() '***************************************************************** 'Loads Head.dat 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_HeadData '***************************************************************** Dim LoopC As Long Dim i As Integer

   'Get Number of heads
   NumHeads = CLng(Var_Get(DataPath & "Head.dat", "INIT", "NumHeads"))
   
   'Resize array
   ReDim HeadData(0 To NumHeads) As HeadData
   
   'Fill List
   For LoopC = 1 To NumHeads
       For i = 1 To 8
           Engine_Init_Grh HeadData(LoopC).Head(i), CLng(Var_Get(DataPath & "Head.dat", LoopC, i)), 0
           Engine_Init_Grh HeadData(LoopC).Blink(i), CLng(Var_Get(DataPath & "Head.dat", LoopC, "b" & i)), 0
           Engine_Init_Grh HeadData(LoopC).AgrHead(i), CLng(Var_Get(DataPath & "Head.dat", LoopC, "a" & i)), 0
           Engine_Init_Grh HeadData(LoopC).AgrBlink(i), CLng(Var_Get(DataPath & "Head.dat", LoopC, "ab" & i)), 0
       Next i
   Next LoopC

End Sub

Public Sub Engine_Init_NPCChat(ByVal Language As String) '***************************************************************** 'Loads the NPC messages according to the language 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_NPCChat '***************************************************************** Dim Conditions() As NPCChatLineCondition Dim NumConditions As Byte 'The number of conditions Dim ConditionFlags As Long 'States what conditions are currently used (so we don't have to loop through the Conditions() array) Dim ChatLine As Byte 'The chat line for the current index Dim ErrTxt As String 'If there is an error, this extra text is added Dim HighIndex As Long 'Highest index retrieved Dim Index As Long 'Current index Dim FileNum As Byte Dim ln As String 'Used to grab our lines Dim Style As Byte 'Style used for the current index Dim TempSplit() As String Dim i As Long Dim F As Long

Dim AskIndex As Byte Dim HighAskIndex As Long Dim AnswerIndex As Byte Dim ln2 As String

   On Error GoTo ErrOut
   'Make sure the file exists
   If Not Engine_FileExist(DataPath & "NPC Chat\" & LCase$(Language) & ".ini", vbNormal) Then
       
       'Error! Change to English before we die!
       Language = "english"
   
   Else
   
       'Load English first, in case any messages are missing from the other language
       If Left$(LCase$(Language), 3) <> "eng" Then Engine_Init_NPCChat "english"
       'Set the initial high-index (to preserve messages from English in case any are missing in the foreign language)
       On Error Resume Next
       HighIndex = UBound(NPCChat)
       On Error GoTo 0
   End If
   
   'Open the file
   FileNum = FreeFile
   Open DataPath & "NPC Chat\" & LCase$(Language) & ".ini" For Input Access Read As FileNum
       
       'Loop until we reach the BEGINFILE line, stating the data is going to start coming in
       Do
           Line Input #FileNum, ln
       Loop While UCase$(Left$(ln, 9)) <> "BEGINFILE"
       
       'Loop through the data
       Do
       
           'Get the line
           Line Input #FileNum, ln
           ln = Trim$(ln)
           
           'Look for empty lines
           If LenB(ln) = 0 Then GoTo NextLine
           
           '*** Look for a new index ***
           If Left$(ln, 1) = "[" Then
               
               'Grab the index
               Index = Mid$(ln, 2, Len(ln) - 2)
               
               'Clear the variables from the last line
               Style = 0
               ChatLine = 0
               Erase Conditions
               NumConditions = 0
               ConditionFlags = 0
               HighAskIndex = 0
               'Resize the chat array according to the index if needed
               If Index > HighIndex Then
                   ReDim Preserve NPCChat(1 To Index)
                   HighIndex = Index
               End If
               
               'Grab the format - this little loop will help us ignore blank lines
               Do
                   Line Input #FileNum, ln
               Loop While LenB(Trim$(ln)) = 0
               
               'Format text not found!
               If UCase$(Left$(ln, 6)) <> "FORMAT" Then
                   ErrTxt = "FORMAT not found immediately after index ([x]) tag!"
                   GoTo ErrOut
               End If
               
               'Figure out what format it is
               ln = Trim$(ln)
               Select Case UCase$(Right$(ln, Len(ln) - 7))
                   Case "RANDOM"
                       NPCChat(Index).Format = NPCCHAT_FORMAT_RANDOM
                   Case "LINEAR"
                       NPCChat(Index).Format = NPCCHAT_FORMAT_LINEAR
                   Case Else
                       ErrTxt = "Unknown format " & UCase$(Right$(ln, Len(ln) - 7)) & " retrieved!"
                       GoTo ErrOut
               End Select
               GoTo NextLine
               
           End If
           
           '*** Look for a new style ***
           If UCase$(Left$(ln, 6)) = "STYLE " Then
           
               'Figure out what style it is
               ln = Trim$(ln)
               Select Case UCase$(Right$(ln, Len(ln) - 6))
                   Case "BUBBLE"
                       Style = NPCCHAT_STYLE_BUBBLE
                   Case "BOX"
                       Style = NPCCHAT_STYLE_BOX
                   Case "BOTH"
                       Style = NPCCHAT_STYLE_BOTH
                   Case Else
                       ErrTxt = "Unknown style " & UCase$(Right$(ln, Len(ln) - 6)) & " retrieved!"
                       GoTo ErrOut
               End Select
               
           End If
           '*** Look for a new condition ***
           If Left$(ln, 1) = "!" Then
               
               'Figure out what condition it is
               ln = Trim$(ln)  'Trim off spaces
               TempSplit = Split(UCase$(Right$(ln, Len(ln) - 1)), " ") 'Remove the ! and turn to uppercase
               Select Case UCase$(TempSplit(0))
                   Case "CLEAR"
                       Erase Conditions
                       NumConditions = 0
                       ConditionFlags = 0
                   Case "LEVELLESSTHAN"
                       If Not ConditionFlags And NPCCHAT_COND_LEVELLESSTHAN Then
                           NumConditions = NumConditions + 1
                           ReDim Preserve Conditions(1 To NumConditions)
                           Conditions(NumConditions).Condition = NPCCHAT_COND_LEVELLESSTHAN
                           Conditions(NumConditions).Value = Val(TempSplit(1))
                           ConditionFlags = ConditionFlags Or NPCCHAT_COND_LEVELLESSTHAN
                       Else
                           For F = 1 To NumConditions
                               If Conditions(F).Condition = NPCCHAT_COND_LEVELLESSTHAN Then
                                   Conditions(F).Value = Val(TempSplit(1))
                                   Exit For
                               End If
                           Next F
                       End If
                   Case "LEVELMORETHAN"
                       If Not ConditionFlags And NPCCHAT_COND_LEVELMORETHAN Then
                           NumConditions = NumConditions + 1
                           ReDim Preserve Conditions(1 To NumConditions)
                           Conditions(NumConditions).Condition = NPCCHAT_COND_LEVELMORETHAN
                           Conditions(NumConditions).Value = Val(TempSplit(1))
                           ConditionFlags = ConditionFlags Or NPCCHAT_COND_LEVELMORETHAN
                       Else
                           For F = 1 To NumConditions
                               If Conditions(F).Condition = NPCCHAT_COND_LEVELMORETHAN Then
                                   Conditions(F).Value = Val(TempSplit(1))
                                   Exit For
                               End If
                           Next F
                       End If
                   Case "HPLESSTHAN"
                       If Not ConditionFlags And NPCCHAT_COND_HPLESSTHAN Then
                           NumConditions = NumConditions + 1
                           ReDim Preserve Conditions(1 To NumConditions)
                           Conditions(NumConditions).Condition = NPCCHAT_COND_HPLESSTHAN
                           Conditions(NumConditions).Value = Val(TempSplit(1))
                           ConditionFlags = ConditionFlags Or NPCCHAT_COND_HPLESSTHAN
                       Else
                           For F = 1 To NumConditions
                               If Conditions(F).Condition = NPCCHAT_COND_HPLESSTHAN Then
                                   Conditions(F).Value = Val(TempSplit(1))
                                   Exit For
                               End If
                           Next F
                       End If
                   Case "HPMORETHAN"
                       If Not ConditionFlags And NPCCHAT_COND_HPMORETHAN Then
                           NumConditions = NumConditions + 1
                           ReDim Preserve Conditions(1 To NumConditions)
                           Conditions(NumConditions).Condition = NPCCHAT_COND_HPMORETHAN
                           Conditions(NumConditions).Value = Val(TempSplit(1))
                           ConditionFlags = ConditionFlags Or NPCCHAT_COND_HPMORETHAN
                       Else
                           For F = 1 To NumConditions
                               If Conditions(F).Condition = NPCCHAT_COND_HPMORETHAN Then
                                   Conditions(F).Value = Val(TempSplit(1))
                                   Exit For
                               End If
                           Next F
                       End If
                   Case "KNOWSKILL"
                       If Not ConditionFlags And NPCCHAT_COND_KNOWSKILL Then
                           NumConditions = NumConditions + 1
                           ReDim Preserve Conditions(1 To NumConditions)
                           Conditions(NumConditions).Condition = NPCCHAT_COND_KNOWSKILL
                           Conditions(NumConditions).Value = Val(TempSplit(1))
                           ConditionFlags = ConditionFlags Or NPCCHAT_COND_KNOWSKILL
                       Else
                           For F = 1 To NumConditions
                               If Conditions(F).Condition = NPCCHAT_COND_KNOWSKILL Then
                                   Conditions(F).Value = Val(TempSplit(1))
                                   Exit For
                               End If
                           Next F
                       End If
                   Case "DONTKNOWSKILL"
                       If Not ConditionFlags And NPCCHAT_COND_DONTKNOWSKILL Then
                           NumConditions = NumConditions + 1
                           ReDim Preserve Conditions(1 To NumConditions)
                           Conditions(NumConditions).Condition = NPCCHAT_COND_DONTKNOWSKILL
                           Conditions(NumConditions).Value = Val(TempSplit(1))
                           ConditionFlags = ConditionFlags Or NPCCHAT_COND_DONTKNOWSKILL
                       Else
                           For F = 1 To NumConditions
                               If Conditions(F).Condition = NPCCHAT_COND_DONTKNOWSKILL Then
                                   Conditions(F).Value = Val(TempSplit(1))
                                   Exit For
                               End If
                           Next F
                       End If
                   Case "SAY"
                       If Not ConditionFlags And NPCCHAT_COND_SAY Then
                           NumConditions = NumConditions + 1
                           ReDim Preserve Conditions(1 To NumConditions)
                           Conditions(NumConditions).Condition = NPCCHAT_COND_SAY  'Notice we UCase$() the next line - this is so we can ignore the case
                           Conditions(NumConditions).ValueStr = UCase$(Replace$(TempSplit(1), "_", " "))   'Replace underscores with spaces
                           ConditionFlags = ConditionFlags Or NPCCHAT_COND_SAY
                       Else
                           For F = 1 To NumConditions
                               If Conditions(F).Condition = NPCCHAT_COND_SAY Then
                                   Conditions(F).ValueStr = UCase$(Replace$(TempSplit(1), "_", " "))
                                   Exit For
                               End If
                           Next F
                       End If
                   Case Else
                       ErrTxt = "Unknown condition " & TempSplit(0) & " retrieved!"
                       GoTo ErrOut
               End Select
               
           End If
           
           '*** Look for a chat line ***
           If UCase$(Left$(ln, 4)) = "SAY " Then
               
               'Split up the information (0 = "SAY", 1 = Delay, 2 = Chat text)
               TempSplit() = Split(ln, " ", 3)
               
               'Raise the lines count
               ChatLine = ChatLine + 1
               ReDim Preserve NPCChat(Index).ChatLine(1 To ChatLine)
               NPCChat(Index).NumLines = ChatLine
               
               'Set the delay, style and text
               NPCChat(Index).ChatLine(ChatLine).Delay = Val(TempSplit(1))
               NPCChat(Index).ChatLine(ChatLine).Text = Replace$(Trim$(TempSplit(2)), "/r", vbNewLine)
               NPCChat(Index).ChatLine(ChatLine).Style = Style
               
               'Check for empty text lines
               If UCase$(NPCChat(Index).ChatLine(ChatLine).Text) = "[EMPTY]" Then
                   NPCChat(Index).ChatLine(ChatLine).Text = vbNullString
               End If
               
               'Set the conditions
               NPCChat(Index).ChatLine(ChatLine).NumConditions = NumConditions
               If NumConditions > 0 Then
                   ReDim NPCChat(Index).ChatLine(ChatLine).Conditions(1 To NumConditions)
                   For i = 1 To NumConditions
                       NPCChat(Index).ChatLine(ChatLine).Conditions(i) = Conditions(i)
                   Next i
               End If
               
           End If
           
       '*** Look for a STARTASK line ***
       If UCase$(Left$(ln, 9)) = "STARTASK " Then
           NPCChat(Index).Ask.StartAsk = Val(Right$(ln, Len(ln) - 9))
           If NPCChat(Index).Ask.StartAsk <= 0 Then
               ErrTxt = "STARTASK is <= 0"
               GoTo ErrOut
           End If
       End If
       
       '*** Look for an ASK line ***
       If UCase$(Left$(ln, 4)) = "ASK " Then
           
           'Split up the information (0 = "ASK", 1 = ID, 2 = Question text)
           TempSplit() = Split(ln, " ", 3)
           
           'Update the ask information
           AskIndex = Val(TempSplit(1))
           If HighAskIndex < AskIndex Then
               HighAskIndex = AskIndex
               ReDim Preserve NPCChat(Index).Ask.Ask(1 To AskIndex)
               NPCChat(Index).Ask.Ask(AskIndex).Question = Replace$(Trim$(TempSplit(2)), "/r", vbNewLine)
           End If
           'Get the answers
           AnswerIndex = 0
           Do
               Line Input #FileNum, ln2
               ln2 = Trim$(ln2)
               If ln2 <> vbNullString Then
                   If UCase$(Left$(ln2, 6)) = "ASKEND" Then Exit Do
                   If UCase$(Left$(ln2, 7)) = "ANSWER " Then
                       TempSplit() = Split(ln2, " ", 3)
                       If UBound(TempSplit) < 2 Then
                           ErrTxt = "Invalid number of ANSWER parameters!" & """ & ln2 & """
                           GoTo ErrOut
                       End If
                       AnswerIndex = AnswerIndex + 1
                       With NPCChat(Index).Ask.Ask(AskIndex)
                           .NumAnswers = AnswerIndex
                           ReDim Preserve .Answer(1 To AnswerIndex)
                           .Answer(AnswerIndex).Text = Trim$(TempSplit(2))
                           .Answer(AnswerIndex).GotoID = Val(TempSplit(1))
                       End With
                   Else
                       ErrTxt = "Unknown command in ASK block!" & vbNewLine & """ ln2 & """
                       GoTo ErrOut
                   End If
               End If
           Loop
           
       End If

NextLine:

       Loop While Not EOF(FileNum)
   
   Close #FileNum
   
   Exit Sub
   

ErrOut:

   MsgBox "Error in NPCChat routine! Stopped on line " & Loc(FileNum) & "!" & vbNewLine & _
           "The remainder of the line text is: " & vbNewLine & ln & vbNewLine & vbNewLine & _
           "The following message has been added:" & vbNewLine & ErrTxt, vbOKOnly Or vbCritical
           
   If FileNum > 0 Then Close #FileNum
   

End Sub

Sub Engine_Init_ParticleEngine(Optional ByVal SkipToTextures As Boolean = False) '***************************************************************** 'Loads all particles into memory - unlike normal textures, these stay in memory. This isn't 'done for any reason in particular, they just use so little memory since they are so small 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_ParticleEngine '***************************************************************** Dim i As Byte

   If Not SkipToTextures Then
   
       'Set the particles texture
       NumEffects = Var_Get(DataPath & "Game.ini", "INIT", "NumEffects")
       ReDim Effect(1 To NumEffects)
   
   End If
   
   For i = 1 To UBound(ParticleTexture())
       If ParticleTexture(i) Is Nothing Then Set ParticleTexture(i) = Nothing
       Set ParticleTexture(i) = D3DX.CreateTextureFromFileEx(D3DDevice, GrhPath & "p" & i & ".png", D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_FILTER_POINT, D3DX_FILTER_POINT, &HFF000000, ByVal 0, ByVal 0)
   Next i

End Sub

Private Sub Engine_Init_RenderStates() '************************************************************ 'Set the render states of the Direct3D Device 'This is in a seperate sub since if using Fullscreen and device is lost 'this is eventually called to restore settings. 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_RenderStates '************************************************************

   With D3DDevice
       
       'Set the shader to be used
       D3DDevice.SetVertexShader FVF
   
       'Set the render states
       .SetRenderState D3DRS_LIGHTING, False
       .SetRenderState D3DRS_SRCBLEND, D3DBLEND_SRCALPHA
       .SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA
       .SetRenderState D3DRS_ALPHABLENDENABLE, True
       .SetRenderState D3DRS_FILLMODE, D3DFILL_SOLID
       .SetRenderState D3DRS_CULLMODE, D3DCULL_NONE
       .SetRenderState D3DRS_ZENABLE, False
       .SetRenderState D3DRS_ZWRITEENABLE, False
       .SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE
       'Particle engine settings
       .SetRenderState D3DRS_POINTSPRITE_ENABLE, 1
       .SetRenderState D3DRS_POINTSCALE_ENABLE, 0
   
       'Set the texture stage stats (filters)
       .SetTextureStageState 0, D3DTSS_MAGFILTER, D3DTEXF_POINT
       .SetTextureStageState 0, D3DTSS_MINFILTER, D3DTEXF_POINT
       
   End With

End Sub

Sub Engine_Init_Texture(ByVal TextureNum As Integer) '***************************************************************** 'Loads a texture into memory 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_Texture '***************************************************************** Dim UseTextureFormat As CONST_D3DFORMAT Dim TexInfo As D3DXIMAGE_INFO_A Dim FilePath As String

   'Check for a valid texture
   If TextureNum < 1 Then Exit Sub
   'Make sure we even need to load the texture
   If SurfaceTimer(TextureNum) > timeGetTime Then Exit Sub
   
   'Set the texture timer
   SurfaceTimer(TextureNum) = timeGetTime + SurfaceTimerMax
   'Check if we have the device
   If D3DDevice.TestCooperativeLevel <> D3D_OK Then Exit Sub
   'Make sure we try not to load a file while the engine is unloading
   If IsUnloading Then Exit Sub
   'Get the path
   FilePath = GrhPath & TextureNum & ".png"
   
   'Check if the texture exists
   If Engine_FileExist(FilePath, vbNormal) = False Then
       MsgBox "Error! Could not find the following texture file:" & vbNewLine & FilePath, vbOKOnly
       IsUnloading = 1
       Exit Sub
   End If
   
   If SurfaceSize(TextureNum).X = 0 Then   'We need to get the size
   
       'Set the texture (and get the dimensions)
       Set SurfaceDB(TextureNum) = D3DX.CreateTextureFromFileEx(D3DDevice, FilePath, D3DX_DEFAULT, D3DX_DEFAULT, 0, 0, TextureCompress, D3DPOOL_MANAGED, D3DX_FILTER_POINT, D3DX_FILTER_NONE, &HFF000000, TexInfo, ByVal 0)
       SurfaceSize(TextureNum).X = TexInfo.Width
       SurfaceSize(TextureNum).Y = TexInfo.Height
       
   Else
       
       'Set the texture (without getting the dimensions)
       Set SurfaceDB(TextureNum) = D3DX.CreateTextureFromFileEx(D3DDevice, FilePath, SurfaceSize(TextureNum).X, SurfaceSize(TextureNum).Y, 0, 0, TextureCompress, D3DPOOL_MANAGED, D3DX_FILTER_POINT, D3DX_FILTER_NONE, &HFF000000, ByVal 0, ByVal 0)
   
   End If

End Sub

Sub Engine_Init_FontTextures() '***************************************************************** 'Init the custom font textures 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_FontTextures '***************************************************************** Dim TexInfo As D3DXIMAGE_INFO_A

   'Check if we have the device
   If D3DDevice.TestCooperativeLevel <> D3D_OK Then Exit Sub
   '*** Default font ***
   
   'Set the texture
   Set Font_Default.Texture = D3DX.CreateTextureFromFileEx(D3DDevice, DataPath & "texdefault.png", D3DX_DEFAULT, D3DX_DEFAULT, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_FILTER_POINT, D3DX_FILTER_NONE, &HFF000000, TexInfo, ByVal 0)
   
   'Store the size of the texture
   Font_Default.TextureSize.X = TexInfo.Width
   Font_Default.TextureSize.Y = TexInfo.Height

End Sub

Sub Engine_Init_FontSettings() '***************************************************************** 'Init the custom font settings 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_FontSettings '***************************************************************** Dim FileNum As Byte Dim LoopChar As Long Dim Row As Single Dim u As Single Dim v As Single

   '*** Default font ***
   'Load the header information
   FileNum = FreeFile
   Open DataPath & "texdefault.dat" For Binary As #FileNum
       Get #FileNum, , Font_Default.HeaderInfo
   Close #FileNum
   
   'Calculate some common values
   Font_Default.CharHeight = Font_Default.HeaderInfo.CellHeight - 4
   Font_Default.RowPitch = Font_Default.HeaderInfo.BitmapWidth \ Font_Default.HeaderInfo.CellWidth
   Font_Default.ColFactor = Font_Default.HeaderInfo.CellWidth / Font_Default.HeaderInfo.BitmapWidth
   Font_Default.RowFactor = Font_Default.HeaderInfo.CellHeight / Font_Default.HeaderInfo.BitmapHeight
   
   'Cache the verticies used to draw the character (only requires setting the color and adding to the X/Y values)
   For LoopChar = 0 To 255
       
       'tU and tV value (basically tU = BitmapXPosition / BitmapWidth, and height for tV)
       Row = (LoopChar - Font_Default.HeaderInfo.BaseCharOffset) \ Font_Default.RowPitch
       u = ((LoopChar - Font_Default.HeaderInfo.BaseCharOffset) - (Row * Font_Default.RowPitch)) * Font_Default.ColFactor
       v = Row * Font_Default.RowFactor
       'Set the verticies
       With Font_Default.HeaderInfo.CharVA(LoopChar)
           .Vertex(0).Color = D3DColorARGB(255, 0, 0, 0)   'Black is the most common color
           .Vertex(0).Rhw = 1
           .Vertex(0).tU = u
           .Vertex(0).tV = v
           .Vertex(0).X = 0
           .Vertex(0).Y = 0
           .Vertex(0).Z = 0
           
           .Vertex(1).Color = D3DColorARGB(255, 0, 0, 0)
           .Vertex(1).Rhw = 1
           .Vertex(1).tU = u + Font_Default.ColFactor
           .Vertex(1).tV = v
           .Vertex(1).X = Font_Default.HeaderInfo.CellWidth
           .Vertex(1).Y = 0
           .Vertex(1).Z = 0
           
           .Vertex(2).Color = D3DColorARGB(255, 0, 0, 0)
           .Vertex(2).Rhw = 1
           .Vertex(2).tU = u
           .Vertex(2).tV = v + Font_Default.RowFactor
           .Vertex(2).X = 0
           .Vertex(2).Y = Font_Default.HeaderInfo.CellHeight
           .Vertex(2).Z = 0
           
           .Vertex(3).Color = D3DColorARGB(255, 0, 0, 0)
           .Vertex(3).Rhw = 1
           .Vertex(3).tU = u + Font_Default.ColFactor
           .Vertex(3).tV = v + Font_Default.RowFactor
           .Vertex(3).X = Font_Default.HeaderInfo.CellWidth
           .Vertex(3).Y = Font_Default.HeaderInfo.CellHeight
           .Vertex(3).Z = 0
       End With
       
   Next LoopChar

End Sub

Public Sub Engine_Init_TileEngine() '***************************************************************** 'Init Tile Engine 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_TileEngine '***************************************************************** Dim t As Long

   'Size the form
   frmMain.Width = ScreenWidth * Screen.TwipsPerPixelX
   frmMain.Height = ScreenHeight * Screen.TwipsPerPixelY
   
   'Get some engine settings
   UseSfx = Val(Var_Get(DataPath & "Game.ini", "INIT", "UseSfx"))
   If UseSfx <> 0 Then UseSfx = 1      'Force to 1 or 0
   
   UseMusic = Val(Var_Get(DataPath & "Game.ini", "INIT", "UseMusic"))
   If UseMusic <> 0 Then UseMusic = 1  'Force to 1 or 0
   
   UseVSync = Val(Var_Get(DataPath & "Game.ini", "INIT", "VSync"))
   If UseVSync <> 0 Then UseVSync = 1  'Force to 1 or 0
   t = Val(Var_Get(DataPath & "Game.ini", "INIT", "Windowed"))
   If t = 0 Then Windowed = False Else Windowed = True
   
   ReverseSound = Val(Var_Get(DataPath & "Game.ini", "INIT", "ReverseSound"))
   If ReverseSound <> 0 Then ReverseSound = -1 Else ReverseSound = 1   'Force to -1 or 1
   
   TextureCompress = Val(Var_Get(DataPath & "Game.ini", "INIT", "TextureCompression"))
   If TextureCompress <> 0 Then TextureCompress = D3DFMT_DXT5  'Force to 0 or D3DFMT_DXT5
   
   Bit32 = Val(Var_Get(DataPath & "Game.ini", "INIT", "32bit"))
   If Bit32 <> 0 Then Bit32 = 1        'Force to 1 or 0
   
   FPSCap = Val(Var_Get(DataPath & "Game.ini", "INIT", "FPSCap"))
   If FPSCap < 0 Then FPSCap = 0
   If FPSCap > 0 Then FPSCap = 1000 \ FPSCap
   
   DisableChatBubbles = Val(Var_Get(DataPath & "Game.ini", "INIT", "DisableChatBubbles"))
   If DisableChatBubbles <> 0 Then DisableChatBubbles = 1        'Force to 1 or 0
   
   UseWeather = Val(Var_Get(DataPath & "Game.ini", "INIT", "UseWeather"))
   If UseWeather <> 0 Then UseWeather = 1
   
   UseMotionBlur = Val(Var_Get(DataPath & "Game.ini", "INIT", "UseMotionBlur"))
   If UseMotionBlur <> 0 Then UseMotionBlur = 1
   
   'Load the key definitions
   Input_Keys_LoadDefinitions
   
   '****** INIT DirectX ******
   ' Create the root D3D objects
   Set DX = New DirectX8
   Set D3D = DX.Direct3DCreate()
   Set D3DX = New D3DX8
   Input_Init
   Sound_Init
   'Create the D3D Device
   If Not Engine_Init_D3DDevice(D3DCREATE_PUREDEVICE) Then
       If Not Engine_Init_D3DDevice(D3DCREATE_HARDWARE_VERTEXPROCESSING) Then
           If Not Engine_Init_D3DDevice(D3DCREATE_MIXED_VERTEXPROCESSING) Then
               If Not Engine_Init_D3DDevice(D3DCREATE_SOFTWARE_VERTEXPROCESSING) Then
                   MsgBox "Could not init D3DDevice. Exiting..."
                   Engine_Init_UnloadTileEngine
                   Engine_UnloadAllForms
                   End
               End If
           End If
       End If
   End If
   Engine_Init_RenderStates
   
   'Load the rest of the tile engine stuff
   Engine_Init_FontTextures
   Engine_Init_ParticleEngine
   
   'Create the needed information for the motion bluring
   If UseMotionBlur Then
       Set DeviceBuffer = D3DDevice.GetRenderTarget
       Set DeviceStencil = D3DDevice.GetDepthStencilSurface
       Set BlurStencil = D3DDevice.CreateDepthStencilSurface(BufferWidth, BufferHeight, D3DFMT_D16, D3DMULTISAMPLE_NONE)
       Set BlurTexture = D3DX.CreateTexture(D3DDevice, BufferWidth, BufferHeight, 0, D3DUSAGE_RENDERTARGET, DispMode.Format, D3DPOOL_DEFAULT)
       Set BlurSurf = BlurTexture.GetSurfaceLevel(0)
       
       'Create the motion-blur vertex array
       For t = 0 To 3
           BlurTA(t).Color = D3DColorXRGB(255, 255, 255)
           BlurTA(t).Rhw = 1
       Next t
       BlurTA(1).X = ScreenWidth
       BlurTA(2).Y = ScreenHeight
       BlurTA(3).X = ScreenWidth
       BlurTA(3).Y = ScreenHeight
       
   End If
   'Set FPS value to 60 for startup
   FPS = 60
   FramesPerSecCounter = 60
   
   'Set the ending time to now (to prevent the client thinking there was a huge FPS jump)
   EndTime = timeGetTime
   'Get the AlternateRender flag
   AlternateRender = Val(Var_Get(DataPath & "Game.ini", "INIT", "AlternateRender"))
   AlternateRenderMap = Val(Var_Get(DataPath & "Game.ini", "INIT", "AlternateRenderMap"))
   AlternateRenderText = Val(Var_Get(DataPath & "Game.ini", "INIT", "AlternateRenderText"))
   If AlternateRender <> 0 Then AlternateRender = 1
   If AlternateRenderMap <> 0 Then AlternateRenderMap = 1
   If AlternateRenderText <> 0 Then AlternateRenderText = 1
   AlternateRenderDefault = AlternateRender
   
   'Set the blur to off
   BlurIntensity = 255
   If AlternateRender = 1 Or AlternateRenderMap = 1 Or AlternateRenderText = 1 Then
       'If using alternate rendering, create the sprite object
       Set Sprite = D3DX.CreateSprite(D3DDevice)
       
       'Set the scaling to default aspect ratio
       SpriteScaleVector.X = 1
       SpriteScaleVector.Y = 1
       
   End If
   
   'Start the engine
   EngineRun = True

End Sub

Public Sub Engine_Init_UnloadTileEngine() '***************************************************************** 'Shutsdown engine 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_UnloadTileEngine '***************************************************************** On Error Resume Next Dim LoopC As Long Dim X As Long Dim Y As Long

   EngineRun = False
   '****** Clear DirectX objects ******
   If Not DIDevice Is Nothing Then DIDevice.Unacquire
   If Not D3DDevice Is Nothing Then Set D3DDevice = Nothing
   If Not DIDevice Is Nothing Then Set DIDevice = Nothing
   If Not D3DX Is Nothing Then Set D3DX = Nothing
   If Not DI Is Nothing Then Set DI = Nothing
   'Clear particles
   For LoopC = 1 To UBound(ParticleTexture)
       If Not ParticleTexture(LoopC) Is Nothing Then Set ParticleTexture(LoopC) = Nothing
   Next LoopC
   'Clear GRH memory
   For LoopC = 1 To NumGrhFiles
       If Not SurfaceDB(LoopC) Is Nothing Then Set SurfaceDB(LoopC) = Nothing
   Next LoopC
   
   'Clear sound buffers
   For LoopC = 1 To NumSfx
       If Not DSBuffer(LoopC) Is Nothing Then Set DSBuffer(LoopC) = Nothing
   Next LoopC
   
   'Clear map sound buffers
   For X = 1 To MapInfo.Width
       For Y = 1 To MapInfo.Height
           If Not MapData(X, Y).Sfx Is Nothing Then Set MapData(X, Y).Sfx = Nothing
       Next Y
   Next X
   'Clear music objects
   For LoopC = 1 To NumMusicBuffers
       If Not DirectShow_Position(LoopC) Is Nothing Then Set DirectShow_Position(LoopC) = Nothing
       If Not DirectShow_Control(LoopC) Is Nothing Then Set DirectShow_Control(LoopC) = Nothing
       If Not DirectShow_Event(LoopC) Is Nothing Then Set DirectShow_Event(LoopC) = Nothing
       If Not DirectShow_Audio(LoopC) Is Nothing Then Set DirectShow_Audio(LoopC) = Nothing
   Next LoopC
   
   'Clear motion blur objects
   If Not BlurTexture Is Nothing Then
       Set BlurTexture = Nothing
       Set BlurSurf = Nothing
       Set BlurStencil = Nothing
       Set DeviceStencil = Nothing
       Set DeviceBuffer = Nothing
   End If
   
   'Clear arrays
   Erase BlurTA
   Erase SurfaceTimer
   Erase SoundBufferTimer
   Erase MapData
   Erase GrhData
   Erase GrhData
   Erase SurfaceSize
   Erase BodyData
   Erase HeadData
   Erase WeaponData
   Erase MapData
   Erase CharList
   Erase OBJList
   Erase BloodList
   Erase EffectList
   Erase DamageList
   Erase SkillList
   Erase QuickBarID
   Erase ShowGameWindow
   Erase SaveLightBuffer
   

End Sub

Sub Engine_Init_WeaponData() '***************************************************************** 'Loads Weapon.dat 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Init_WeaponData '***************************************************************** Dim LoopC As Long

   'Get number of weapons
   NumWeapons = CLng(Var_Get(DataPath & "Weapon.dat", "INIT", "NumWeapons"))
   
   'Resize array
   ReDim WeaponData(0 To NumWeapons) As WeaponData
   
   'Fill list
   For LoopC = 1 To NumWeapons
       Engine_Init_Grh WeaponData(LoopC).Walk(1), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Walk1")), 0
       Engine_Init_Grh WeaponData(LoopC).Walk(2), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Walk2")), 0
       Engine_Init_Grh WeaponData(LoopC).Walk(3), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Walk3")), 0
       Engine_Init_Grh WeaponData(LoopC).Walk(4), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Walk4")), 0
       Engine_Init_Grh WeaponData(LoopC).Walk(5), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Walk5")), 0
       Engine_Init_Grh WeaponData(LoopC).Walk(6), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Walk6")), 0
       Engine_Init_Grh WeaponData(LoopC).Walk(7), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Walk7")), 0
       Engine_Init_Grh WeaponData(LoopC).Walk(8), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Walk8")), 0
       Engine_Init_Grh WeaponData(LoopC).Attack(1), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Attack1")), 1
       Engine_Init_Grh WeaponData(LoopC).Attack(2), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Attack2")), 1
       Engine_Init_Grh WeaponData(LoopC).Attack(3), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Attack3")), 1
       Engine_Init_Grh WeaponData(LoopC).Attack(4), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Attack4")), 1
       Engine_Init_Grh WeaponData(LoopC).Attack(5), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Attack5")), 1
       Engine_Init_Grh WeaponData(LoopC).Attack(6), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Attack6")), 1
       Engine_Init_Grh WeaponData(LoopC).Attack(7), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Attack7")), 1
       Engine_Init_Grh WeaponData(LoopC).Attack(8), CLng(Var_Get(DataPath & "Weapon.dat", "Weapon" & LoopC, "Attack8")), 1
   Next LoopC

End Sub

Sub Engine_Weather_UpdateFog() '***************************************************************** 'Update the fog effects 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Weather_UpdateFog '***************************************************************** Dim TempGrh As Grh Dim i As Long Dim X As Long Dim Y As Long Dim c As Long

   'Make sure we have the fog value
   If WeatherFogCount = 0 Then WeatherFogCount = 13
   
   'Update the fog's position
   WeatherFogX1 = WeatherFogX1 + (ElapsedTime * (0.018 + Rnd * 0.01)) + (LastOffsetX - ParticleOffsetX)
   WeatherFogY1 = WeatherFogY1 + (ElapsedTime * (0.013 + Rnd * 0.01)) + (LastOffsetY - ParticleOffsetY)
   Do While WeatherFogX1 < -512
       WeatherFogX1 = WeatherFogX1 + 512
   Loop
   Do While WeatherFogY1 < -512
       WeatherFogY1 = WeatherFogY1 + 512
   Loop
   Do While WeatherFogX1 > 0
       WeatherFogX1 = WeatherFogX1 - 512
   Loop
   Do While WeatherFogY1 > 0
       WeatherFogY1 = WeatherFogY1 - 512
   Loop
   
   WeatherFogX2 = WeatherFogX2 - (ElapsedTime * (0.037 + Rnd * 0.01)) + (LastOffsetX - ParticleOffsetX)
   WeatherFogY2 = WeatherFogY2 - (ElapsedTime * (0.021 + Rnd * 0.01)) + (LastOffsetY - ParticleOffsetY)
   Do While WeatherFogX2 < -512
       WeatherFogX2 = WeatherFogX2 + 512
   Loop
   Do While WeatherFogY2 < -512
       WeatherFogY2 = WeatherFogY2 + 512
   Loop
   Do While WeatherFogX2 > 0
       WeatherFogX2 = WeatherFogX2 - 512
   Loop
   Do While WeatherFogY2 > 0
       WeatherFogY2 = WeatherFogY2 - 512
   Loop
   TempGrh.FrameCounter = 1
   
   'Render fog 2
   TempGrh.GrhIndex = 4
   X = 2
   Y = -1
   c = D3DColorARGB(100, 255, 255, 255)
   For i = 1 To WeatherFogCount
       Engine_Render_Grh TempGrh, (X * 512) + WeatherFogX2, (Y * 512) + WeatherFogY2, 0, 0, False, c, c, c, c
       X = X + 1
       If X > (1 + (ScreenWidth \ 512)) Then
           X = 0
           Y = Y + 1
       End If
   Next i
           
   'Render fog 1
   TempGrh.GrhIndex = 3
   X = 0
   Y = 0
   c = D3DColorARGB(75, 255, 255, 255)
   For i = 1 To WeatherFogCount
       Engine_Render_Grh TempGrh, (X * 512) + WeatherFogX1, (Y * 512) + WeatherFogY1, 0, 0, False, c, c, c, c
       X = X + 1
       If X > (2 + (ScreenWidth \ 512)) Then
           X = 0
           Y = Y + 1
       End If
   Next i

End Sub

Sub Engine_Weather_UpdateLightning() '***************************************************************** 'Updates the lightning count-down and creates the flash if its ready 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Weather_UpdateLightning '***************************************************************** Dim X As Long Dim Y As Long Dim i As Long

   'Check if we are in the middle of a flash
   If FlashTimer > 0 Then
       FlashTimer = FlashTimer - ElapsedTime
       
       'The flash has run out
       If FlashTimer <= 0 Then
       
           'Change the light of all the tiles back
           For X = 1 To MapInfo.Width
               For Y = 1 To MapInfo.Height
                   For i = 1 To 24
                       MapData(X, Y).Light(i) = SaveLightBuffer(X, Y).Light(i)
                   Next i
               Next Y
           Next X
       
       End If
       
   'Update the timer, see if it is time to flash
   Else
       LightningTimer = LightningTimer - ElapsedTime
       
       'Flash me, baby!
       If LightningTimer <= 0 Then
           LightningTimer = 15000 + (Rnd * 15000)  'Reset timer (flash every 15 to 30 seconds)
           FlashTimer = 250    'How long the flash is (miliseconds)
           
           'Sound effect
           Sound_Play WeatherSfx2, DSBPLAY_DEFAULT  'BAM!
           
           'Change the light of all the tiles to white
           For X = 1 To MapInfo.Width
               For Y = 1 To MapInfo.Height
                   For i = 1 To 24
                       MapData(X, Y).Light(i) = -1
                   Next i
               Next Y
           Next X
           
       End If
       
   End If

End Sub

Sub Engine_Weather_Update() '***************************************************************** 'Initializes the weather effects 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Weather_Update '*****************************************************************

   'Check if we're using weather
   If UseWeather = 0 Then Exit Sub
   'Only update the weather settings if it has changed!
   If LastWeather <> MapInfo.Weather Then
   
       'Set the lastweather to the current weather
       LastWeather = MapInfo.Weather
       
       'Erase sounds
       Sound_Erase WeatherSfx1
       Sound_Erase WeatherSfx2
   
       Select Case LastWeather
       
       Case 1  'Snow (light fall)
           If WeatherEffectIndex <= 0 Then
               WeatherEffectIndex = Effect_Snow_Begin(1, 400)
           ElseIf Effect(WeatherEffectIndex).EffectNum <> EffectNum_Snow Then
               Effect_Kill WeatherEffectIndex
               WeatherEffectIndex = Effect_Snow_Begin(1, 400)
           ElseIf Not Effect(WeatherEffectIndex).Used Then
               WeatherEffectIndex = Effect_Snow_Begin(1, 400)
           End If
           WeatherDoLightning = 0
           WeatherDoFog = 0
           
       Case 2  'Rain Storm (heavy rain + lightning)
           If WeatherEffectIndex <= 0 Then
               WeatherEffectIndex = Effect_Rain_Begin(9, 300)
           ElseIf Effect(WeatherEffectIndex).EffectNum <> EffectNum_Rain Then
               Effect_Kill WeatherEffectIndex
               WeatherEffectIndex = Effect_Rain_Begin(9, 300)
           ElseIf Not Effect(WeatherEffectIndex).Used Then
               WeatherEffectIndex = Effect_Rain_Begin(9, 300)
           End If
           LightningTimer = 15000 + (Rnd * 15000)
           WeatherDoLightning = 1  'We take our rain with a bit of lightning on top >:D
           WeatherDoFog = 0
           Sound_Set WeatherSfx1, 3
           Sound_Set WeatherSfx2, 2
           Sound_Play WeatherSfx1, DSBPLAY_LOOPING
           
       Case 3  'Inside of a house in a storm (lightning + muted rain sound)
           If WeatherEffectIndex > 0 Then  'Kill the weather effect if used
               If Effect(WeatherEffectIndex).Used Then Effect_Kill WeatherEffectIndex
           End If
           LightningTimer = 15000 + (Rnd * 15000)
           WeatherDoLightning = 1
           WeatherDoFog = 0
           Sound_Set WeatherSfx1, 4
           Sound_Set WeatherSfx2, 6
           Sound_Play WeatherSfx1, DSBPLAY_LOOPING
           
       Case 4  'Inside of a cave in a storm (lightning + muted rain sound + fog)
           If WeatherEffectIndex > 0 Then  'Kill the weather effect if used
               If Effect(WeatherEffectIndex).Used Then Effect_Kill WeatherEffectIndex
           End If
           LightningTimer = 15000 + (Rnd * 15000)
           WeatherDoLightning = 1
           WeatherDoFog = 10    'This will make it nice and spooky! >:D
           Sound_Set WeatherSfx1, 4
           Sound_Set WeatherSfx2, 6
           Sound_Play WeatherSfx1, DSBPLAY_LOOPING
           
       Case Else   'None
           If WeatherEffectIndex > 0 Then  'Kill the weather effect if used
               If Effect(WeatherEffectIndex).Used Then Effect_Kill WeatherEffectIndex
               Sound_Erase WeatherSfx1  'Remove the sounds
               Sound_Erase WeatherSfx2
           End If
           WeatherDoLightning = 0
           WeatherDoFog = 0
           
       End Select
       
   End If
   
   'Update fog
   If WeatherDoFog Then Engine_Weather_UpdateFog
   'Update lightning
   If WeatherDoLightning Then Engine_Weather_UpdateLightning

End Sub

Sub Engine_ShowNPCChatWindow(ByVal NPCName As String, ByVal ChatIndex As Byte, ByVal AskIndex As Byte) '***************************************************************** 'Shows the NPC chat window 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_ShowNPCChatWindow '***************************************************************** Dim i As Long Dim Offset As Long

   'Set the window values
   ActiveAsk.AskIndex = AskIndex
   ActiveAsk.ChatIndex = ChatIndex
   ActiveAsk.AskName = NPCName
   ActiveAsk.QuestionTxt = NPCName & ": " & vbNewLine & Engine_WordWrap(NPCChat(ChatIndex).Ask.Ask(AskIndex).Question, GameWindow.NPCChat.Screen.Width - 10)
   'Set the window information
   With GameWindow.NPCChat
       .NumAnswers = NPCChat(ChatIndex).Ask.Ask(AskIndex).NumAnswers
       ReDim .Answer(1 To .NumAnswers)
       
       Offset = .Screen.Height - 5
       For i = .NumAnswers To 1 Step -1
           Offset = Offset - Font_Default.CharHeight
           .Answer(i).Y = Offset
           .Answer(i).Height = Font_Default.CharHeight
           .Answer(i).X = 5
           .Answer(i).Width = Engine_GetTextWidth(Font_Default, i & ". " & NPCChat(ChatIndex).Ask.Ask(AskIndex).Answer(i).Text)
       Next i
       
   End With
   
   ShowGameWindow(NPCChatWindow) = 1
   LastClickedWindow = NPCChatWindow
   SelGameWindow = NPCChatWindow

End Sub

Function Engine_LegalPos(ByVal X As Integer, ByVal Y As Integer, ByVal Heading As Byte) As Boolean '***************************************************************** 'Checks to see if a tile position is legal 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_LegalPos '***************************************************************** Dim i As Integer

   'Check that it is in the map
   If X < 1 Then Exit Function
   If X > MapInfo.Width Then Exit Function
   If Y < 1 Then Exit Function
   If Y > MapInfo.Height Then Exit Function
   'Check to see if its blocked
   If MapData(X, Y).Blocked = BlockedAll Then Exit Function
   'Check the heading for directional blocking
   If Heading > 0 Then
       If MapData(X, Y).Blocked And BlockedNorth Then
           If Heading = NORTH Then Exit Function
           If Heading = NORTHEAST Then Exit Function
           If Heading = NORTHWEST Then Exit Function
       End If
       If MapData(X, Y).Blocked And BlockedEast Then
           If Heading = EAST Then Exit Function
           If Heading = NORTHEAST Then Exit Function
           If Heading = SOUTHEAST Then Exit Function
       End If
       If MapData(X, Y).Blocked And BlockedSouth Then
           If Heading = SOUTH Then Exit Function
           If Heading = SOUTHEAST Then Exit Function
           If Heading = SOUTHWEST Then Exit Function
       End If
       If MapData(X, Y).Blocked And BlockedWest Then
           If Heading = WEST Then Exit Function
           If Heading = NORTHWEST Then Exit Function
           If Heading = SOUTHWEST Then Exit Function
       End If
   End If
   'Check for character
   For i = 1 To LastChar
       If CharList(i).Active Then
           If CharList(i).Pos.X = X Then
               If CharList(i).Pos.Y = Y Then
                   If CharList(i).OwnerChar <> UserCharIndex Then
                       Exit Function
                   End If
               End If
           End If
       End If
   Next i
   'The position is legal
   Engine_LegalPos = True

End Function

Sub Engine_MoveScreen(ByVal Heading As Byte) '***************************************************************** 'Starts the screen moving in a direction 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_MoveScreen '***************************************************************** Dim X As Integer Dim Y As Integer Dim tX As Integer Dim tY As Integer

   'Figure out which way to move
   Select Case Heading
   Case NORTH
       Y = -1
   Case EAST
       X = 1
   Case SOUTH
       Y = 1
   Case WEST
       X = -1
   Case NORTHEAST
       Y = -1
       X = 1
   Case SOUTHEAST
       Y = 1
       X = 1
   Case SOUTHWEST
       Y = 1
       X = -1
   Case NORTHWEST
       Y = -1
       X = -1
   End Select
   
   'Fill temp pos
   tX = UserPos.X + X
   tY = UserPos.Y + Y
   
   If tX < 1 Then tX = 1: If X < 0 Then X = 0
   If tX > MapInfo.Width Then tX = MapInfo.Width: If X > 0 Then X = 0
   If tY < 1 Then tY = 1: If Y < 0 Then Y = 0
   If tY > MapInfo.Height Then tY = MapInfo.Height: If Y > 0 Then Y = 0
   'Start moving... MainLoop does the rest
   AddtoUserPos.X = X
   UserPos.X = tX
   AddtoUserPos.Y = Y
   UserPos.Y = tY
   UserMoving = True

End Sub

Sub Engine_MoveUser(ByVal Direction As Byte) '***************************************************************** 'Move user in appropriate direction 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_MoveUser '***************************************************************** Dim ax As Integer Dim ay As Integer Dim aX2 As Integer Dim aY2 As Integer Dim aX3 As Integer Dim aY3 As Integer Dim Direction2 As Byte Dim Direction3 As Byte

   'Check for a valid UserCharIndex
   If UserCharIndex <= 0 Or UserCharIndex > LastChar Then
   
       'We have an invalid user char index, so we must have the wrong one - request an update on the right one
       sndBuf.Put_Byte DataCode.User_RequestUserCharIndex
       Exit Sub
       
   End If
   'Dont move if the mail composing window is up
   If ShowGameWindow(WriteMessageWindow) Then Exit Sub
   'Figure out the AddX and AddY values
   Select Case Direction
       Case NORTHEAST
           ax = 1
           ay = -1
           aX2 = 0
           aY2 = -1
           aX3 = 1
           aY3 = 0
           Direction2 = NORTH
           Direction3 = EAST
       Case NORTHWEST
           ax = -1
           ay = -1
           aX2 = 0
           aY2 = -1
           aX3 = -1
           aY3 = 0
           Direction2 = NORTH
           Direction3 = WEST
       Case SOUTHEAST
           ax = 1
           ay = 1
           aX2 = 0
           aY2 = 1
           aX3 = 1
           aY3 = 0
           Direction2 = SOUTH
           Direction3 = EAST
       Case SOUTHWEST
           ax = -1
           ay = 1
           aX2 = 0
           aY2 = 1
           aX3 = -1
           aY3 = 0
           Direction2 = SOUTH
           Direction3 = WEST
       Case NORTH
           ax = 0
           ay = -1
       Case EAST
           ax = 1
           ay = 0
       Case SOUTH
           ax = 0
           ay = 1
       Case WEST
           ax = -1
           ay = 0
   End Select
   'If the shop, mailbox or read mail window are showing, hide them
   ShowGameWindow(MailboxWindow) = 0
   ShowGameWindow(ShopWindow) = 0
   ShowGameWindow(ViewMessageWindow) = 0
   ShowGameWindow(AmountWindow) = 0
   ShowGameWindow(BankWindow) = 0
   If LastClickedWindow = MailboxWindow Or LastClickedWindow = ShopWindow Or LastClickedWindow = ViewMessageWindow Or _
       LastClickedWindow = AmountWindow Or LastClickedWindow = BankWindow Then LastClickedWindow = 0
   AmountWindowUsage = 0
   AmountWindowItemIndex = 0
   AmountWindowValue = vbNullString
   'Try the first movement
   If Engine_LegalPos(UserPos.X + ax, UserPos.Y + ay, Direction) Then
       Engine_SendMovePacket Direction
       Exit Sub
   End If
   
   'If the first movement failed, use the second and third if a diagonal direction
   If Direction2 > 0 Then
       If Engine_LegalPos(UserPos.X + aX2, UserPos.Y + aY2, Direction) Then
           Engine_SendMovePacket Direction2
           Exit Sub
       End If
       If Engine_LegalPos(UserPos.X + aX3, UserPos.Y + aY3, Direction3) Then
           Engine_SendMovePacket Direction3
           Exit Sub
       End If
   End If
   'Movement failed, rotate the user to face the direction if needed
   'Only rotate if the user is not already facing that direction
   If CharList(UserCharIndex).Heading <> Direction Then
       sndBuf.Allocate 2
       sndBuf.Put_Byte DataCode.User_Rotate
       sndBuf.Put_Byte Direction
   End If

End Sub

Sub Engine_SendMovePacket(ByVal Direction As Byte) '***************************************************************** 'Sends the user's movement packet to the server 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_SendMovePacket '***************************************************************** Dim Running As Byte

   'If running
   If GetAsyncKeyState(vbKeyShift) Then
   
       'Check if the user has enough stamina to run
       If BaseStats(SID.MinSTA) > RunningCost Then Running = 1
       
   End If
   'Send the information to the server
   sndBuf.Allocate 2
   sndBuf.Put_Byte DataCode.User_Move
   
   'Running or not
   If Running = 1 Then sndBuf.Put_Byte Direction Or 128 Else sndBuf.Put_Byte Direction
   'If the user changed directions or just started moving, request a position update
   If CharList(UserCharIndex).Moving = 0 Or CharList(UserCharIndex).Heading <> Direction Then
       sndBuf.Allocate 3
       sndBuf.Put_Byte DataCode.Server_SetUserPosition
       sndBuf.Put_Byte UserPos.X
       sndBuf.Put_Byte UserPos.Y
   End If
   'Move the screen and character
   Engine_Char_Move_ByHead UserCharIndex, Direction, Running
   Engine_MoveScreen Direction
   
   'Update the map sounds
   Sound_UpdateMap
   

End Sub

Sub Engine_OBJ_Create(ByVal GrhIndex As Long, ByVal X As Byte, ByVal Y As Byte) '***************************************************************** 'Create an object on the map and update LastOBJ value 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_OBJ_Create '***************************************************************** Dim ObjIndex As Integer

   'Get the next open obj slot
   Do
       ObjIndex = ObjIndex + 1
       'Update LastObj if we go over the size of the current array
       If ObjIndex > LastObj Then
           LastObj = ObjIndex
           ReDim Preserve OBJList(1 To ObjIndex)
           Exit Do
       End If
   Loop While OBJList(ObjIndex).Grh.GrhIndex > 0
   'Set the object position
   OBJList(ObjIndex).Pos.X = X
   OBJList(ObjIndex).Pos.Y = Y
   
   'Set a random offset
   OBJList(ObjIndex).Offset.X = -16 + Int(Rnd * 32)
   OBJList(ObjIndex).Offset.Y = -16 + Int(Rnd * 32)
   'Create the object
   Engine_Init_Grh OBJList(ObjIndex).Grh, GrhIndex

End Sub

Function Engine_OBJ_AtTile(ByVal X As Byte, ByVal Y As Byte) As Boolean '***************************************************************** 'Checks for an object at tile (X,Y) 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_OBJ_AtTile '***************************************************************** Dim i As Long

   'Check if any objects exist
   If LastObj = 0 Then Exit Function
   'Loop through all the objects
   For i = 1 To LastObj
       
       'Check if the object is located at the tile
       If OBJList(i).Pos.X = X Then
           If OBJList(i).Pos.Y = Y Then
               
               'We have an object here!
               Engine_OBJ_AtTile = True
               Exit Function
               
           End If
       End If
       
   Next i

End Function

Sub Engine_OBJ_Erase(ByVal ObjIndex As Integer) '***************************************************************** 'Erase an object from the map and update the LastOBJ value 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_OBJ_Erase '*****************************************************************

   'Check for a valid object
   If ObjIndex > LastObj Then Exit Sub
   If ObjIndex <= 0 Then Exit Sub
   'Erase the object
   ZeroMemory OBJList(ObjIndex), LenB(OBJList(ObjIndex))
   'Update LastOBJ
   If ObjIndex = LastObj Then
       Do Until OBJList(LastObj).Grh.GrhIndex > 1
           'Move down one object
           LastObj = LastObj - 1
           If LastObj = 0 Then Exit Do
       Loop
       If ObjIndex <> LastObj Then
           'We still have objects, resize the array to end at the last used slot
           If LastObj <> 0 Then
               ReDim Preserve OBJList(1 To LastObj)
           Else
               Erase OBJList
           End If
       End If
   End If

End Sub

Function Engine_PixelPosX(ByVal X As Integer) As Integer '***************************************************************** 'Converts a tile position to a screen position 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_PixelPosX '*****************************************************************

   Engine_PixelPosX = (X - 1) * TilePixelWidth

End Function

Function Engine_PixelPosY(ByVal Y As Integer) As Integer '***************************************************************** 'Converts a tile position to a screen position 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_PixelPosY '*****************************************************************

   Engine_PixelPosY = (Y - 1) * TilePixelHeight

End Function

Private Function Engine_Collision_Between(ByVal Value As Single, ByVal Bound1 As Single, ByVal Bound2 As Single) As Byte '***************************************************************** 'Find if a value is between two other values (used for line collision) 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Collision_Between '*****************************************************************

   'Checks if a value lies between two bounds
   If Bound1 > Bound2 Then
       If Value >= Bound2 Then
           If Value <= Bound1 Then Engine_Collision_Between = 1
       End If
   Else
       If Value >= Bound1 Then
           If Value <= Bound2 Then Engine_Collision_Between = 1
       End If
   End If
   

End Function

Public Function Engine_Collision_Line(ByVal L1X1 As Long, ByVal L1Y1 As Long, ByVal L1X2 As Long, ByVal L1Y2 As Long, ByVal L2X1 As Long, ByVal L2Y1 As Long, ByVal L2X2 As Long, ByVal L2Y2 As Long) As Byte '***************************************************************** 'Check if two lines intersect (return 1 if true) 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Collision_Line '***************************************************************** Dim m1 As Single Dim M2 As Single Dim B1 As Single Dim B2 As Single Dim IX As Single

   'This will fix problems with vertical lines
   If L1X1 = L1X2 Then L1X1 = L1X1 + 1
   If L2X1 = L2X2 Then L2X1 = L2X1 + 1
   'Find the first slope
   m1 = (L1Y2 - L1Y1) / (L1X2 - L1X1)
   B1 = L1Y2 - m1 * L1X2
   'Find the second slope
   M2 = (L2Y2 - L2Y1) / (L2X2 - L2X1)
   B2 = L2Y2 - M2 * L2X2
   
   'Check if the slopes are the same
   If M2 - m1 = 0 Then
   
       If B2 = B1 Then
           'The lines are the same
           Engine_Collision_Line = 1
       Else
           'The lines are parallel (can never intersect)
           Engine_Collision_Line = 0
       End If
       
   Else
       
       'An intersection is a point that lies on both lines. To find this, we set the Y equations equal and solve for X.
       'M1X+B1 = M2X+B2 -> M1X-M2X = -B1+B2 -> X = B1+B2/(M1-M2)
       IX = ((B2 - B1) / (m1 - M2))
       
       'Check for the collision
       If Engine_Collision_Between(IX, L1X1, L1X2) Then
           If Engine_Collision_Between(IX, L2X1, L2X2) Then Engine_Collision_Line = 1
       End If
       
   End If
   

End Function

Public Function Engine_Collision_LineRect(ByVal SX As Long, ByVal SY As Long, ByVal SW As Long, ByVal SH As Long, ByVal x1 As Long, ByVal Y1 As Long, ByVal x2 As Long, ByVal Y2 As Long) As Byte '***************************************************************** 'Check if a line intersects with a rectangle (returns 1 if true) 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Collision_LineRect '*****************************************************************

   'Top line
   If Engine_Collision_Line(SX, SY, SX + SW, SY, x1, Y1, x2, Y2) Then
       Engine_Collision_LineRect = 1
       Exit Function
   End If
   
   'Right line
   If Engine_Collision_Line(SX + SW, SY, SX + SW, SY + SH, x1, Y1, x2, Y2) Then
       Engine_Collision_LineRect = 1
       Exit Function
   End If
   'Bottom line
   If Engine_Collision_Line(SX, SY + SH, SX + SW, SY + SH, x1, Y1, x2, Y2) Then
       Engine_Collision_LineRect = 1
       Exit Function
   End If
   'Left line
   If Engine_Collision_Line(SX, SY, SX, SY + SW, x1, Y1, x2, Y2) Then
       Engine_Collision_LineRect = 1
       Exit Function
   End If

End Function

Function Engine_Collision_Rect(ByVal x1 As Integer, ByVal Y1 As Integer, ByVal Width1 As Integer, ByVal Height1 As Integer, ByVal x2 As Integer, ByVal Y2 As Integer, ByVal Width2 As Integer, ByVal Height2 As Integer) As Boolean '***************************************************************** 'Check for collision between two rectangles 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Collision_Rect '*****************************************************************

   If x1 + Width1 >= x2 Then
       If x1 <= x2 + Width2 Then
           If Y1 + Height1 >= Y2 Then
               If Y1 <= Y2 + Height2 Then
                   Engine_Collision_Rect = True
               End If
           End If
       End If
   End If

End Function

Private Sub Engine_Render_Char(ByVal CharIndex As Long, ByVal PixelOffsetX As Single, ByVal PixelOffsetY As Single) '***************************************************************** 'Draw a character to the screen by the CharIndex 'First variables are set, then all shadows drawn, then character drawn, then extras (emoticons, icons, etc) 'Any variables not handled in "Set the variables" are set in Shadow calls - do not call a second time in the 'normal character rendering calls 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_Char '***************************************************************** Dim TempGrh As Grh Dim Moved As Boolean Dim IconCount As Byte Dim IconOffset As Integer Dim RenderColor(1 To 4) As Long Dim TempBlock As MapBlock Dim TempBlock2 As MapBlock Dim HeadGrh As Grh Dim BodyGrh As Grh Dim WeaponGrh As Grh Dim HairGrh As Grh Dim WingsGrh As Grh

   '***** Set the variables *****
   
   'Update blinking
   If CharList(CharIndex).BlinkTimer <= 0 Then
       CharList(CharIndex).StartBlinkTimer = CharList(CharIndex).StartBlinkTimer - ElapsedTime
       If CharList(CharIndex).StartBlinkTimer <= 0 Then
           CharList(CharIndex).BlinkTimer = 300
           CharList(CharIndex).StartBlinkTimer = Engine_GetBlinkTime
       End If
   End If
   
   'Set the map block the char is on to the TempBlock, and the block above the user as TempBlock2
   TempBlock = MapData(CharList(CharIndex).Pos.X, CharList(CharIndex).Pos.Y)
   If CharList(CharIndex).Pos.Y > 1 Then
       TempBlock2 = MapData(CharList(CharIndex).Pos.X, CharList(CharIndex).Pos.Y - 1)
   Else
       TempBlock2 = TempBlock
   End If
   
   'Check for selected NPC
   If CharIndex = TargetCharIndex Then
   
       'Clear pathway to the targeted character
       If ClearPathToTarget Then
           RenderColor(1) = D3DColorARGB(255, 100, 255, 100)
           RenderColor(2) = RenderColor(1)
           RenderColor(3) = RenderColor(1)
           RenderColor(4) = RenderColor(1)
       Else
           RenderColor(1) = D3DColorARGB(255, 255, 100, 100)
           RenderColor(2) = RenderColor(1)
           RenderColor(3) = RenderColor(1)
           RenderColor(4) = RenderColor(1)
       End If
       
   Else
       RenderColor(1) = TempBlock2.Light(1)
       RenderColor(2) = TempBlock2.Light(2)
       RenderColor(3) = TempBlock.Light(3)
       RenderColor(4) = TempBlock.Light(4)
   End If
   If CharList(CharIndex).Moving Then
       'If needed, move left and right
       If CharList(CharIndex).ScrollDirectionX <> 0 Then
           CharList(CharIndex).MoveOffset.X = CharList(CharIndex).MoveOffset.X + (ScrollPixelsPerFrameX + CharList(CharIndex).Speed + (RunningSpeed * CharList(CharIndex).Running)) * Sgn(CharList(CharIndex).ScrollDirectionX) * TickPerFrame
           'Start animation
           CharList(CharIndex).Body.Walk(CharList(CharIndex).Heading).Started = 1
           'Char moved
           Moved = True
           'Check if we already got there
           If (Sgn(CharList(CharIndex).ScrollDirectionX) = 1 And CharList(CharIndex).MoveOffset.X >= 0) Or (Sgn(CharList(CharIndex).ScrollDirectionX) = -1 And CharList(CharIndex).MoveOffset.X <= 0) Then
               CharList(CharIndex).MoveOffset.X = 0
               CharList(CharIndex).ScrollDirectionX = 0
           End If
       End If
       'If needed, move up and down
       If CharList(CharIndex).ScrollDirectionY <> 0 Then
           CharList(CharIndex).MoveOffset.Y = CharList(CharIndex).MoveOffset.Y + (ScrollPixelsPerFrameY + CharList(CharIndex).Speed + (RunningSpeed * CharList(CharIndex).Running)) * Sgn(CharList(CharIndex).ScrollDirectionY) * TickPerFrame
           'Start animation
           CharList(CharIndex).Body.Walk(CharList(CharIndex).Heading).Started = 1
           'Char moved
           Moved = True
           'Check if we already got there
           If (Sgn(CharList(CharIndex).ScrollDirectionY) = 1 And CharList(CharIndex).MoveOffset.Y >= 0) Or (Sgn(CharList(CharIndex).ScrollDirectionY) = -1 And CharList(CharIndex).MoveOffset.Y <= 0) Then
               CharList(CharIndex).MoveOffset.Y = 0
               CharList(CharIndex).ScrollDirectionY = 0
           End If
       End If
   End If
   'Update movement reset timer
   If CharList(CharIndex).ScrollDirectionX = 0 Or CharList(CharIndex).ScrollDirectionY = 0 Then
       'If done moving stop animation
       If Not Moved Then
           If CharList(CharIndex).Body.Walk(CharList(CharIndex).Heading).Started Then
               'Stop animation
               CharList(CharIndex).Body.Walk(CharList(CharIndex).Heading).Started = 0
               CharList(CharIndex).Body.Walk(CharList(CharIndex).Heading).FrameCounter = 1
               CharList(CharIndex).Moving = 0
               If CharList(CharIndex).ActionIndex = 1 Then CharList(CharIndex).ActionIndex = 0
               
               'If it is the user's character, confirm the position is correct
               If CharIndex = UserCharIndex Then
                   sndBuf.Allocate 3
                   sndBuf.Put_Byte DataCode.Server_SetUserPosition
                   sndBuf.Put_Byte CharList(CharIndex).Pos.X
                   sndBuf.Put_Byte CharList(CharIndex).Pos.Y
               End If
           End If
       End If
   End If
   'Set the pixel offset
   PixelOffsetX = PixelOffsetX + CharList(CharIndex).MoveOffset.X
   PixelOffsetY = PixelOffsetY + CharList(CharIndex).MoveOffset.Y
   
   'Save the values in the realpos variable
   CharList(CharIndex).RealPos.X = PixelOffsetX
   CharList(CharIndex).RealPos.Y = PixelOffsetY
   '***** Render Shadows *****
   'Draw Body
   If CharList(CharIndex).ActionIndex <= 1 Then
       'Shadow
       Engine_Render_Grh CharList(CharIndex).Body.Walk(CharList(CharIndex).Heading), PixelOffsetX, PixelOffsetY, 1, 1, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
       Engine_Render_Grh CharList(CharIndex).Weapon.Walk(CharList(CharIndex).Heading), PixelOffsetX, PixelOffsetY, 1, 1, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
   Else
       'Shadow
       Engine_Render_Grh CharList(CharIndex).Body.Attack(CharList(CharIndex).Heading), PixelOffsetX, PixelOffsetY, 1, 1, False, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
       Engine_Render_Grh CharList(CharIndex).Weapon.Attack(CharList(CharIndex).Heading), PixelOffsetX, PixelOffsetY, 1, 1, False, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
       'Check if animation has stopped
       If CharList(CharIndex).Body.Attack(CharList(CharIndex).Heading).Started = 0 Then CharList(CharIndex).ActionIndex = 0
   End If
   
   'Update aggressive timer
   If CharList(CharIndex).Aggressive > 0 Then
       If CharList(CharIndex).AggressiveCounter < timeGetTime Then
           CharList(CharIndex).Aggressive = 0
           CharList(CharIndex).AggressiveCounter = 0
       End If
   End If
   'Draw Head
   If CharList(CharIndex).Aggressive > 0 Then
       'Aggressive
       If CharList(CharIndex).BlinkTimer > 0 Then
           CharList(CharIndex).BlinkTimer = CharList(CharIndex).BlinkTimer - ElapsedTime
           'Blinking
           Engine_Render_Grh CharList(CharIndex).Head.AgrBlink(CharList(CharIndex).HeadHeading), PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, True, False, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
       Else
           'Normal
           Engine_Render_Grh CharList(CharIndex).Head.AgrHead(CharList(CharIndex).HeadHeading), PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, True, False, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
       End If
   Else
       'Not Aggressive
       If CharList(CharIndex).BlinkTimer > 0 Then
           CharList(CharIndex).BlinkTimer = CharList(CharIndex).BlinkTimer - ElapsedTime
           'Blinking
           Engine_Render_Grh CharList(CharIndex).Head.Blink(CharList(CharIndex).HeadHeading), PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, True, False, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
       Else
           'Normal
           Engine_Render_Grh CharList(CharIndex).Head.Head(CharList(CharIndex).HeadHeading), PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, True, False, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
       End If
   End If
   'Hair
   Engine_Render_Grh CharList(CharIndex).Hair.Hair(CharList(CharIndex).HeadHeading), PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, True, False, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
   '***** Render Character *****
   '***** (When updating this, make sure you copy it to the NPCEditor and MapEditor, too!) *****
   CharList(CharIndex).Weapon.Walk(CharList(CharIndex).Heading).FrameCounter = CharList(CharIndex).Body.Walk(CharList(CharIndex).Heading).FrameCounter
   'The body, weapon and wings
   If CharList(CharIndex).ActionIndex <= 1 Then
       'Walking
       BodyGrh = CharList(CharIndex).Body.Walk(CharList(CharIndex).Heading)
       WeaponGrh = CharList(CharIndex).Weapon.Walk(CharList(CharIndex).Heading)
       WingsGrh = CharList(CharIndex).Wings.Walk(CharList(CharIndex).Heading)
       CharList(CharIndex).Weapon.Walk(CharList(CharIndex).Heading).FrameCounter = CharList(CharIndex).Body.Walk(CharList(CharIndex).Heading).FrameCounter
   Else
       'Attacking
       BodyGrh = CharList(CharIndex).Body.Attack(CharList(CharIndex).Heading)
       WeaponGrh = CharList(CharIndex).Weapon.Attack(CharList(CharIndex).Heading)
       WingsGrh = CharList(CharIndex).Wings.Attack(CharList(CharIndex).Heading)
       CharList(CharIndex).Weapon.Attack(CharList(CharIndex).Heading).FrameCounter = CharList(CharIndex).Body.Attack(CharList(CharIndex).Heading).FrameCounter
   End If
   
   'The head
   If CharList(CharIndex).Aggressive > 0 Then  'Aggressive
       If CharList(CharIndex).BlinkTimer > 0 Then HeadGrh = CharList(CharIndex).Head.AgrBlink(CharList(CharIndex).HeadHeading) Else HeadGrh = CharList(CharIndex).Head.AgrHead(CharList(CharIndex).HeadHeading)
   Else    'Non-aggressive
       If CharList(CharIndex).BlinkTimer > 0 Then HeadGrh = CharList(CharIndex).Head.Blink(CharList(CharIndex).HeadHeading) Else HeadGrh = CharList(CharIndex).Head.Head(CharList(CharIndex).HeadHeading)
   End If
   
   'The hair
   HairGrh = CharList(CharIndex).Hair.Hair(CharList(CharIndex).HeadHeading)
   
   'Make the paperdoll layering based off the direction they are heading
       
   '*** NORTH / NORTHEAST *** (1.Weapon 2.Body 3.Head 4.Hair 5.Wings)
   If CharList(CharIndex).Heading = NORTH Or CharList(CharIndex).Heading = NORTHEAST Then
       Engine_Render_Grh WeaponGrh, PixelOffsetX, PixelOffsetY, True, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh BodyGrh, PixelOffsetX, PixelOffsetY, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh HeadGrh, PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh HairGrh, PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh WingsGrh, PixelOffsetX, PixelOffsetY, True, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       
   '*** EAST / SOUTHEAST *** (1.Body 2.Head 3.Hair 4.Wings 5.Weapon)
   ElseIf CharList(CharIndex).Heading = EAST Or CharList(CharIndex).Heading = SOUTHEAST Then
       Engine_Render_Grh BodyGrh, PixelOffsetX, PixelOffsetY, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh HeadGrh, PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh HairGrh, PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh WingsGrh, PixelOffsetX, PixelOffsetY, True, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh WeaponGrh, PixelOffsetX, PixelOffsetY, True, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       
   '*** SOUTH / SOUTHWEST *** (1.Wings 2.Body 3.Head 4.Hair 5.Weapon)
   ElseIf CharList(CharIndex).Heading = SOUTH Or CharList(CharIndex).Heading = SOUTHWEST Then
       Engine_Render_Grh WingsGrh, PixelOffsetX, PixelOffsetY, True, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh BodyGrh, PixelOffsetX, PixelOffsetY, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh HeadGrh, PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh HairGrh, PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh WeaponGrh, PixelOffsetX, PixelOffsetY, True, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       
   '*** WEST / NORTHWEST *** (1.Weapon 1.Body 2.Head 3.Hair 4.Wings)
   ElseIf CharList(CharIndex).Heading = WEST Or CharList(CharIndex).Heading = NORTHWEST Then
       Engine_Render_Grh WeaponGrh, PixelOffsetX, PixelOffsetY, True, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh BodyGrh, PixelOffsetX, PixelOffsetY, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh HeadGrh, PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh HairGrh, PixelOffsetX + CharList(CharIndex).Body.HeadOffset.X, PixelOffsetY + CharList(CharIndex).Body.HeadOffset.Y, 1, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       Engine_Render_Grh WingsGrh, PixelOffsetX, PixelOffsetY, True, 0, True, RenderColor(1), RenderColor(2), RenderColor(3), RenderColor(4)
       
   End If
   '***** Render Extras *****
   'Draw name over head
   Engine_Render_Text Font_Default, CharList(CharIndex).Name, PixelOffsetX + 16 - CharList(CharIndex).NameOffset, PixelOffsetY - 40, RenderColor(1)
   'Count the number of icons that will be needed to draw
   With CharList(CharIndex).CharStatus
       IconCount = 0
       IconCount = .Blessed + .Protected + .Strengthened + .Cursed + .WarCursed + .IronSkinned + .Exhausted
   End With
   
   'Health/Mana bars
   Engine_Render_Rectangle PixelOffsetX - 4, PixelOffsetY + 34, (CharList(CharIndex).HealthPercent / 100) * 40, 4, 1, 1, 1, 1, 1, 1, 0, 0, HealthColor, HealthColor, HealthColor, HealthColor, 0, False
   Engine_Render_Rectangle PixelOffsetX - 4, PixelOffsetY + 38, (CharList(CharIndex).ManaPercent / 100) * 40, 4, 1, 1, 1, 1, 1, 1, 0, 0, ManaColor, ManaColor, ManaColor, ManaColor, 0, False
   'Draw the icons
   If IconCount > 0 Then
       'Calculate the icon offset
       IconOffset = PixelOffsetX + 16 - (IconCount * 8)
       If CharList(CharIndex).CharStatus.Blessed Then
           Engine_Init_Grh TempGrh, 15
           Engine_Render_Grh TempGrh, IconOffset, PixelOffsetY - 50, 0, 0, False
           IconOffset = IconOffset + 16
       End If
       If CharList(CharIndex).CharStatus.Protected Then
           Engine_Init_Grh TempGrh, 20
           Engine_Render_Grh TempGrh, IconOffset, PixelOffsetY - 50, 0, 0, False
           IconOffset = IconOffset + 16
       End If
       If CharList(CharIndex).CharStatus.Strengthened Then
           Engine_Init_Grh TempGrh, 17
           Engine_Render_Grh TempGrh, IconOffset, PixelOffsetY - 50, 0, 0, False
           IconOffset = IconOffset + 16
       End If
       If CharList(CharIndex).CharStatus.Cursed Then
           Engine_Init_Grh TempGrh, 18
           Engine_Render_Grh TempGrh, IconOffset, PixelOffsetY - 50, 0, 0, False
           IconOffset = IconOffset + 16
       End If
       If CharList(CharIndex).CharStatus.WarCursed Then
           Engine_Init_Grh TempGrh, 19
           Engine_Render_Grh TempGrh, IconOffset, PixelOffsetY - 50, 0, 0, False
           IconOffset = IconOffset + 16
       End If
       If CharList(CharIndex).CharStatus.IronSkinned Then
           Engine_Init_Grh TempGrh, 16
           Engine_Render_Grh TempGrh, IconOffset, PixelOffsetY - 50, 0, 0, False
           IconOffset = IconOffset + 16
       End If
       If CharList(CharIndex).CharStatus.Exhausted Then
           Engine_Init_Grh TempGrh, 22
           Engine_Render_Grh TempGrh, IconOffset, PixelOffsetY - 50, 0, 0, False
           IconOffset = IconOffset + 16
       End If
   End If
   'Emoticons
   If CharList(CharIndex).EmoDir > 0 Then
       'Fade in
       If CharList(CharIndex).EmoDir = 1 Then
           CharList(CharIndex).EmoFade = CharList(CharIndex).EmoFade + (ElapsedTime * 1.5)
           If CharList(CharIndex).EmoFade >= 255 Then
               CharList(CharIndex).EmoFade = 255
               CharList(CharIndex).EmoDir = 2
           End If
       End If
       'Fade out
       If CharList(CharIndex).Emoticon.Started = 0 Then    'Animation has stopped
           If CharList(CharIndex).EmoDir = 2 Then
               CharList(CharIndex).EmoFade = CharList(CharIndex).EmoFade - (ElapsedTime * 1.5)
               If CharList(CharIndex).EmoFade <= 0 Then
                   CharList(CharIndex).EmoFade = 0
                   CharList(CharIndex).EmoDir = 0
               End If
               'Stop at the last frame, don't roll over to the first
               CharList(CharIndex).Emoticon.FrameCounter = GrhData(CharList(CharIndex).Emoticon.GrhIndex).NumFrames
           End If
       End If
       'Render
       Engine_Render_Grh CharList(CharIndex).Emoticon, PixelOffsetX + 8, PixelOffsetY - 40, 0, 1, False, D3DColorARGB(CharList(CharIndex).EmoFade, 255, 255, 255), D3DColorARGB(CharList(CharIndex).EmoFade, 255, 255, 255), D3DColorARGB(CharList(CharIndex).EmoFade, 255, 255, 255), D3DColorARGB(CharList(CharIndex).EmoFade, 255, 255, 255)
   End If

End Sub

Private Sub Engine_Render_ChatTextBuffer() '************************************************************ 'Update and render the chat text buffer 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_ChatTextBuffer '************************************************************ Dim SrcRect As RECT Dim v2 As D3DVECTOR2 Dim v3 As D3DVECTOR2 Dim i As Long

   'Check if we have the device
   If D3DDevice.TestCooperativeLevel <> D3D_OK Then Exit Sub
   
   'Assign the alternate rendering value
   AlternateRender = AlternateRenderText
   'Check if using alternate rendering
   If AlternateRender Then
       'Check if the texture is no longer the font texture
       If LastTexture <> Font_Default_TextureNum Then
       
           'End the old sprite we had going
           If SpriteBegun = 1 Then
               Sprite.End
               Sprite.Begin
           End If
           
       End If
       
       'Loop through all the characters
       For i = 0 To UBound(ChatVA) Step 6
           'Create the source rectangle
           With SrcRect
               .Left = ChatVA(i).tU * Font_Default.TextureSize.X
               .Top = ChatVA(i).tV * Font_Default.TextureSize.Y
               .Right = ChatVA(i + 5).tU * Font_Default.TextureSize.X
               .bottom = ChatVA(i + 5).tV * Font_Default.TextureSize.Y
           End With
           
           'Set the translation (location on the screen)
           v3.X = ChatVA(i).X
           v3.Y = ChatVA(i).Y
           
           'Draw the character
           Sprite.Draw Font_Default.Texture, SrcRect, SpriteScaleVector, v2, 0, v3, ChatVA(i).Color
   
       Next i
   Else
       
       'Clear the LastTexture, letting the rest of the engine know that the texture needs to be changed for next rect render
       D3DDevice.SetTexture 0, Font_Default.Texture
       LastTexture = Font_Default_TextureNum
   
       'Set up the vertex buffer
       If ShowGameWindow(ChatWindow) Then
           If ChatArrayUbound > 0 Then
               D3DDevice.SetStreamSource 0, ChatVB, FVF_Size
               D3DDevice.DrawPrimitive D3DPT_TRIANGLELIST, 0, (ChatArrayUbound + 1) \ 3
           End If
       End If
   
   End If
   
   'Retreive the default alternate render value
   AlternateRender = AlternateRenderDefault

End Sub

Private Function Engine_UpdateGrh(ByRef Grh As Grh, Optional ByVal LoopAnim As Boolean = True) As Boolean '***************************************************************** 'Updates the grh's animation 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_UpdateGrh '*****************************************************************

   'Check that the grh is started
   If Grh.Started = 1 Then
   
       'Update the frame counter
       Grh.FrameCounter = Grh.FrameCounter + ((timeGetTime - Grh.LastCount) * GrhData(Grh.GrhIndex).Speed)
       Grh.LastCount = timeGetTime
       
       'If the frame counter is higher then the number of frames...
       If Grh.FrameCounter >= GrhData(Grh.GrhIndex).NumFrames + 1 Then
       
           'Loop the animation
           If LoopAnim Then
               Do While Grh.FrameCounter >= GrhData(Grh.GrhIndex).NumFrames + 1
                   Grh.FrameCounter = Grh.FrameCounter - GrhData(Grh.GrhIndex).NumFrames
               Loop
           
           'Looping isn't set, just kill the animation
           Else
               Grh.Started = 0
               Exit Function
           End If
           
       End If
       
   End If
   
   'The grpahic will be rendered
   Engine_UpdateGrh = True
   

End Function

Sub Engine_Render_Grh(ByRef Grh As Grh, ByVal X As Integer, ByVal Y As Integer, ByVal Center As Byte, ByVal Animate As Byte, Optional ByVal LoopAnim As Boolean = True, Optional ByVal Light1 As Long = -1, Optional ByVal Light2 As Long = -1, Optional ByVal Light3 As Long = -1, Optional ByVal Light4 As Long = -1, Optional ByVal Shadow As Byte = 0, Optional ByVal Angle As Single = 0) '***************************************************************** 'Draws a GRH transparently to a X and Y position 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_Grh '***************************************************************** Dim CurrGrhIndex As Long 'The grh index we will be working with (acquired after updating animations) Dim FileNum As Integer

   'Check to make sure it is legal
   If Grh.GrhIndex < 1 Then Exit Sub
   If GrhData(Grh.GrhIndex).NumFrames < 1 Then Exit Sub
   If Grh.FrameCounter < 1 Then
       'Grh has a delay, so just update the frame and then leave
       Engine_UpdateGrh Grh, LoopAnim
       Exit Sub
   End If
   If Int(Grh.FrameCounter) > GrhData(Grh.GrhIndex).NumFrames Then Grh.FrameCounter = 1
   
   'Figure out what frame to draw (always 1 if not animated)
   CurrGrhIndex = GrhData(Grh.GrhIndex).Frames(Int(Grh.FrameCounter))
   'Check for in-bounds
   If X + GrhData(CurrGrhIndex).pixelWidth > 0 Then
       If Y + GrhData(CurrGrhIndex).pixelHeight > 0 Then
           If X < ScreenWidth Then
               If Y < ScreenHeight Then
               
                   'Update the animation frame
                   If Animate Then
                       If Not Engine_UpdateGrh(Grh, LoopAnim) Then Exit Sub
                   End If
                   
                   'Set the file number in a shorter variable
                   FileNum = GrhData(CurrGrhIndex).FileNum
               
                   'Center Grh over X,Y pos
                   If Center Then
                       If GrhData(CurrGrhIndex).TileWidth > 1 Then
                           X = X - GrhData(CurrGrhIndex).TileWidth * TilePixelWidth \ 2 + TilePixelWidth \ 2
                       End If
                       If GrhData(CurrGrhIndex).TileHeight > 1 Then
                           Y = Y - GrhData(CurrGrhIndex).TileHeight * TilePixelHeight + TilePixelHeight
                       End If
                   End If
               
                   'Check the rendering method to use
                   If AlternateRender = 0 Then
                   
                       'Render the texture with 2 triangles on a triangle strip
                       Engine_Render_Rectangle X, Y, GrhData(CurrGrhIndex).pixelWidth, GrhData(CurrGrhIndex).pixelHeight, GrhData(CurrGrhIndex).SX, _
                           GrhData(CurrGrhIndex).SY, GrhData(CurrGrhIndex).pixelWidth, GrhData(CurrGrhIndex).pixelHeight, , , Angle, FileNum, Light1, Light2, Light3, Light4, Shadow, False
                       
                   Else
                       
                       'Render the texture as a D3DXSprite
                       Engine_Render_D3DXSprite X, Y, GrhData(CurrGrhIndex).pixelWidth, GrhData(CurrGrhIndex).pixelHeight, GrhData(CurrGrhIndex).SX, GrhData(CurrGrhIndex).SY, Light1, FileNum, Angle
                       
                   End If
                   
               End If
           End If
       End If
   End If

End Sub

Private Sub Engine_Render_D3DXSprite(ByVal X As Single, ByVal Y As Single, ByVal Width As Single, ByVal Height As Single, ByVal SrcX As Single, ByVal SrcY As Single, ByVal Light As Long, ByVal TextureNum As Long, ByVal Degrees As Single) '***************************************************************** 'Renders a Grh in the form of a D3DXSprite instead of a rectangle (slower, less flexibility) 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_D3DXSprite '***************************************************************** Dim SrcRect As RECT Dim v2 As D3DVECTOR2 Dim v3 As D3DVECTOR2

   'End the old sprite we had going (only if the texture changed)
   If TextureNum <> LastTexture Then
       If SpriteBegun = 1 Then
           Sprite.End
           Sprite.Begin
       End If
   End If
   
   'Ready the texture
   Engine_ReadyTexture TextureNum
   
   'Create the source rectangle
   With SrcRect
       .Left = SrcX
       .Top = SrcY
       .Right = .Left + Width
       .bottom = .Top + Height
   End With
   
   'Create the rotation point
   If Degrees Then
       Degrees = ((Degrees + 180) * DegreeToRadian)
       If Degrees > 360 Then Degrees = Degrees - 360
       With v2
           .X = (Width * 0.5)
           .Y = (Height * 0.5)
       End With
   End If
   
   'Set the translation (location on the screen)
   v3.X = X
   v3.Y = Y
   'Draw the sprite
   If TextureNum > 0 Then
       Sprite.Draw SurfaceDB(TextureNum), SrcRect, SpriteScaleVector, v2, Degrees, v3, Light
   Else
       Sprite.Draw Nothing, SrcRect, SpriteScaleVector, v2, 0, v3, Light
   End If
   

End Sub

Private Sub Engine_Render_ChatBubble(ByVal Text As String, ByVal X As Integer, ByVal Y As Integer) '***************************************************************** 'Renders a chat bubble and the text for the given text and co-ordinates 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_ChatBubble '***************************************************************** Const BubbleSectionSize As Long = 6 'The width/height of each "sector" of the bubble in the graphic file Const RenderColor As Long = -1761607681 Dim TempGrh As Grh Dim BubbleWidth As Long Dim BubbleHeight As Long Dim TempSplit() As String Dim i As Long Dim j As Long

   If DisableChatBubbles Then Exit Sub
   'Set up the temp grh
   TempGrh.FrameCounter = 1
   TempGrh.Started = 1
   'Split up the string
   TempSplit = Split(Text, vbNewLine)
   
   '*** Calculate the bubble width and height ***
   If UBound(TempSplit) > 0 Then
   
       'If there are multiple lines, it is assumed it is the max width
       BubbleWidth = BubbleMaxWidth
       
       'Because there are multiple lines, we have to calculate the height, too
       BubbleHeight = Font_Default.CharHeight * (UBound(TempSplit) + 1)
       
   Else
   
       'Theres only one line, so that line is the width
       BubbleWidth = Engine_GetTextWidth(Font_Default, Text)
       BubbleHeight = Font_Default.CharHeight
       
   End If
   
   'Round the width and height to the nearest BubbleSectionSize (the size of each chat bubble side section)
   BubbleWidth = BubbleWidth + BubbleSectionSize
   If BubbleWidth Mod BubbleSectionSize Then BubbleWidth = BubbleWidth + (BubbleSectionSize - (BubbleWidth Mod BubbleSectionSize))
   If BubbleHeight Mod BubbleSectionSize Then BubbleHeight = BubbleHeight + (BubbleSectionSize - (BubbleHeight Mod BubbleSectionSize))
   'Modify the X and Y values the center the bubble
   X = X - (BubbleWidth * 0.5) + 16    'Center
   Y = Y - BubbleHeight - 20           'Align above the head
   '*** Draw the bubble ***
   'Top-left corner
   TempGrh.GrhIndex = 109
   Engine_Render_Grh TempGrh, X, Y, 0, 0, False, RenderColor, RenderColor, RenderColor, RenderColor
   
   'Top-right corner
   TempGrh.GrhIndex = 111
   Engine_Render_Grh TempGrh, X + BubbleWidth + BubbleSectionSize, Y, 0, 0, False, RenderColor, RenderColor, RenderColor, RenderColor
   
   'Bottom-left corner
   TempGrh.GrhIndex = 115
   Engine_Render_Grh TempGrh, X, Y + BubbleHeight + BubbleSectionSize, 0, 0, False, RenderColor, RenderColor, RenderColor, RenderColor
   
   'Bottom-right corner
   TempGrh.GrhIndex = 117
   Engine_Render_Grh TempGrh, X + BubbleWidth + BubbleSectionSize, Y + BubbleHeight + BubbleSectionSize, 0, 0, False, RenderColor, RenderColor, RenderColor, RenderColor
   
   'Top side
   TempGrh.GrhIndex = 110
   For i = 0 To (BubbleWidth \ BubbleSectionSize) - 1
       Engine_Render_Grh TempGrh, X + ((i + 1) * BubbleSectionSize), Y, 0, 0, False, RenderColor, RenderColor, RenderColor, RenderColor
   Next i
   
   'Left side
   TempGrh.GrhIndex = 112
   For i = 0 To (BubbleHeight \ BubbleSectionSize) - 1
       Engine_Render_Grh TempGrh, X, Y + ((i + 1) * BubbleSectionSize), 0, 0, False, RenderColor, RenderColor, RenderColor, RenderColor
   Next i
   
   'Right side
   TempGrh.GrhIndex = 114
   For i = 0 To (BubbleHeight \ BubbleSectionSize) - 1
       Engine_Render_Grh TempGrh, X + BubbleWidth + BubbleSectionSize, Y + ((i + 1) * BubbleSectionSize), 0, 0, False, RenderColor, RenderColor, RenderColor, RenderColor
   Next i
   'Bottom side
   TempGrh.GrhIndex = 116
   For i = 0 To (BubbleWidth \ BubbleSectionSize) - 1
       Engine_Render_Grh TempGrh, X + ((i + 1) * BubbleSectionSize), Y + BubbleHeight + BubbleSectionSize, 0, 0, False, RenderColor, RenderColor, RenderColor, RenderColor
   Next i
   'Middle
   TempGrh.GrhIndex = 113
   For i = 1 To (BubbleWidth \ BubbleSectionSize)
       For j = 1 To (BubbleHeight \ BubbleSectionSize)
           Engine_Render_Grh TempGrh, X + (i * BubbleSectionSize), Y + (j * BubbleSectionSize), 0, 0, False, RenderColor, RenderColor, RenderColor, RenderColor
       Next j
   Next i
   'Render the text (finally!)
   Engine_Render_Text Font_Default, Text, X + BubbleSectionSize, Y + BubbleSectionSize, D3DColorARGB(255, 0, 0, 0)

End Sub

Private Sub Engine_Render_GUI() '***************************************************************** 'Render the GUI 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_GUI '***************************************************************** Dim TempGrh As Grh Dim i As Long

   'Render the rest of the windows
   For i = NumGameWindows To 1 Step -1
       If i <> LastClickedWindow Then
           If ShowGameWindow(i) Then Engine_Render_GUI_Window i
       End If
   Next i
   'Render the last clicked window
   If LastClickedWindow > 0 Then
       If ShowGameWindow(LastClickedWindow) Then Engine_Render_GUI_Window LastClickedWindow
   End If
   'Render the spells list
   If DrawSkillList Then Engine_Render_Skills
   'Render an item where the cursor should be (item being dragged)
   If DragItemSlot Then
       
       Select Case DragSourceWindow
           Case InventoryWindow
               TempGrh.GrhIndex = UserInventory(DragItemSlot).GrhIndex
           Case ShopWindow
               TempGrh.GrhIndex = NPCTradeItems(DragItemSlot).GrhIndex
           Case BankWindow
               TempGrh.GrhIndex = UserBank(DragItemSlot).GrhIndex
       End Select
       'Draw
       TempGrh.FrameCounter = 1
       Engine_Render_Grh TempGrh, MousePos.X, MousePos.Y, 0, 0, False
       
   End If
   'Render the cursor
   If Not Windowed Then
       TempGrh.FrameCounter = 1
       TempGrh.GrhIndex = 69
       Engine_Render_Grh TempGrh, MousePos.X, MousePos.Y, 0, 0, False
   End If
   
   'Draw item description
   Engine_Render_ItemDesc

End Sub

Private Sub Engine_Render_GUI_Window(ByVal WindowIndex As Byte) '***************************************************************** 'Render a GUI window 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_GUI_Window '***************************************************************** Dim User1RenderColor As Long Dim User2RenderColor As Long Dim TempGrh As Grh Dim TempGrh2 As Grh Dim t As String Dim s() As String Dim i As Byte Dim j As Long

   TempGrh.FrameCounter = 1
   TempGrh2.FrameCounter = 1
   Select Case WindowIndex
       Case TradeWindow
           With GameWindow.Trade
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               
               If TradeTable.User1Accepted Then User1RenderColor = D3DColorARGB(255, 0, 255, 0) Else User1RenderColor = D3DColorARGB(255, 255, 255, 255)
               If TradeTable.User2Accepted Then User2RenderColor = D3DColorARGB(255, 0, 255, 0) Else User2RenderColor = D3DColorARGB(255, 255, 255, 255)
               Engine_Render_Text Font_Default, TradeTable.User1Name, .Screen.X + .User1Name.X, .Screen.Y + .User1Name.Y, User1RenderColor
               Engine_Render_Text Font_Default, TradeTable.User2Name, .Screen.X + .User2Name.X, .Screen.Y + .User2Name.Y, User2RenderColor
               
               For j = 1 To 9
               
                   TempGrh.GrhIndex = TradeTable.Trade1(j).Grh
                   TempGrh2.GrhIndex = TradeTable.Trade2(j).Grh
               
                   Engine_Render_Grh TempGrh, .Screen.X + .Trade1(j).X, .Screen.Y + .Trade1(j).Y, 0, 0, False, User1RenderColor, User1RenderColor, User1RenderColor, User1RenderColor
                   Engine_Render_Grh TempGrh2, .Screen.X + .Trade2(j).X, .Screen.Y + .Trade2(j).Y, 0, 0, False, User2RenderColor, User2RenderColor, User2RenderColor, User2RenderColor
                   
                   Engine_Render_Text Font_Default, TradeTable.Gold1, .Screen.X + .Gold1.X, .Screen.Y + .Gold1.Y, User1RenderColor
                   Engine_Render_Text Font_Default, TradeTable.Gold2, .Screen.X + .Gold2.X, .Screen.Y + .Gold2.Y, User2RenderColor
               
               Next j
                       
           End With
       
       Case NPCChatWindow
           With GameWindow.NPCChat
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               Engine_Render_Text Font_Default, ActiveAsk.QuestionTxt, .Screen.X + 5, .Screen.Y + 5, D3DColorARGB(255, 255, 255, 255)
               For i = 1 To .NumAnswers
                   Engine_Render_Text Font_Default, i & ". " & NPCChat(ActiveAsk.ChatIndex).Ask.Ask(ActiveAsk.AskIndex).Answer(i).Text, .Screen.X + .Answer(i).X, .Screen.Y + .Answer(i).Y, D3DColorARGB(255, 0, 255, 0)
               Next i
           End With
       
       Case StatWindow
           With GameWindow.StatWindow
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               Engine_Render_Text Font_Default, "Str: " & BaseStats(SID.Str) & " + " & ModStats(SID.Str) - BaseStats(SID.Str) & " (" & ModStats(SID.Str) & ")", .Screen.X + .Str.X, .Screen.Y + .Str.Y, -1
               Engine_Render_Text Font_Default, "Agi: " & BaseStats(SID.Agi) & " + " & ModStats(SID.Agi) - BaseStats(SID.Agi) & " (" & ModStats(SID.Agi) & ")", .Screen.X + .Agi.X, .Screen.Y + .Agi.Y, -1
               Engine_Render_Text Font_Default, "Mag: " & BaseStats(SID.Mag) & " + " & ModStats(SID.Mag) - BaseStats(SID.Mag) & " (" & ModStats(SID.Mag) & ")", .Screen.X + .Mag.X, .Screen.Y + .Mag.Y, -1
               If BaseStats(SID.Points) > 0 Then
                   Engine_Render_Grh .AddGrh, .Screen.X + .AddStr.X, .Screen.Y + .AddStr.Y, 0, 1
                   Engine_Render_Grh .AddGrh, .Screen.X + .AddAgi.X, .Screen.Y + .AddAgi.Y, 0, 1
                   Engine_Render_Grh .AddGrh, .Screen.X + .AddMag.X, .Screen.Y + .AddMag.Y, 0, 1
               End If
               Engine_Render_Text Font_Default, "Points: " & BaseStats(SID.Points), .Screen.X + .Points.X, .Screen.Y + .Points.Y, -1
               Engine_Render_Text Font_Default, "Gold: " & BaseStats(SID.Gold), .Screen.X + .Gold.X, .Screen.Y + .Gold.Y, -1
               Engine_Render_Text Font_Default, "Def: " & BaseStats(SID.DEF) & " + " & ModStats(SID.DEF) - BaseStats(SID.DEF) & " (" & ModStats(SID.DEF) & ")", .Screen.X + .DEF.X, .Screen.Y + .DEF.Y, -1
               Engine_Render_Text Font_Default, "Dmg: " & BaseStats(SID.MinHIT) & "+" & ModStats(SID.MinHIT) - BaseStats(SID.MinHIT) & " ~ " & BaseStats(SID.MaxHIT) & "+" & ModStats(SID.MaxHIT) - BaseStats(SID.MaxHIT) & " (" & ModStats(SID.MinHIT) & " ~ " & ModStats(SID.MaxHIT) & ")", .Screen.X + .Dmg.X, .Screen.Y + .Dmg.Y, -1
           End With
       
        Case ChatWindow
           With GameWindow.ChatWindow
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
           End With
           
           'Render the chat text
           Engine_Render_ChatTextBuffer
           
           'Draw entered text
           If EnterText = True Then
               If EnterTextBufferWidth = 0 Then EnterTextBufferWidth = 1   'Dividing by 0 is never good
               If LenB(ShownText) <> 0 Then Engine_Render_Text Font_Default, ShownText, GameWindow.ChatWindow.Screen.X + GameWindow.ChatWindow.Text.X, GameWindow.ChatWindow.Screen.Y + GameWindow.ChatWindow.Text.Y, FontColor_Talk
               If timeGetTime Mod CursorFlashRate * 2 < CursorFlashRate Then
                   TempGrh.GrhIndex = 39
                   TempGrh.FrameCounter = 1
                   TempGrh.Started = 1
                   Engine_Render_Grh TempGrh, GameWindow.ChatWindow.Screen.X + GameWindow.ChatWindow.Text.X + Engine_GetTextWidth(Font_Default, ShownText), GameWindow.ChatWindow.Screen.Y + GameWindow.ChatWindow.Text.Y, 0, 0, False
               End If
           End If
           
       Case MenuWindow
           With GameWindow.Menu
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
           End With
               
       Case QuickBarWindow
           With GameWindow.QuickBar
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               For i = 1 To 12
                   Select Case QuickBarID(i).Type
                   Case QuickBarType_Skill
                       TempGrh.GrhIndex = Engine_SkillIDtoGRHID(QuickBarID(i).ID)
                       If TempGrh.GrhIndex Then Engine_Render_Grh TempGrh, .Screen.X + .Image(i).X, .Screen.Y + .Image(i).Y, 0, 0, False
                   Case QuickBarType_Item
                       TempGrh.GrhIndex = UserInventory(QuickBarID(i).ID).GrhIndex
                       If TempGrh.GrhIndex Then Engine_Render_Grh TempGrh, .Screen.X + .Image(i).X, .Screen.Y + .Image(i).Y, 0, 0, False
                   End Select
               Next i
           End With
   
       Case InventoryWindow
           With GameWindow.Inventory
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               Engine_Render_Inventory
           End With
   
       Case ShopWindow
           With GameWindow.Shop
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               Engine_Render_Inventory 2
           End With
       
       Case BankWindow
           With GameWindow.Bank
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               Engine_Render_Inventory 3
           End With
   
       Case MailboxWindow
           With GameWindow.Mailbox
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               Engine_Render_Text Font_Default, MailboxListBuffer, .Screen.X + .List.X, .Screen.Y + .List.Y, -1
               Engine_Render_Text Font_Default, "Read", .Screen.X + .ReadLbl.X, .Screen.Y + .ReadLbl.Y, -1
               Engine_Render_Text Font_Default, "Write", .Screen.X + .WriteLbl.X, .Screen.Y + .WriteLbl.Y, -1
               Engine_Render_Text Font_Default, "Delete", .Screen.X + .DeleteLbl.X, .Screen.Y + .DeleteLbl.Y, -1
               If SelMessage > 0 Then Engine_Render_Rectangle .Screen.X + .List.X, .Screen.Y + .List.Y + ((SelMessage - 1) * Font_Default.CharHeight), .List.Width, Font_Default.CharHeight, 1, 1, 1, 1, 1, 1, 0, 0, 2097217280, 2097217280, 2097217280, 2097217280, , False   'ARGB: 125/0/255/0
           End With
   
       Case ViewMessageWindow
           With GameWindow.ViewMessage
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               Engine_Render_Text Font_Default, ReadMailData.WriterName, .Screen.X + .From.X, .Screen.Y + .From.Y, -1
               Engine_Render_Text Font_Default, ReadMailData.Subject, .Screen.X + .Subject.X, .Screen.Y + .Subject.Y, -1
               Engine_Render_Text Font_Default, ReadMailData.Message, .Screen.X + .Message.X, .Screen.Y + .Message.Y, -1
               For i = 1 To MaxMailObjs
                   If ReadMailData.Obj(i) > 0 Then
                       TempGrh.GrhIndex = ReadMailData.Obj(i)
                       Engine_Render_Grh TempGrh, .Screen.X + .Image(i).X, .Screen.Y + .Image(i).Y, 0, 0, False
                   End If
               Next i
           End With
   
       Case WriteMessageWindow
           With GameWindow.WriteMessage
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               
               '"To" text box
               If LenB(WriteMailData.RecieverName) <> 0 Then Engine_Render_Text Font_Default, WriteMailData.RecieverName, .Screen.X + .From.X, .Screen.Y + .From.Y, -1
               If WMSelCon = wmFrom Then
                   If timeGetTime Mod CursorFlashRate * 2 < CursorFlashRate Then
                       TempGrh.GrhIndex = 39
                       Engine_Render_Grh TempGrh, .Screen.X + .From.X + Engine_GetTextWidth(Font_Default, WriteMailData.RecieverName), .Screen.Y + .From.Y, 0, 0, False
                   End If
               End If
               'Subject text box
               If LenB(WriteMailData.Subject) <> 0 Then Engine_Render_Text Font_Default, WriteMailData.Subject, .Screen.X + .Subject.X, .Screen.Y + .Subject.Y, -1
               If WMSelCon = wmSubject Then
                   If timeGetTime Mod CursorFlashRate * 2 < CursorFlashRate Then
                       TempGrh.GrhIndex = 39
                       Engine_Render_Grh TempGrh, .Screen.X + .Subject.X + Engine_GetTextWidth(Font_Default, WriteMailData.Subject), .Screen.Y + .Subject.Y, 0, 0, False
                   End If
               End If
               'Message body text box
               t = Engine_WordWrap(WriteMailData.Message, GameWindow.WriteMessage.Message.Width)
               If LenB(WriteMailData.Message) <> 0 Then Engine_Render_Text Font_Default, t, .Screen.X + .Message.X, .Screen.Y + .Message.Y, -1
               If WMSelCon = wmMessage Then
                   If timeGetTime Mod CursorFlashRate * 2 < CursorFlashRate Then
                       If InStr(1, t, vbNewLine) Then
                           s = Split(t, vbNewLine)
                           i = UBound(s)
                           j = Engine_GetTextWidth(Font_Default, s(i))
                       Else
                           i = 0   'Ubound
                           j = Engine_GetTextWidth(Font_Default, t) 'Size
                       End If
                       TempGrh.GrhIndex = 39
                       Engine_Render_Grh TempGrh, .Screen.X + .Message.X + j, .Screen.Y + .Message.Y + (i * Font_Default.CharHeight), 0, 0, False
                   End If
               End If
               'Objects
               For i = 1 To MaxMailObjs
                   If WriteMailData.ObjIndex(i) > 0 Then
                       TempGrh.GrhIndex = UserInventory(WriteMailData.ObjIndex(i)).GrhIndex
                       Engine_Render_Grh TempGrh, .Screen.X + .Image(i).X, .Screen.Y + .Image(i).Y, 0, 0, False
                   End If
               Next i
               
           End With
   
       Case AmountWindow
           With GameWindow.Amount
               Engine_Render_Grh .SkinGrh, .Screen.X, .Screen.Y, 0, 1, True, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
               If LenB(AmountWindowValue) <> 0 Then Engine_Render_Text Font_Default, AmountWindowValue, .Screen.X + .Value.X, .Screen.Y + .Value.Y, -1
           End With
   End Select

End Sub

Public Sub Engine_Render_Inventory(Optional ByVal InventoryType As Long = 1) '***************************************************************** 'Renders the inventory 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_Inventory '***************************************************************** Dim TempGrh As Grh Dim DestX As Single Dim DestY As Single Dim LoopC As Long

   Select Case InventoryType
       'User inventory
   Case 1
       For LoopC = 1 To MAX_INVENTORY_SLOTS
           If UserInventory(LoopC).GrhIndex Then
               DestX = GameWindow.Inventory.Screen.X + GameWindow.Inventory.Image(LoopC).X
               DestY = GameWindow.Inventory.Screen.Y + GameWindow.Inventory.Image(LoopC).Y
               TempGrh.FrameCounter = 1
               TempGrh.GrhIndex = UserInventory(LoopC).GrhIndex
               If DragItemSlot = LoopC And DragSourceWindow = InventoryWindow Then
                   Engine_Render_Grh TempGrh, DestX, DestY, 0, 0, False, -1761607681, -1761607681, -1761607681, -1761607681    'ARGB 150/255/255/255
               Else
                   Engine_Render_Grh TempGrh, DestX, DestY, 0, 0, False
               End If
               'If UserInventory(LoopC).Amount > 1 Then
                   Engine_Render_Text Font_Default, UserInventory(LoopC).Amount, DestX, DestY, -1
               'End If
               If UserInventory(LoopC).Equipped Then Engine_Render_Text Font_Default, "E", DestX + (30 - Engine_GetTextWidth(Font_Default, "E")), DestY, -16711936
           End If
       Next LoopC
       'Shop inventory
   Case 2
       For LoopC = 1 To NPCTradeItemArraySize
           If NPCTradeItems(LoopC).GrhIndex Then
               DestX = GameWindow.Shop.Screen.X + GameWindow.Shop.Image(LoopC).X
               DestY = GameWindow.Shop.Screen.Y + GameWindow.Shop.Image(LoopC).Y
               TempGrh.FrameCounter = 1
               TempGrh.GrhIndex = NPCTradeItems(LoopC).GrhIndex
               If DragItemSlot = LoopC And DragSourceWindow = ShopWindow Then
                   Engine_Render_Grh TempGrh, DestX, DestY, 0, 0, False, -1761607681, -1761607681, -1761607681, -1761607681    'ARGB 150/255/255/255
               Else
                   Engine_Render_Grh TempGrh, DestX, DestY, 0, 0, False
               End If
           End If
       Next LoopC
       'Bank inventory
   Case 3
       For LoopC = 1 To MAX_INVENTORY_SLOTS
           If UserBank(LoopC).GrhIndex Then
               DestX = GameWindow.Bank.Screen.X + GameWindow.Bank.Image(LoopC).X
               DestY = GameWindow.Bank.Screen.Y + GameWindow.Bank.Image(LoopC).Y
               TempGrh.FrameCounter = 1
               TempGrh.GrhIndex = UserBank(LoopC).GrhIndex
               If DragItemSlot = LoopC And DragSourceWindow = BankWindow Then
                   Engine_Render_Grh TempGrh, DestX, DestY, 0, 0, False, -1761607681, -1761607681, -1761607681, -1761607681    'ARGB 150/255/255/255
               Else
                   Engine_Render_Grh TempGrh, DestX, DestY, 0, 0, False
               End If
               If UserBank(LoopC).Amount <> -1 Then Engine_Render_Text Font_Default, UserBank(LoopC).Amount, DestX, DestY, -1
           End If
       Next LoopC
   End Select

End Sub

Private Sub Engine_Render_ItemDesc() '************************************************************ 'Draw description text 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_ItemDesc '************************************************************ Dim X As Integer Dim Y As Integer Dim i As Byte

'Check if the description text is there

   If ItemDescLines = 0 Then Exit Sub
   'Check the description position
   X = MousePos.X
   Y = MousePos.Y
   If X < 0 Then X = 0
   If X + ItemDescWidth > ScreenWidth Then X = ScreenWidth - ItemDescWidth
   If Y < 0 Then Y = 0
   If Y + (ItemDescLines * Font_Default.CharHeight) > ScreenHeight Then Y = ScreenHeight - (ItemDescLines * Font_Default.CharHeight)
   'Draw backdrop
   Engine_Render_Rectangle X - 5, Y - 5, ItemDescWidth + 10, (Font_Default.CharHeight * ItemDescLines) + 10, 1, 1, 1, 1, 1, 1, 0, 0, -1761607681, -1761607681, -1761607681, -1761607681, , False
   'Draw text
   For i = 1 To ItemDescLines
       Engine_Render_Text Font_Default, ItemDescLine(i), X, Y + ((i - 1) * Font_Default.CharHeight), -16777216
   Next i

End Sub

Private Sub Engine_ReadyTexture(ByVal TextureNum As Long) '************************************************************ 'Gets a texture ready to for usage 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_ReadyTexture '************************************************************

   'Load the surface into memory if it is not in memory and reset the timer
   If TextureNum > 0 Then
       If SurfaceTimer(TextureNum) < timeGetTime Then Engine_Init_Texture TextureNum
       SurfaceTimer(TextureNum) = timeGetTime + SurfaceTimerMax
   End If
   
   'Check what render method we're using
   If AlternateRender Then
   
       'Set the texture
       LastTexture = TextureNum
       If TextureNum <= 0 Then D3DDevice.SetTexture 0, Nothing
       
   Else
   
       'Set the texture
       If TextureNum <= 0 Then
           D3DDevice.SetTexture 0, Nothing
           LastTexture = 0
       Else
           If LastTexture <> TextureNum Then
               D3DDevice.SetTexture 0, SurfaceDB(TextureNum)
               LastTexture = TextureNum
           End If
       End If
       
   End If

End Sub

Sub Engine_Render_Rectangle(ByVal X As Single, ByVal Y As Single, ByVal Width As Single, ByVal Height As Single, ByVal SrcX As Single, ByVal SrcY As Single, ByVal SrcWidth As Single, ByVal SrcHeight As Single, Optional ByVal SrcBitmapWidth As Long = -1, Optional ByVal SrcBitmapHeight As Long = -1, Optional ByVal Degrees As Single = 0, Optional ByVal TextureNum As Long, Optional ByVal Color0 As Long = -1, Optional ByVal Color1 As Long = -1, Optional ByVal Color2 As Long = -1, Optional ByVal Color3 As Long = -1, Optional ByVal Shadow As Byte = 0, Optional ByVal InBoundsCheck As Boolean = True) '************************************************************ 'Render a square/rectangle based on the specified values then rotate it if needed 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_Rectangle '************************************************************ Dim VertexArray(0 To 3) As TLVERTEX Dim RadAngle As Single 'The angle in Radians Dim CenterX As Single Dim CenterY As Single Dim Index As Integer Dim NewX As Single Dim NewY As Single Dim SinRad As Single Dim CosRad As Single Dim ShadowAdd As Single Dim L As Single

   'Perform in-bounds check if needed
   If InBoundsCheck Then
       If X + SrcWidth <= 0 Then Exit Sub
       If Y + SrcHeight <= 0 Then Exit Sub
       If X >= ScreenWidth Then Exit Sub
       If Y >= ScreenHeight Then Exit Sub
   End If
   'Ready the texture
   Engine_ReadyTexture TextureNum
   'Set the bitmap dimensions if needed
   If SrcBitmapWidth = -1 Then SrcBitmapWidth = SurfaceSize(TextureNum).X
   If SrcBitmapHeight = -1 Then SrcBitmapHeight = SurfaceSize(TextureNum).Y
   
   'Set the RHWs (must always be 1)
   VertexArray(0).Rhw = 1
   VertexArray(1).Rhw = 1
   VertexArray(2).Rhw = 1
   VertexArray(3).Rhw = 1
   
   'Apply the colors
   VertexArray(0).Color = Color0
   VertexArray(1).Color = Color1
   VertexArray(2).Color = Color2
   VertexArray(3).Color = Color3
   If Shadow Then
       'To make things easy, we just do a completely separate calculation the top two points
       ' with an uncropped tU / tV algorithm
       VertexArray(0).X = X + (Width * 0.5)
       VertexArray(0).Y = Y - (Height * 0.5)
       VertexArray(0).tU = (SrcX / SrcBitmapWidth)
       VertexArray(0).tV = (SrcY / SrcBitmapHeight)
       
       VertexArray(1).X = VertexArray(0).X + Width
       VertexArray(1).tU = ((SrcX + Width) / SrcBitmapWidth)
       VertexArray(2).X = X
       VertexArray(2).tU = (SrcX / SrcBitmapWidth)
       VertexArray(3).X = X + Width
       VertexArray(3).tU = (SrcX + SrcWidth + ShadowAdd) / SrcBitmapWidth
   Else
       
       '------------------------------------------------------------------------------------------------------
       '------------------------------------------------------------------------------------------------------
       'If the image is partially outside of the screen, it is trimmed so only that which is in the screen is drawn
       'This provides for quite a decent FPS boost if you have lots of tiles that stretch outside of the view area
       'Important: Something about this doesn't seem to be functioning correctly. It is supposed to crop down the
       'image and only draw that which is going to be in the screen, but it doesn't work right and I have no
       'idea why. Uncomment the lines to see what happens. I have given up on this since the FPS boost really isn't
       'significant for me to put any more work into it, but if someone could fix it, it would definitely be
       'added back into the engine.
       '------------------------------------------------------------------------------------------------------
       '------------------------------------------------------------------------------------------------------
       'If X < 0 Then
       '    SrcX = SrcX - X
       '    SrcWidth = SrcWidth + X
       '    Width = Width + X
       '    X = 0
       'End If
       'If Y < 0 Then
       '    SrcY = SrcY - Y
       '    SrcHeight = SrcHeight + Y
       '    Height = Height + Y
       '    Y = 0
       'End If
       'If X + Width > ScreenWidth Then
       '    L = X + Width - ScreenWidth
       '    Width = Width - L
       '    SrcWidth = SrcWidth - L
       'End If
       'If Y + Height > ScreenHeight Then
       '    L = Y + Height - ScreenHeight
       '    Height = Height - L
       '    SrcHeight = SrcHeight - L
       'End If
       '------------------------------------------------------------------------------------------------------
       '------------------------------------------------------------------------------------------------------
       
       'If we are NOT using shadows, then we add +1 to the width/height (trust me, just do it)
       ShadowAdd = 1
       'Find the left side of the rectangle
       VertexArray(0).X = X
       VertexArray(0).tU = (SrcX / SrcBitmapWidth)
       'Find the top side of the rectangle
       VertexArray(0).Y = Y
       VertexArray(0).tV = (SrcY / SrcBitmapHeight)
   
       'Find the right side of the rectangle
       VertexArray(1).X = X + Width
       VertexArray(1).tU = (SrcX + SrcWidth + ShadowAdd) / SrcBitmapWidth
       'These values will only equal each other when not a shadow
       VertexArray(2).X = VertexArray(0).X
       VertexArray(3).X = VertexArray(1).X
       
   End If
   
   'Find the bottom of the rectangle
   VertexArray(2).Y = Y + Height
   VertexArray(2).tV = (SrcY + SrcHeight + ShadowAdd) / SrcBitmapHeight
   'Because this is a perfect rectangle, all of the values below will equal one of the values we already got
   VertexArray(1).Y = VertexArray(0).Y
   VertexArray(1).tV = VertexArray(0).tV
   VertexArray(2).tU = VertexArray(0).tU
   VertexArray(3).Y = VertexArray(2).Y
   VertexArray(3).tU = VertexArray(1).tU
   VertexArray(3).tV = VertexArray(2).tV
   
   'Check if a rotation is required
   If Degrees <> 0 And Degrees <> 360 Then
       'Converts the angle to rotate by into radians
       RadAngle = Degrees * DegreeToRadian
       'Set the CenterX and CenterY values
       CenterX = X + (Width * 0.5)
       CenterY = Y + (Height * 0.5)
       'Pre-calculate the cosine and sine of the radiant
       SinRad = Sin(RadAngle)
       CosRad = Cos(RadAngle)
       'Loops through the passed vertex buffer
       For Index = 0 To 3
           'Calculates the new X and Y co-ordinates of the vertices for the given angle around the center co-ordinates
           NewX = CenterX + (VertexArray(Index).X - CenterX) * CosRad - (VertexArray(Index).Y - CenterY) * SinRad
           NewY = CenterY + (VertexArray(Index).Y - CenterY) * CosRad + (VertexArray(Index).X - CenterX) * SinRad
           'Applies the new co-ordinates to the buffer
           VertexArray(Index).X = NewX
           VertexArray(Index).Y = NewY
       Next Index
   End If
   'Render the texture to the device
   D3DDevice.DrawPrimitiveUP D3DPT_TRIANGLESTRIP, 2, VertexArray(0), FVF_Size

End Sub

Public Sub Engine_CreateTileLayers() '************************************************************ 'Creates the tile layers used for rendering the tiles so they can be drawn faster 'Has to happen every time the user warps or moves a whole tile 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_CreateTileLayers '************************************************************ Dim Layer As Byte Dim ScreenX As Long Dim ScreenY As Long Dim tBuf As Integer Dim pX As Integer Dim pY As Integer Dim X As Long Dim Y As Long

   'Raise the buffer up + 1 to prevent graphical errors
   tBuf = TileBufferSize '+ 1
   
   'Loop through each layer and check which tiles there are that will need to be drawn
   For Layer = 1 To 6
       
       'Clear the number of tiles
       TileLayer(Layer).NumTiles = 0
       
       'Allocate enough memory for all the tiles
       ReDim TileLayer(Layer).Tile(1 To ((maxY - minY + 1) * (maxX - minX + 1)))
       
       'Loop through all the tiles within the buffer's range
       ScreenY = (10 - tBuf)
       For Y = minY To maxY
           ScreenX = (10 - tBuf)
           For X = minX To maxX
           
               'Check that the tile is in the range of the map
               If X >= 1 Then
                   If Y >= 1 Then
                       If X <= MapInfo.Width Then
                           If Y <= MapInfo.Height Then
                       
                               'Check if the tile even has a graphic on it
                               If MapData(X, Y).Graphic(Layer).GrhIndex Then
                               
                                   'Calculate the pixel values
                                   pX = Engine_PixelPosX(ScreenX) - 288
                                   pY = Engine_PixelPosY(ScreenY) - 288
                                   
                                   'Check that the tile is in the screen
                                   With GrhData(MapData(X, Y).Graphic(Layer).GrhIndex)
                                       If pX >= -.pixelWidth Then
                                           If pX <= ScreenWidth + .pixelWidth Then
                                               If pY >= -.pixelHeight Then
                                                   If pY <= ScreenHeight + .pixelHeight Then
                                                       
                                                       'The tile is valid to be used, so raise the count
                                                       TileLayer(Layer).NumTiles = TileLayer(Layer).NumTiles + 1
                                                       
                                                       'Store the needed information
                                                       TileLayer(Layer).Tile(TileLayer(Layer).NumTiles).TileX = X
                                                       TileLayer(Layer).Tile(TileLayer(Layer).NumTiles).TileY = Y
                                                       TileLayer(Layer).Tile(TileLayer(Layer).NumTiles).PixelPosX = pX + 288
                                                       TileLayer(Layer).Tile(TileLayer(Layer).NumTiles).PixelPosY = pY + 288
   
                                                   End If
                                               End If
                                           End If
                                       End If
                                   End With
   
                               End If
                               
                           End If
                       End If
                   End If
               End If
               ScreenX = ScreenX + 1
           Next X
           ScreenY = ScreenY + 1
       Next Y
   
       'We got all the information we need, now resize the array as small as possible to save RAM, then do the same for every other layer :o
       If TileLayer(Layer).NumTiles > 0 Then
           ReDim Preserve TileLayer(Layer).Tile(1 To TileLayer(Layer).NumTiles)
       Else
           Erase TileLayer(Layer).Tile
       End If
       
   Next Layer
       

End Sub

Sub Engine_Render_Screen(ByVal TileX As Integer, ByVal TileY As Integer, ByVal PixelOffsetX As Integer, ByVal PixelOffsetY As Integer) '************************************************************ 'Draw current visible to scratch area based on TileX and TileY 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_Screen '************************************************************ Dim FrameUseMotionBlur As Boolean 'Lets us know if this frame is using motion blur so we don't have to leave support for it on Dim LightOffset As Long Dim ChrID() As Integer Dim ChrY() As Integer Dim Y As Long 'Keeps track of where on map we are Dim X As Long Dim j As Long Dim Angle As Single Dim Layer As Byte

   'Check for valid positions
   If UserPos.X = 0 Then Exit Sub
   If UserPos.Y = 0 Then Exit Sub
   If UserCharIndex = 0 Then Exit Sub
   
   'Check if we need to update the graphics
   If TileX <> LastTileX Or TileY <> LastTileY Then
   
       'Figure out Ends and Starts of screen
       ScreenMinY = TileY - (WindowTileHeight \ 2)
       ScreenMaxY = TileY + (WindowTileHeight \ 2)
       ScreenMinX = TileX - (WindowTileWidth \ 2)
       ScreenMaxX = TileX + (WindowTileWidth \ 2)
       minY = ScreenMinY - TileBufferSize
       maxY = ScreenMaxY + TileBufferSize
       minX = ScreenMinX - TileBufferSize
       maxX = ScreenMaxX + TileBufferSize
       
       'Update the last position
       LastTileX = TileX
       LastTileY = TileY
       
       'Re-create the tile layers
       Engine_CreateTileLayers
       
   End If
   
   'Calculate the particle offset values
   'Do NOT move this any farther down in the module or you will get "jumps" as the left/top borders on particles
   ParticleOffsetX = (Engine_PixelPosX(ScreenMinX) - PixelOffsetX)
   ParticleOffsetY = (Engine_PixelPosY(ScreenMinY) - PixelOffsetY)
   'Check if we have the device
   If D3DDevice.TestCooperativeLevel <> D3D_OK Then
       
       'The worst we can do at this point is avoid an error we can't fix!
       On Error Resume Next
       
       'Do a loop while device is lost
       If D3DDevice.TestCooperativeLevel = D3DERR_DEVICELOST Then Exit Sub
           
       'Clear all the textures
       LastTexture = -999
       For j = 1 To NumGrhFiles
           Set SurfaceDB(j) = Nothing
           SurfaceTimer(j) = 0
           SurfaceSize(j).X = 0
           SurfaceSize(j).Y = 0
       Next j
       
       'Clear the D3DXSprite
       If AlternateRenderDefault = 1 Or AlternateRenderMap = 1 Or AlternateRenderText = 1 Then
           SpriteBegun = 0
           Set Sprite = Nothing
           Set Sprite = D3DX.CreateSprite(D3DDevice)
       End If
       
       Set DeviceBuffer = Nothing
       Set DeviceStencil = Nothing
       Set BlurStencil = Nothing
       Set BlurTexture = Nothing
       Set BlurSurf = Nothing
       
       'Make sure the scene is ended
       D3DDevice.EndScene
       
       'Reset the device
       D3DDevice.Reset D3DWindow
       
       Set DeviceBuffer = D3DDevice.GetRenderTarget
       Set DeviceStencil = D3DDevice.GetDepthStencilSurface
       Set BlurStencil = D3DDevice.CreateDepthStencilSurface(BufferWidth, BufferHeight, D3DFMT_D16, D3DMULTISAMPLE_NONE)
       Set BlurTexture = D3DX.CreateTexture(D3DDevice, BufferWidth, BufferHeight, 0, D3DUSAGE_RENDERTARGET, DispMode.Format, D3DPOOL_DEFAULT)
       Set BlurSurf = BlurTexture.GetSurfaceLevel(0)
       
       'Reset the render states
       Engine_Init_RenderStates
       
       'Load the particle textures
       Engine_Init_ParticleEngine True
       
       On Error GoTo 0
   Else
   
       'We have to bypass the present the first time through here or else we get an error
       If NotFirstRender Then
       
           'Close off the last sprite
           If SpriteBegun Then
               Sprite.End
               SpriteBegun = 0
               LastTexture = -101
           End If
           With D3DDevice
               
               'End the rendering (scene)
               .EndScene
               
               'Flip the backbuffer to the screen
               .Present ByVal 0, ByVal 0, 0, ByVal 0
               
           End With
               
       Else
       
           'Set NotFirstRender to True so we can start displaying
           NotFirstRender = True
           
       End If
   
   End If
   
   'Check if running (turn on motion blur)
   If UseMotionBlur Then
       If UserCharIndex > 0 Then
           If CharList(UserCharIndex).Moving = 1 And CharList(UserCharIndex).Running Then
               BlurIntensity = 45
           Else
               If BlurIntensity < 255 Then
                   BlurIntensity = BlurIntensity + (ElapsedTime * 0.8)
                   If BlurIntensity > 255 Then BlurIntensity = 255
               End If
           End If
       End If
   End If
   
   'Set the motion blur if needed
   If UseMotionBlur Then
       If BlurIntensity < 255 Or ZoomLevel > 0 Then
           FrameUseMotionBlur = True
           D3DDevice.SetRenderTarget BlurSurf, BlurStencil, 0
       End If
   End If
   'Begin the scene
   D3DDevice.BeginScene
   
   'Clear the screen with a solid color (to prevent artifacts)
   D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET, 0, 1#, 0
   
   '************** Layer 1 to 3 **************
   'Set the alternate rendering for the map on / off
   AlternateRender = AlternateRenderMap
   
   'Loop through the lower 3 layers
   For Layer = 1 To 3
       LightOffset = ((Layer - 1) * 4) + 1
       
       'Loop through all the tiles we know we will draw for this layer
       For j = 1 To TileLayer(Layer).NumTiles
           With TileLayer(Layer).Tile(j)
               
               'Check if we have to draw with a shadow or not (slighty changes because we have to animate on the shadow, not the main render)
               If MapData(.TileX, .TileY).Shadow(Layer) = 1 Then
                   Engine_Render_Grh MapData(.TileX, .TileY).Graphic(Layer), .PixelPosX + PixelOffsetX, .PixelPosY + PixelOffsetY, 0, 1, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
                   Engine_Render_Grh MapData(.TileX, .TileY).Graphic(Layer), .PixelPosX + PixelOffsetX, .PixelPosY + PixelOffsetY, 0, 0, True, MapData(.TileX, .TileY).Light(LightOffset), MapData(.TileX, .TileY).Light(LightOffset + 1), MapData(.TileX, .TileY).Light(LightOffset + 2), MapData(.TileX, .TileY).Light(LightOffset + 3)
               Else
                   Engine_Render_Grh MapData(.TileX, .TileY).Graphic(Layer), .PixelPosX + PixelOffsetX, .PixelPosY + PixelOffsetY, 0, 1, True, MapData(.TileX, .TileY).Light(LightOffset), MapData(.TileX, .TileY).Light(LightOffset + 1), MapData(.TileX, .TileY).Light(LightOffset + 2), MapData(.TileX, .TileY).Light(LightOffset + 3)
               End If
               
           End With
       Next j
       
   Next Layer
   
   'Set the alternate rendering back to what it was before
   AlternateRender = AlternateRenderDefault
   '************** Objects **************
   For j = 1 To LastObj
       If OBJList(j).Grh.GrhIndex Then
           X = Engine_PixelPosX(OBJList(j).Pos.X - minX) + PixelOffsetX + OBJList(j).Offset.X + TileBufferOffset
           Y = Engine_PixelPosY(OBJList(j).Pos.Y - minY) + PixelOffsetY + OBJList(j).Offset.Y + TileBufferOffset
           If Y >= -32 Then
               If Y <= (ScreenHeight + 32) Then
                   If X >= -32 Then
                       If X <= (ScreenWidth + 32) Then
                           Engine_Render_Grh OBJList(j).Grh, X, Y, 1, 1, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
                           Engine_Render_Grh OBJList(j).Grh, X, Y, 1, 1, True, MapData(OBJList(j).Pos.X, OBJList(j).Pos.Y).Light(1), _
                               MapData(OBJList(j).Pos.X, OBJList(j).Pos.Y).Light(2), MapData(OBJList(j).Pos.X, OBJList(j).Pos.Y).Light(3), _
                               MapData(OBJList(j).Pos.X, OBJList(j).Pos.Y).Light(4)
                       End If
                   End If
               End If
           End If
       End If
   Next j
   '************** Characters **************
   'Size the to the smallest safe size (LastChar)
   ReDim ChrID(1 To LastChar)
   ReDim ChrY(1 To LastChar)
   'Fill the array
   For j = 1 To LastChar
       ChrY(j) = CharList(j).Pos.Y
       ChrID(j) = j
   Next j
   'Sort the char list
   Engine_SortIntArray ChrY, ChrID, 1, LastChar

   'Loop through the sorted characters
   For j = 1 To LastChar
       If CharList(ChrID(j)).Active Then
           X = Engine_PixelPosX(CharList(ChrID(j)).Pos.X - minX) + PixelOffsetX + TileBufferOffset
           Y = Engine_PixelPosY(CharList(ChrID(j)).Pos.Y - minY) + PixelOffsetY + TileBufferOffset
           
           If Y >= -32 And Y <= (ScreenHeight + 32) And X >= -32 And X <= (ScreenWidth + 32) Then
               
               'Update the NPC chat
               Engine_NPCChat_Update ChrID(j)
           
               'Draw the character
               Engine_Render_Char ChrID(j), X, Y
               
           Else
               
               'Update just the real position
               CharList(ChrID(j)).RealPos.X = X + CharList(ChrID(j)).MoveOffset.X
               CharList(ChrID(j)).RealPos.Y = Y + CharList(ChrID(j)).MoveOffset.Y
           
           End If
       End If
   Next j
   '************** Layer 4 to 6 **************
   AlternateRender = AlternateRenderMap
   For Layer = 4 To 6
       LightOffset = ((Layer - 1) * 4) + 1
       For j = 1 To TileLayer(Layer).NumTiles
           With TileLayer(Layer).Tile(j)
               If MapData(.TileX, .TileY).Shadow(Layer) = 1 Then
                   Engine_Render_Grh MapData(.TileX, .TileY).Graphic(Layer), .PixelPosX + PixelOffsetX, .PixelPosY + PixelOffsetY, 0, 1, True, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
                   Engine_Render_Grh MapData(.TileX, .TileY).Graphic(Layer), .PixelPosX + PixelOffsetX, .PixelPosY + PixelOffsetY, 0, 0, True, MapData(.TileX, .TileY).Light(LightOffset), MapData(.TileX, .TileY).Light(LightOffset + 1), MapData(.TileX, .TileY).Light(LightOffset + 2), MapData(.TileX, .TileY).Light(LightOffset + 3)
               Else
                   Engine_Render_Grh MapData(.TileX, .TileY).Graphic(Layer), .PixelPosX + PixelOffsetX, .PixelPosY + PixelOffsetY, 0, 1, True, MapData(.TileX, .TileY).Light(LightOffset), MapData(.TileX, .TileY).Light(LightOffset + 1), MapData(.TileX, .TileY).Light(LightOffset + 2), MapData(.TileX, .TileY).Light(LightOffset + 3)
               End If
           End With
       Next j
   Next Layer
   AlternateRender = AlternateRenderDefault
   
   '************** Effects **************
   
   'Loop to do drawing
   If LastEffect > 0 Then
       For j = 1 To LastEffect
           If EffectList(j).Grh.GrhIndex Then
               X = Engine_PixelPosX(EffectList(j).Pos.X - minX) + PixelOffsetX + TileBufferOffset
               Y = Engine_PixelPosY(EffectList(j).Pos.Y - minY) + PixelOffsetY + TileBufferOffset
               If EffectList(j).Time <> 0 And EffectList(j).Time < timeGetTime Then
               
                   'Timer ran out
                   Engine_Effect_Erase j
                   
               ElseIf Y >= -32 And Y <= (ScreenHeight + 32) And X >= -32 And X <= (ScreenWidth + 32) Then
               
                   'Timer or animation going, render
                   Engine_Render_Grh EffectList(j).Grh, X, Y, 1, 1, 0, , , , , , EffectList(j).Angle
                   
                   'Check if the animation is still running
                   If EffectList(j).Animated = 1 Then
                       If EffectList(j).Grh.Started = 0 Then Engine_Effect_Erase j
                   End If
                   
               Else
               
                   'Animation is going but not in screen, update the animation frame
                   Engine_UpdateGrh EffectList(j).Grh, False
                   
                   'Check if the animation is still running
                   If EffectList(j).Animated = 1 Then
                       If EffectList(j).Grh.Started = 0 Then Engine_Effect_Erase j
                   End If
                   
               End If
           End If
       Next j
   End If
   
   '************** Projectiles **************
   'Loop to do drawing
   If LastProjectile > 0 Then
       For j = 1 To LastProjectile
           If ProjectileList(j).Grh.GrhIndex Then
               
               'Update the position
               Angle = DegreeToRadian * Engine_GetAngle(ProjectileList(j).X, ProjectileList(j).Y, ProjectileList(j).tX, ProjectileList(j).tY)
               ProjectileList(j).X = ProjectileList(j).X + (Sin(Angle) * ElapsedTime * 0.63)
               ProjectileList(j).Y = ProjectileList(j).Y - (Cos(Angle) * ElapsedTime * 0.63)
               
               'Update the rotation
               If ProjectileList(j).RotateSpeed > 0 Then
                   ProjectileList(j).Rotate = ProjectileList(j).Rotate + (ProjectileList(j).RotateSpeed * ElapsedTime * 0.01)
                   Do While ProjectileList(j).Rotate > 360
                       ProjectileList(j).Rotate = ProjectileList(j).Rotate - 360
                   Loop
               End If
               'Draw if within range
               X = ((-minX - 1) * 32) + ProjectileList(j).X + PixelOffsetX + TileBufferOffset
               Y = ((-minY - 1) * 32) + ProjectileList(j).Y + PixelOffsetY + TileBufferOffset
               If Y >= -32 Then
                   If Y <= (ScreenHeight + 32) Then
                       If X >= -32 Then
                           If X <= (ScreenWidth + 32) Then
                               If ProjectileList(j).Rotate = 0 Then
                                   Engine_Render_Grh ProjectileList(j).Grh, X, Y, 0, 1, 1, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1
                                   Engine_Render_Grh ProjectileList(j).Grh, X, Y, 0, 0, 1
                               Else
                                   Engine_Render_Grh ProjectileList(j).Grh, X, Y, 0, 0, 1, ShadowColor, ShadowColor, ShadowColor, ShadowColor, 1, ProjectileList(j).Rotate
                                   Engine_Render_Grh ProjectileList(j).Grh, X, Y, 0, 1, 1, , , , , , ProjectileList(j).Rotate
                               End If
                           End If
                       End If
                   End If
               End If
               
           End If
       Next j
       
       'Check if it is close enough to the target to remove
       For j = 1 To LastProjectile
           If ProjectileList(j).Grh.GrhIndex Then
               If Abs(ProjectileList(j).X - ProjectileList(j).tX) < 20 Then
                   If Abs(ProjectileList(j).Y - ProjectileList(j).tY) < 20 Then
                       Engine_Projectile_Erase j
                   End If
               End If
           End If
       Next j
       
   End If
   
   '************** Blood Splatters **************
   'Loop to do drawing
   For j = 1 To LastBlood
       If BloodList(j).Grh.GrhIndex Then
           X = Engine_PixelPosX(BloodList(j).Pos.X - minX) + PixelOffsetX + TileBufferOffset
           Y = Engine_PixelPosY(BloodList(j).Pos.Y - minY) + PixelOffsetY + TileBufferOffset
           If Y >= -32 Then
               If Y <= (ScreenHeight + 32) Then
                   If X >= -32 Then
                       If X <= (ScreenWidth + 32) Then
                           Engine_Render_Grh BloodList(j).Grh, X, Y, 1, 1, False
                       End If
                   End If
               End If
           End If
       End If
   Next j
   'Seperate loop to remove the unused - I dont like removing while drawing
   For j = 1 To LastBlood
       If BloodList(j).Grh.GrhIndex Then
           If BloodList(j).Grh.Started = 0 Then Engine_Blood_Erase j
       End If
   Next j
   '************** Update weather **************
   
   'Do the general weather updating
   Engine_Weather_Update
   '************** Chat bubbles **************
   'Loop through the chars
   For j = 1 To LastChar
       If CharList(j).Active Then
           If LenB(CharList(j).BubbleStr) <> 0 Then
               If CharList(j).RealPos.X > -25 Then
                   If CharList(j).RealPos.X < ScreenWidth + 25 Then
                       If CharList(j).RealPos.Y > -25 Then
                           If CharList(j).RealPos.Y < ScreenHeight + 25 Then
                               Engine_Render_ChatBubble CharList(j).BubbleStr, CharList(j).RealPos.X, CharList(j).RealPos.Y
                               CharList(j).BubbleTime = CharList(j).BubbleTime - ElapsedTime
                               If CharList(j).BubbleTime <= 0 Then
                                   CharList(j).BubbleTime = 0
                                   CharList(j).BubbleStr = vbNullString
                               End If
                           End If
                       End If
                   End If
               End If
           End If
       End If
   Next j
   '************** Damage text **************
   'Loop to do drawing
   For j = 1 To LastDamage
       If DamageList(j).Counter > 0 Then
           DamageList(j).Counter = DamageList(j).Counter - ElapsedTime
           X = (((DamageList(j).Pos.X - minX) - 1) * TilePixelWidth) + PixelOffsetX + TileBufferOffset
           Y = (((DamageList(j).Pos.Y - minY) - 1) * TilePixelHeight) + PixelOffsetY + TileBufferOffset
           If Y >= -32 Then
               If Y <= (ScreenHeight + 32) Then
                   If X >= -32 Then
                       If X <= (ScreenWidth + 32) Then
                           Engine_Render_Text Font_Default, DamageList(j).Value, X, Y, D3DColorARGB(255, 255, 0, 0)
                       End If
                   End If
               End If
           End If
           DamageList(j).Pos.Y = DamageList(j).Pos.Y - (ElapsedTime * 0.001)
       End If
   Next j
   'Seperate loop to remove the unused - I dont like removing while drawing
   For j = 1 To LastDamage
       If DamageList(j).Width Then
           If DamageList(j).Counter <= 0 Then Engine_Damage_Erase j
       End If
   Next j
   '************** Misc Rendering **************
   'Update and render particle effects
   Effect_UpdateAll
   'Clear the shift-related variables
   LastOffsetX = ParticleOffsetX
   LastOffsetY = ParticleOffsetY
   'Render the GUI
   Engine_Render_GUI
   
   '************** Mini-map **************
   Const tS As Single = 3  'Size of the mini-map dots
   
   'Check if the mini-map is being shownquit
   If ShowMiniMap Then
   
       'Make sure the mini-map vertex buffer is valid
       If MiniMapVBSize > 0 Then
           
           'Clear the texture
           LastTexture = 0
           D3DDevice.SetTexture 0, Nothing
           
           'Draw the map outline
           D3DDevice.SetStreamSource 0, MiniMapVB, FVF_Size
           D3DDevice.DrawPrimitive D3DPT_TRIANGLELIST, 0, MiniMapVBSize \ 3
 
           'Draw the characters
           For X = 1 To LastChar
               If CharList(X).Active Then
               
                   'The user's character
                   If X = UserCharIndex Then
                       j = D3DColorARGB(200, 0, 255, 0)    'User's character
                       Engine_Render_Rectangle CharList(X).Pos.X * tS, CharList(X).Pos.Y * tS, tS, tS, 1, 1, 1, 1, 1, 1, 0, 0, j, j, j, j, , False
                       GoTo NextChar
                   End If
                   
                   'Part of the user's group or one of the user's slaves
                   If CharList(X).CharType = ClientCharType_Grouped Or (CharList(X).CharType = ClientCharType_Slave And UserCharIndex = CharList(X).OwnerChar) Then
                       If X <> UserCharIndex Then
                           j = D3DColorARGB(200, 100, 220, 100)    'PC (grouped) or the user's slave
                           Engine_Render_Rectangle CharList(X).Pos.X * tS, CharList(X).Pos.Y * tS, tS, tS, 1, 1, 1, 1, 1, 1, 0, 0, j, j, j, j, , False
                           GoTo NextChar
                       End If
                   End If
               
                   'Check if the character is in screen, since the only characters drawn outside of the screen are grouped characters
                   If CharList(X).Pos.X > ScreenMinX Then
                       If CharList(X).Pos.X < ScreenMaxX Then
                           If CharList(X).Pos.Y > ScreenMinY Then
                               If CharList(X).Pos.Y < ScreenMaxY Then
                                   
                                   'Character is a PC
                                   If CharList(X).CharType = ClientCharType_PC Then
                                       j = D3DColorARGB(200, 0, 255, 255)  'PC (not grouped)
                                   'Character is a NPC
                                   Else
                                       j = D3DColorARGB(200, 0, 150, 150)  'NPC
                                   End If
                                   
                                   'Any character but one part of the user's group
                                   Engine_Render_Rectangle CharList(X).Pos.X * tS, CharList(X).Pos.Y * tS, tS, tS, 1, 1, 1, 1, 1, 1, 0, 0, j, j, j, j, , False
                               End If
                           End If
                       End If
                   End If
                   
               End If
               

NextChar:

           Next X
           
       End If
       
   End If
   'Show FPS
   Engine_Render_Text Font_Default, "FPS: " & FPS, ScreenWidth - 80, 2, -1
   'Check if using motion blur / zooming
   If UseMotionBlur Then
       If FrameUseMotionBlur Then
           With D3DDevice
           
               'Perform the zooming calculations
               ' * 1.333... maintains the aspect ratio
               ' ... / 1024 is to factor in the buffer size
               BlurTA(0).tU = ZoomLevel * 1.333333333
               BlurTA(0).tV = ZoomLevel
               BlurTA(1).tU = ((ScreenWidth + 1) / 1024) - (ZoomLevel * 1.333333333)
               BlurTA(1).tV = ZoomLevel
               BlurTA(2).tU = ZoomLevel * 1.333333333
               BlurTA(2).tV = ((ScreenHeight + 1) / 1024) - ZoomLevel
               BlurTA(3).tU = BlurTA(1).tU
               BlurTA(3).tV = BlurTA(2).tV
               
               'Draw what we have drawn thus far since the last .Clear
               LastTexture = -100
               .SetRenderTarget DeviceBuffer, DeviceStencil, 0
               .SetTexture 0, BlurTexture
               .SetRenderState D3DRS_TEXTUREFACTOR, D3DColorARGB(BlurIntensity, 255, 255, 255)
               .SetTextureStageState 0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR
               .DrawPrimitiveUP D3DPT_TRIANGLESTRIP, 2, BlurTA(0), FVF_Size
               .SetTextureStageState 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE
           
           End With
       End If
   End If

End Sub

Public Sub Engine_BuildMiniMap() '************************************************************ 'Builds the array for the minimap. Theres multiple styles available, but only one 'is used in the demo, so experiment with them and check which one you like! 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_BuildMiniMap '************************************************************ Dim NumMiniMapTiles As Long 'UBound of the MiniMapTile array Dim MiniMapTile() As MiniMapTile 'Color of each tile and their position Dim MMC_Blocked As Long Dim MMC_Exit As Long Dim MMC_Sign As Long Dim Offset As Long Dim tVA() As TLVERTEX Dim X As Long Dim Y As Long Dim j As Long

   'Change to the type of map you want
   Const UseOption As Byte = 2
   
   'The size of the tiles
   'If you change this value, you must also change the "tS" constant in Engine_Render_Screen to the same value!
   Const MiniMapSize As Single = 3
   'Create the colors (character colors are defined in Engine_RenderScreen when it is rendered)
   MMC_Blocked = D3DColorARGB(75, 255, 255, 255)   'Blocked tiles
   MMC_Exit = D3DColorARGB(150, 255, 0, 0)         'Exit tiles (warps)
   MMC_Sign = D3DColorARGB(125, 255, 255, 0)       'Tiles with a sign
   
   'Clear the old array by resizing to the largest array we can possibly use
   ReDim MiniMapTile(1 To CLng(MapInfo.Width) * CLng(MapInfo.Height)) As MiniMapTile
   NumMiniMapTiles = 0
   
   Select Case UseOption
       
       '***** Option 1 *****
       Case 1
           For Y = 1 To MapInfo.Height
               For X = 1 To MapInfo.Width
                   
                   'Check for signs
                   If MapData(X, Y).Sign > 1 Then
                       NumMiniMapTiles = NumMiniMapTiles + 1
                       MiniMapTile(NumMiniMapTiles).X = X
                       MiniMapTile(NumMiniMapTiles).Y = Y
                       MiniMapTile(NumMiniMapTiles).Color = MMC_Sign
                   Else
                   
                       'Check for exits
                       If MapData(X, Y).Warp = 1 Then
                           NumMiniMapTiles = NumMiniMapTiles + 1
                           MiniMapTile(NumMiniMapTiles).X = X
                           MiniMapTile(NumMiniMapTiles).Y = Y
                           MiniMapTile(NumMiniMapTiles).Color = MMC_Exit
                       Else
                           
                           'Check for blocked tiles
                           If MapData(X, Y).Blocked = 0 Then
                               NumMiniMapTiles = NumMiniMapTiles + 1
                               MiniMapTile(NumMiniMapTiles).X = X
                               MiniMapTile(NumMiniMapTiles).Y = Y
                               MiniMapTile(NumMiniMapTiles).Color = MMC_Blocked
                           End If
                       End If
                   End If
                   
               Next X
           Next Y
               
       '***** Option 2 *****
       Case 2
           For Y = 1 To MapInfo.Height
               j = 0   'Clear the row settings
               For X = 1 To MapInfo.Width
                   
                   'Check if there is a sign
                   If MapData(X, Y).Sign > 1 Then
                       NumMiniMapTiles = NumMiniMapTiles + 1
                       MiniMapTile(NumMiniMapTiles).X = X
                       MiniMapTile(NumMiniMapTiles).Y = Y
                       MiniMapTile(NumMiniMapTiles).Color = MMC_Sign
                   Else
                   
                       'Check if there is an exit
                       If MapData(X, Y).Warp = 1 Then
                           NumMiniMapTiles = NumMiniMapTiles + 1
                           MiniMapTile(NumMiniMapTiles).X = X
                           MiniMapTile(NumMiniMapTiles).Y = Y
                           MiniMapTile(NumMiniMapTiles).Color = MMC_Exit
                       Else
                           
                           'Only check blocked tiles
                           If MapData(X, Y).Blocked > 0 Then
       
                               'If the row is set to draw, just keep drawing
                               If j = 1 Then
                                   NumMiniMapTiles = NumMiniMapTiles + 1
                                   MiniMapTile(NumMiniMapTiles).X = X
                                   MiniMapTile(NumMiniMapTiles).Y = Y
                                   MiniMapTile(NumMiniMapTiles).Color = MMC_Blocked
                                   
                               'The row isn't drawing, check if it is time to draw it
                               Else
       
                                   'If the next tile is not blocked, this one will be (to draw an outline)
                                   If j = 0 Then
                                       If X + 1 <= MapInfo.Width Then
                                           If MapData(X + 1, Y).Blocked = 0 Then
                                               NumMiniMapTiles = NumMiniMapTiles + 1
                                               MiniMapTile(NumMiniMapTiles).X = X
                                               MiniMapTile(NumMiniMapTiles).Y = Y
                                               MiniMapTile(NumMiniMapTiles).Color = MMC_Blocked
                                               j = 1
                                           End If
                                       End If
                                   End If
                                   
                                   'If the tile above or below is blocked, draw the tile
                                   If j = 0 Then
                                       If Y > 1 Then
                                           If MapData(X, Y - 1).Blocked = 0 Then
                                               NumMiniMapTiles = NumMiniMapTiles + 1
                                               MiniMapTile(NumMiniMapTiles).X = X
                                               MiniMapTile(NumMiniMapTiles).Y = Y
                                               MiniMapTile(NumMiniMapTiles).Color = MMC_Blocked
                                               j = 1
                                           End If
                                       End If
                                   End If
                                   If j = 0 Then
                                       If Y < MapInfo.Height Then
                                           If MapData(X, Y + 1).Blocked = 0 Then
                                               NumMiniMapTiles = NumMiniMapTiles + 1
                                               MiniMapTile(NumMiniMapTiles).X = X
                                               MiniMapTile(NumMiniMapTiles).Y = Y
                                               MiniMapTile(NumMiniMapTiles).Color = MMC_Blocked
                                               j = 1
                                           End If
                                       End If
                                   End If
                                   
                                   'If we STILL haven't drawn the tile, check to the diagonals (this makes corners smoothed)
                                   If j = 0 Then
                                       If Y > 1 Then
                                           If Y < MapInfo.Height Then
                                               If X > 1 Then
                                                   If X < MapInfo.Width Then
                                                       If MapData(X - 1, Y - 1).Blocked = 0 Or MapData(X - 1, Y + 1).Blocked = 0 Or MapData(X + 1, Y - 1).Blocked = 0 Or MapData(X + 1, Y + 1).Blocked = 0 Then
                                                           NumMiniMapTiles = NumMiniMapTiles + 1
                                                           MiniMapTile(NumMiniMapTiles).X = X
                                                           MiniMapTile(NumMiniMapTiles).Y = Y
                                                           MiniMapTile(NumMiniMapTiles).Color = MMC_Blocked
                                                           j = 1
                                                       End If
                                                   End If
                                               End If
                                           End If
                                       End If
                                   End If
                                   
                               End If
                               
                               'If the next tile isn't blocked, we remove the row drawing
                               If j = 1 Then
                                   If X < MapInfo.Width Then
                                       If MapData(X + 1, Y).Blocked > 0 Then j = 0
                                   End If
                               End If
                               
                           End If
                       End If
                   End If
               Next X
           Next Y
   End Select
   
   'Resize the array to fit the amount of data we have
   If NumMiniMapTiles = 0 Then
       Erase MiniMapTile
       Exit Sub
   Else
       ReDim Preserve MiniMapTile(1 To NumMiniMapTiles)
   End If
   
   '***** Build the vertex buffer according to the information we gathered in the MiniMapTile array *****
   
   'Create the temp vertex array large enough to fit every tile (2 triangles per tile, 3 points per triangle)
   ReDim tVA(0 To (NumMiniMapTiles * 6) - 1) As TLVERTEX
   
   'Start our offset at -6 so the first offset is 0
   Offset = -6
   
   'Fill the temp vertex array
   For j = 1 To NumMiniMapTiles
   
       'Raise the offset count
       Offset = Offset + 6
   
       '*** Triangle 1 ***
       
       'Top-left corner
       With tVA(0 + Offset)
           .X = MiniMapTile(j).X * MiniMapSize
           .Y = MiniMapTile(j).Y * MiniMapSize
           .Color = MiniMapTile(j).Color
           .Rhw = 1
       End With
       
       'Top-right corner
       With tVA(1 + Offset)
           .X = MiniMapTile(j).X * MiniMapSize + MiniMapSize
           .Y = MiniMapTile(j).Y * MiniMapSize
           .Color = MiniMapTile(j).Color
           .Rhw = 1
       End With
       
       'Bottom-left corner
       With tVA(2 + Offset)
           .X = MiniMapTile(j).X * MiniMapSize
           .Y = MiniMapTile(j).Y * MiniMapSize + MiniMapSize
           .Color = MiniMapTile(j).Color
           .Rhw = 1
       End With
       
       '*** Triangle 2 ***
       
       'Top-right corner
       tVA(3 + Offset) = tVA(1 + Offset)
       
       'Bottom-right corner
       With tVA(4 + Offset)
           .X = MiniMapTile(j).X * MiniMapSize + MiniMapSize
           .Y = MiniMapTile(j).Y * MiniMapSize + MiniMapSize
           .Color = MiniMapTile(j).Color
           .Rhw = 1
       End With
       
       'Bottom-left corner
       tVA(5 + Offset) = tVA(2 + Offset)
       
   Next j
   
   'Build the vertex buffer
   MiniMapVBSize = Offset + 6
   Set MiniMapVB = D3DDevice.CreateVertexBuffer(FVF_Size * MiniMapVBSize, 0, FVF, D3DPOOL_MANAGED)
   D3DVertexBuffer8SetData MiniMapVB, 0, FVF_Size * MiniMapVBSize, 0, tVA(0)
   
   'Clear the temp arrays
   Erase tVA
   Erase MiniMapTile

End Sub

Private Function Engine_NPCChat_MeetsConditions(ByVal NPCIndex As Integer, ByVal LineIndex As Byte, Optional ByVal SayLine As String = vbNullString) As Byte '***************************************************************** 'Checks if the conditions have been satisfied for a chat line 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_NPCChat_MeetsConditions '***************************************************************** Dim s() As String Dim j As Byte Dim i As Byte

   'Make sure we have a valid line and index
   If LineIndex = 0 Then Exit Function
   If CharList(NPCIndex).NPCChatIndex = 0 Then Exit Function
   If CharList(NPCIndex).NPCChatIndex > UBound(NPCChat()) Then Exit Function
   If LineIndex > UBound(NPCChat(CharList(NPCIndex).NPCChatIndex).ChatLine()) Then Exit Function
   'Woo baby, we're not going to want to type THIS line more then once!
   With NPCChat(CharList(NPCIndex).NPCChatIndex).ChatLine(LineIndex)
       
       'If the SayLine is used, it must be the user just talked - so we ONLY want a trigger line!
       If LenB(SayLine) <> 0 Then   'If the string is not empty
           SayLine = " " & UCase$(SayLine) & " "   'We compair it in UCase$(), since case doesn't matter
           If .NumConditions = 0 Then Exit Function        'If there are no conditions, then theres definintely no SAY condition
           For i = 1 To .NumConditions
               If .Conditions(i).Condition = NPCCHAT_COND_SAY Then Exit For    'Good, we have a SAY condition! We can continue...
               If i = .NumConditions Then Exit Function    'Last condition checked, and it wasn't a SAY, so no SAYs found - goodbye :(
           Next i
       End If
       
       'Loop through all the conditions
       For i = 1 To .NumConditions
       
           'Check what condition it is - keep in mind we exit on a "False" situation, so are checks
           ' are written to check if the condition is false, not true (a little more confusing, but effecient)
           Select Case .Conditions(i).Condition
               
               'If there is a SAY requirement, things get tricky...
               Case NPCCHAT_COND_SAY
                   If LenB(SayLine) = 0 Then Exit Function     'No chance it can be right if theres no text!
                   s() = Split(.Conditions(i).ValueStr, ",")   'Split up our commas (which allow us to have multiple valid words)
                   For j = 0 To UBound(s)  'Loop through each word so we can check if it is in the SayLine
                       If InStr(1, SayLine, s(j)) Then 'Check if the trigger word is in the SayLine
                           Exit For    'Match made! We're good to go - get the hell outta here!
                       End If
                       If j = UBound(s) Then Exit Function 'Oh bummer, the last trigger word was checked and was a no-go, we loose!
                   Next j
                   
               'User doesn't know skill X
               Case NPCCHAT_COND_DONTKNOWSKILL
                   If Not (UserKnowSkill(.Conditions(i).Value) = 0) Then Exit Function
                   
               'User knows skill X
               Case NPCCHAT_COND_KNOWSKILL
                   If Not (UserKnowSkill(.Conditions(i).Value) = 1) Then Exit Function
               
               'NPC's HP is less then or equal to X percent
               Case NPCCHAT_COND_HPLESSTHAN
                   If Not (CharList(UserCharIndex).HealthPercent <= .Conditions(i).Value) Then Exit Function
                   
               'NPC's HP is greater then or equal to X percent
               Case NPCCHAT_COND_HPMORETHAN
                   If Not (CharList(UserCharIndex).HealthPercent >= .Conditions(i).Value) Then Exit Function
               'User's level is less than or equal to X
               Case NPCCHAT_COND_LEVELLESSTHAN
                   If Not (BaseStats(SID.ELV) <= .Conditions(i).Value) Then Exit Function
                   
               'User level is greater than or equal to X
               Case NPCCHAT_COND_LEVELMORETHAN
                   If Not (BaseStats(SID.ELV) >= .Conditions(i).Value) Then Exit Function
           
           End Select
           
       Next i
       
   End With
   
   'We made it, horray!
   Engine_NPCChat_MeetsConditions = 1
   

End Function

Public Sub Engine_NPCChat_CheckForChatTriggers(ByVal ChatTxt As String) '***************************************************************** 'Checks for a NPC chat triggers 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_NPCChat_CheckForChatTriggers '***************************************************************** Dim i As Integer Dim j As Byte

   For i = 1 To LastChar
       
       'We're going to be using this object a hell of a lot...
       With CharList(i)
           
           'We only want an active char
           If .Active Then
           
               'Make sure the NPC has automated chat
               If .NPCChatIndex > 0 Then
   
                   'Check for a valid distance
                   If Engine_RectDistance(.RealPos.X, .RealPos.Y, .RealPos.X - ((ScreenWidth - 50) \ 2), .RealPos.Y - ((ScreenHeight - 50) \ 2), ((ScreenWidth - 50) \ 2) + 1, ((ScreenHeight - 50) \ 2) + 1) Then
                   
                       'Get the next line to use
                       j = Engine_NPCChat_NextLine(i, ChatTxt)
                       
                       'If j = 0, then no valid lines were found
                       If j > 0 Then
                       
                           'Assign the new line
                           .NPCChatLine = j
                           
                           'Say the chat (delay assigned through the routine)
                           Engine_NPCChat_AddText i
                           
                       End If
                   
                   End If
                   
               End If
                   
           End If
           
       End With
   
   Next i
                   

End Sub

Private Sub Engine_NPCChat_Update(ByVal CharIndex As Integer) '***************************************************************** 'Updates the automated NPC chatting 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_NPCChat_Update '***************************************************************** Dim i As Byte

   'We're going to be using this object a hell of a lot...
   With CharList(CharIndex)
       
       'Make sure the NPC has automated chat
       If .NPCChatIndex > 0 Then
           
           'Check for a valid distance
           If Engine_RectDistance(.RealPos.X, .RealPos.Y, .RealPos.X - ((ScreenWidth - 50) \ 2), .RealPos.Y - ((ScreenHeight - 50) \ 2), ((ScreenWidth - 50) \ 2) + 1, ((ScreenHeight - 50) \ 2) + 1) Then
           
               'Update the delay time
               If .NPCChatDelay > 0 Then
                   .NPCChatDelay = .NPCChatDelay - ElapsedTime
                   
               'Time to get a new line!
               Else
                   
                   'Get the new NPCChat line
                   i = Engine_NPCChat_NextLine(CharIndex)
                   If i = 0 Then Exit Sub
                   .NPCChatLine = i
                   
                   'Add the chat
                   Engine_NPCChat_AddText CharIndex
               End If
           End If
       End If
       
   End With

End Sub

Private Sub Engine_NPCChat_AddText(ByVal CharIndex As Integer) '***************************************************************** 'Adds the NPCChat text according to the style 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_NPCChat_AddText '*****************************************************************

   With CharList(CharIndex)
       'Check for text before adding it
       If LenB(NPCChat(.NPCChatIndex).ChatLine(.NPCChatLine).Text) <> 0 Then
   
           'Find out the style used, and add the chat according to the style
           Select Case NPCChat(.NPCChatIndex).ChatLine(.NPCChatLine).Style
               Case NPCCHAT_STYLE_BUBBLE
                   Engine_MakeChatBubble CharIndex, Engine_WordWrap(.Name & ": " & NPCChat(.NPCChatIndex).ChatLine(.NPCChatLine).Text, BubbleMaxWidth)
               Case NPCCHAT_STYLE_BOX
                   Engine_AddToChatTextBuffer .Name & ": " & NPCChat(.NPCChatIndex).ChatLine(.NPCChatLine).Text, FontColor_Talk
               Case NPCCHAT_STYLE_BOTH
                   Engine_MakeChatBubble CharIndex, Engine_WordWrap(.Name & ": " & NPCChat(.NPCChatIndex).ChatLine(.NPCChatLine).Text, BubbleMaxWidth)
                   Engine_AddToChatTextBuffer .Name & ": " & NPCChat(.NPCChatIndex).ChatLine(.NPCChatLine).Text, FontColor_Talk
           End Select
           
       End If
           
       'Add the chat delay (we do the delay even if theres no text)
       .NPCChatDelay = NPCChat(.NPCChatIndex).ChatLine(.NPCChatLine).Delay
       
   End With

End Sub

Private Function Engine_NPCChat_NextLine(ByVal CharIndex As Integer, Optional ByVal ChatTxt As String) As Byte '***************************************************************** 'Gets the next free line to use for the NPC chat (0 if none found) 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_NPCChat_NextLine '***************************************************************** Dim b() As Byte Dim k As Byte Dim j As Byte Dim i As Byte

   With CharList(CharIndex)
   
       'Select the new line to start from according to the format
       Select Case NPCChat(.NPCChatIndex).Format
       
           'Linear selection
           Case NPCCHAT_FORMAT_LINEAR
           
               'Start with the next line
               i = .NPCChatLine + 1
               If i > NPCChat(.NPCChatIndex).NumLines Then i = 1
               
               'Loop through all the lines, checking for the next line with a valid condition
               For j = 1 To NPCChat(.NPCChatIndex).NumLines
                   
                   'Get the new line number to check - roll over to the start if needed
                   k = i + j
                   If k > NPCChat(.NPCChatIndex).NumLines Then k = k - NPCChat(.NPCChatIndex).NumLines
                   
                   'Check if the conditions were met
                   If Engine_NPCChat_MeetsConditions(CharIndex, k, ChatTxt) = 1 Then Exit For
                   
                   'If j is on the last index, then no conditions were met - put on a delay and leave
                   If j = NPCChat(.NPCChatIndex).NumLines Then
                       .NPCChatDelay = 1500    'This delay lets a load off the client
                       Exit Function
                   End If
                   
               Next j
               
           'Random selection
           Case NPCCHAT_FORMAT_RANDOM
           
               'Scramble the numbers so we can pick randomly
               ReDim b(1 To NPCChat(.NPCChatIndex).NumLines)       'Room for all the lines
               For i = 1 To NPCChat(.NPCChatIndex).NumLines        'Loop through every line
                   Do  'Keep looping until we get what we want
                       j = Int(Rnd * NPCChat(.NPCChatIndex).NumLines) + 1  'We have to hold the value in a temp variable
                       If b(j) = 0 Then    'If = 0, the index is free
                           b(j) = i        'Store the index in the random array slot
                           Exit Do         'Leave the DO loop since we have what we want
                       End If
                   Loop
               Next i
               'Now b() holds all the line numbers scrambled up, so we can go through one by one just like with linear
               For j = 1 To NPCChat(.NPCChatIndex).NumLines - 1    '-1 because we are took out the index we already used
                   
                   'Make sure the number is valid (just in case)
                   If b(j) <> 0 Then
                       
                       'Don't check the line we just used (yet)
                       If .NPCChatLine <> b(j) Then
                           
                           'Check the conditions
                           If Engine_NPCChat_MeetsConditions(CharIndex, b(j), ChatTxt) = 1 Then
                               k = b(j)    'Store the successful value in the k variable for below
                               Exit For
                           End If
                       
                       End If
                       
                   End If
                       
                   'If j is on the last index, and no conditions were met, we try the line we last used
                   If j = NPCChat(.NPCChatIndex).NumLines - 1 Then 'If the For loop is just about to end
                       If b(j) > 0 Then    'If this is the NPC's first line, it'd be 0, so check to make sure its not 0 just in case
                           If Engine_NPCChat_MeetsConditions(CharIndex, .NPCChatLine, ChatTxt) = 1 Then
                               k = b(j)    'Store the successful value in the k variable for below
                               Exit For    'We got the text!
                           Else
                               Exit Function   'None of the lines worked :(
                           End If
                       End If
                   End If
               
               Next j
 
       End Select
       'Return the value
       Engine_NPCChat_NextLine = k
       
   End With

End Function

Public Function Engine_ClearPath(ByVal UserX As Long, ByVal UserY As Long, ByVal TargetX As Long, ByVal TargetY As Long) As Byte '***************************************************************** 'Check if the path is clear from the user to the target of blocked tiles 'For the line-rect collision, we pretend that each tile is 2 units wide 'so we can give them a width of 1 to center things 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_ClearPath '***************************************************************** Dim X As Long Dim Y As Long

   '****************************************
   '***** Target is on top of the user *****
   '****************************************
   
   'If the target position = user position, we must be targeting ourself, so nothing can be blocking us from us (I hope o.O )
   If UserX = TargetX Then
       If UserY = TargetY Then
           Engine_ClearPath = 1
           Exit Function
       End If
   End If
   '********************************************
   '***** Target is right next to the user *****
   '********************************************
   
   'Target is at one of the 4 diagonals of the user
   If Abs(UserX - TargetX) = 1 Then
       If Abs(UserY - TargetY) = 1 Then
           Engine_ClearPath = 1
           Exit Function
       End If
   End If
   
   'Target is above or below the user
   If UserX = TargetX Then
       If Abs(UserY - TargetY) = 1 Then
           Engine_ClearPath = 1
           Exit Function
       End If
   End If
   
   'Target is to the left or right of the user
   If UserY = TargetY Then
       If Abs(UserX - TargetX) = 1 Then
           Engine_ClearPath = 1
           Exit Function
       End If
   End If
   
   '********************************************
   '***** Target is diagonal from the user *****
   '********************************************
   
   'Check if the target is diagonal from the user - only do the following checks if diagonal from the target
   If Abs(UserX - TargetX) = Abs(UserY - TargetY) Then
       If UserX > TargetX Then
                       
           'Diagonal to the top-left
           If UserY > TargetY Then
               For X = TargetX To UserX - 1
                   For Y = TargetY To UserY - 1
                       If MapData(X, Y).BlockedAttack Then
                           Engine_ClearPath = 0
                           Exit Function
                       End If
                   Next Y
               Next X
           
           'Diagonal to the bottom-left
           Else
               For X = TargetX To UserX - 1
                   For Y = UserY + 1 To TargetY
                       If MapData(X, Y).BlockedAttack Then
                           Engine_ClearPath = 0
                           Exit Function
                       End If
                   Next Y
               Next X
           End If
       End If
       
       If UserX < TargetX Then
       
           'Diagonal to the top-right
           If UserY > TargetY Then
               For X = UserX + 1 To TargetX
                   For Y = TargetY To UserY - 1
                       If MapData(X, Y).BlockedAttack Then
                           Engine_ClearPath = 0
                           Exit Function
                       End If
                   Next Y
               Next X
               
           'Diagonal to the bottom-right
           Else
               For X = UserX + 1 To TargetX
                   For Y = UserY + 1 To TargetY
                       If MapData(X, Y).BlockedAttack Then
                           Engine_ClearPath = 0
                           Exit Function
                       End If
                   Next Y
               Next X
           End If
       
       End If
   
       Engine_ClearPath = 1
       Exit Function
   
   End If
   '*******************************************************************
   '***** Target is directly vertical or horizontal from the user *****
   '*******************************************************************
   
   'Check if target is directly above the user
   If UserX = TargetX Then 'Check if x values are the same (straight line between the two)
       If UserY > TargetY Then
           For Y = TargetY + 1 To UserY - 1
               If MapData(UserX, Y).BlockedAttack Then
                   Engine_ClearPath = 0
                   Exit Function
               End If
           Next Y
           Engine_ClearPath = 1
           Exit Function
       End If
   End If
   
   'Check if the target is directly below the user
   If UserX = TargetX Then
       If UserY < TargetY Then
           For Y = UserY + 1 To TargetY - 1
               If MapData(UserX, Y).BlockedAttack Then
                   Engine_ClearPath = 0
                   Exit Function
               End If
           Next Y
           Engine_ClearPath = 1
           Exit Function
       End If
   End If
   
   'Check if the target is directly to the left of the user
   If UserY = TargetY Then
       If UserX > TargetX Then
           For X = TargetX + 1 To UserX - 1
               If MapData(X, UserY).BlockedAttack Then
                   Engine_ClearPath = 0
                   Exit Function
               End If
           Next X
           Engine_ClearPath = 1
           Exit Function
       End If
   End If
   
   'Check if the target is directly to the right of the user
   If UserY = TargetY Then
       If UserX < TargetX Then
           For X = UserX + 1 To TargetX - 1
               If MapData(X, UserY).BlockedAttack Then
                   Engine_ClearPath = 0
                   Exit Function
               End If
           Next X
           Engine_ClearPath = 1
           Exit Function
       End If
   End If
   '***************************************************
   '***** Target is directly not in a direct path *****
   '***************************************************
   
   
   If UserY > TargetY Then
   
       'Check if the target is to the top-left of the user
       If UserX > TargetX Then
           For X = TargetX To UserX
               For Y = TargetY To UserY
                   'We must do * 2 on the tiles so we can use +1 to get the center (its like * 32 and + 16 - this does the same affect)
                   If Engine_Collision_LineRect(X * 2, Y * 2, 2, 2, UserX * 2 + 1, UserY * 2 + 1, TargetX * 2 + 1, TargetY * 2 + 1) Then
                       If MapData(X, Y).BlockedAttack Then
                           Engine_ClearPath = 0
                           Exit Function
                       End If
                   End If
               Next Y
           Next X
           Engine_ClearPath = 1
           Exit Function
   
       'Check if the target is to the top-right of the user
       Else
           For X = UserX To TargetX
               For Y = TargetY To UserY
                   If Engine_Collision_LineRect(X * 2, Y * 2, 2, 2, UserX * 2 + 1, UserY * 2 + 1, TargetX * 2 + 1, TargetY * 2 + 1) Then
                       If MapData(X, Y).BlockedAttack Then
                           Engine_ClearPath = 0
                           Exit Function
                       End If
                   End If
               Next Y
           Next X
       End If
       
   Else
   
       'Check if the target is to the bottom-left of the user
       If UserX > TargetX Then
           For X = TargetX To UserX
               For Y = UserY To TargetY
                   If Engine_Collision_LineRect(X * 2, Y * 2, 2, 2, UserX * 2 + 1, UserY * 2 + 1, TargetX * 2 + 1, TargetY * 2 + 1) Then
                       If MapData(X, Y).BlockedAttack Then
                           Engine_ClearPath = 0
                           Exit Function
                       End If
                   End If
               Next Y
           Next X
       
       'Check if the target is to the bottom-right of the user
       Else
           For X = UserX To TargetX
               For Y = UserY To TargetY
                   If Engine_Collision_LineRect(X * 2, Y * 2, 2, 2, UserX * 2 + 1, UserY * 2 + 1, TargetX * 2 + 1, TargetY * 2 + 1) Then
                       If MapData(X, Y).BlockedAttack Then
                           Engine_ClearPath = 0
                           Exit Function
                       End If
                   End If
               Next Y
           Next X
       End If
   
   End If
   
   Engine_ClearPath = 1

End Function

Public Sub Engine_Render_Skills() '***************************************************************** 'Render the spells list 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_Skills '***************************************************************** Dim TempGrh As Grh Dim i As Byte

   TempGrh.FrameCounter = 1
   'Loop through the skills
   For i = 1 To SkillListSize
       If SkillList(i).SkillID = 0 Then Exit For
       'Render the icon
       TempGrh.GrhIndex = 106
       Engine_Render_Grh TempGrh, SkillList(i).X, SkillList(i).Y, 0, 0, False, GUIColorValue, GUIColorValue, GUIColorValue, GUIColorValue
       TempGrh.GrhIndex = Engine_SkillIDtoGRHID(SkillList(i).SkillID)
       Engine_Render_Grh TempGrh, SkillList(i).X, SkillList(i).Y, 0, 0, False
   Next i

End Sub

Public Sub Engine_Render_Text(ByRef UseFont As CustomFont, ByVal Text As String, ByVal X As Long, ByVal Y As Long, ByVal Color As Long) '***************************************************************** 'Render text with a custom font 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Render_Text '***************************************************************** Dim TempVA(0 To 3) As TLVERTEX Dim TempStr() As String Dim Count As Integer Dim Ascii() As Byte Dim Row As Integer Dim u As Single Dim v As Single Dim i As Long Dim j As Long Dim KeyPhrase As Byte Dim TempColor As Long Dim ResetColor As Byte Dim SrcRect As RECT Dim v2 As D3DVECTOR2 Dim v3 As D3DVECTOR2 Dim YOffset As Single

   'Check if we have the device
   If D3DDevice.TestCooperativeLevel <> D3D_OK Then Exit Sub
   'Check for valid text to render
   If LenB(Text) = 0 Then Exit Sub
   
   'Assign the alternate rendering value
   AlternateRender = AlternateRenderText
   'Get the text into arrays (split by vbCrLf)
   TempStr = Split(Text, vbCrLf)
   
   'Set the temp color (or else the first character has no color)
   TempColor = Color
   
   'Check for alternate rendering
   If AlternateRender Then
       'End the old sprite we had going
       If SpriteBegun = 1 Then
           Sprite.End
           Sprite.Begin
       End If
       
   Else
       
       'Set the texture
       D3DDevice.SetTexture 0, UseFont.Texture
   End If
   
   'Clear the LastTexture, letting the rest of the engine know that the texture needs to be changed for next rect render
   LastTexture = -(Rnd * 10000)
   
   'Loop through each line if there are line breaks (vbCrLf)
   For i = 0 To UBound(TempStr)
       If Len(TempStr(i)) > 0 Then
           YOffset = i * UseFont.CharHeight
           Count = 0
       
           'Convert the characters to the ascii value
           Ascii() = StrConv(TempStr(i), vbFromUnicode)
       
           'Loop through the characters
           For j = 1 To Len(TempStr(i))
               'Check for a key phrase
               If Ascii(j - 1) = 124 Then 'If Ascii = "|"
                   KeyPhrase = (Not KeyPhrase)  'TempColor = ARGB 255/255/0/0
                   If KeyPhrase Then TempColor = -65536 Else ResetColor = 1
               Else
                   'Render with triangles
                   If AlternateRender = 0 Then
                       'Copy from the cached vertex array to the temp vertex array
                       CopyMemory TempVA(0), UseFont.HeaderInfo.CharVA(Ascii(j - 1)).Vertex(0), FVF_Size * 4
                       'Set up the verticies
                       TempVA(0).X = X + Count
                       TempVA(0).Y = Y + YOffset
                       
                       TempVA(1).X = TempVA(1).X + X + Count
                       TempVA(1).Y = TempVA(0).Y
                       TempVA(2).X = TempVA(0).X
                       TempVA(2).Y = TempVA(2).Y + TempVA(0).Y
                       TempVA(3).X = TempVA(1).X
                       TempVA(3).Y = TempVA(2).Y
                       
                       'Set the colors
                       TempVA(0).Color = TempColor
                       TempVA(1).Color = TempColor
                       TempVA(2).Color = TempColor
                       TempVA(3).Color = TempColor
                       'Draw the verticies
                       D3DDevice.DrawPrimitiveUP D3DPT_TRIANGLESTRIP, 2, TempVA(0), FVF_Size
                       
                   'Render with D3DXSprite
                   Else
                   
                       'tU and tV value (basically tU = BitmapXPosition / BitmapWidth, and height for tV)
                       Row = (Ascii(j - 1) - UseFont.HeaderInfo.BaseCharOffset) \ UseFont.RowPitch
                       u = ((Ascii(j - 1) - UseFont.HeaderInfo.BaseCharOffset) - (Row * UseFont.RowPitch)) * UseFont.ColFactor
                       v = Row * UseFont.RowFactor
                       'Create the source rectangle
                       With SrcRect
                           .Left = u * UseFont.TextureSize.X
                           .Top = v * UseFont.TextureSize.Y
                           .Right = .Left + (UseFont.ColFactor * UseFont.TextureSize.X)
                           .bottom = .Top + (UseFont.RowFactor * UseFont.TextureSize.Y)
                       End With
                       
                       'Set the translation (location on the screen)
                       v3.X = X + Count
                       v3.Y = Y + (UseFont.CharHeight * i)
                   
                       'Draw the sprite
                       Sprite.Draw UseFont.Texture, SrcRect, SpriteScaleVector, v2, 0, v3, Color
 
                   End If
 
                   'Shift over the the position to render the next character
                   Count = Count + UseFont.HeaderInfo.CharWidth(Ascii(j - 1))
               
               End If
               
               'Check to reset the color
               If ResetColor Then
                   ResetColor = 0
                   TempColor = Color
               End If
               
           Next j
           
       End If
   Next i
   
   'Retreive the default alternate render value
   AlternateRender = AlternateRenderDefault

End Sub

Public Sub Engine_SetItemDesc(ByVal Name As String, Optional ByVal Amount As Integer = 0, Optional ByVal Value As Long = 0) '***************************************************************** 'Set item description values 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_SetItemDesc '***************************************************************** Dim i As Byte Dim X As Long

   'Set the item values
   ItemDescLine(1) = Name
   ItemDescLines = 1
   If Amount <> 0 Then
       ItemDescLines = ItemDescLines + 1
       ItemDescLine(ItemDescLines) = "Amount: " & Amount
   End If
   If Value <> 0 Then
       ItemDescLines = ItemDescLines + 1
       ItemDescLine(ItemDescLines) = "Value: " & Value
   End If
   'Get the largest size
   ItemDescWidth = Engine_GetTextWidth(Font_Default, ItemDescLine(1))
   If ItemDescLines > 1 Then
       For i = 2 To ItemDescLines
           X = Engine_GetTextWidth(Font_Default, ItemDescLine(i))
           If X > ItemDescWidth Then ItemDescWidth = X
       Next i
   End If

End Sub

Sub Engine_ShowNextFrame() '***************************************************************** 'Updates and draws next frame to screen 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_ShowNextFrame '*****************************************************************

   '***** Check if engine is allowed to run ******
   If EngineRun Then
       If UserMoving Then
       
           '****** Move screen Left and Right if needed ******
           If AddtoUserPos.X <> 0 Then
               OffsetCounterX = OffsetCounterX - (ScrollPixelsPerFrameX + CharList(UserCharIndex).Speed + (RunningSpeed * CharList(UserCharIndex).Running)) * AddtoUserPos.X * TickPerFrame
               If Abs(OffsetCounterX) >= Abs(TilePixelWidth * AddtoUserPos.X) Then
                   OffsetCounterX = 0
                   AddtoUserPos.X = 0
                   UserMoving = False
               End If
           End If
           
           '****** Move screen Up and Down if needed ******
           If AddtoUserPos.Y <> 0 Then
               OffsetCounterY = OffsetCounterY - (ScrollPixelsPerFrameY + CharList(UserCharIndex).Speed + (RunningSpeed * CharList(UserCharIndex).Running)) * AddtoUserPos.Y * TickPerFrame
               If Abs(OffsetCounterY) >= Abs(TilePixelHeight * AddtoUserPos.Y) Then
                   OffsetCounterY = 0
                   AddtoUserPos.Y = 0
                   UserMoving = False
               End If
           End If
           
       End If
       '****** Update screen ******
       Call Engine_Render_Screen(UserPos.X - AddtoUserPos.X, UserPos.Y - AddtoUserPos.Y, OffsetCounterX - 288, OffsetCounterY - 288)
       
       'Get timing info
       ElapsedTime = Engine_ElapsedTime()
       If ElapsedTime > 200 Then ElapsedTime = 200
       TickPerFrame = (ElapsedTime * EngineBaseSpeed)
       If FPSLastCheck + 1000 < timeGetTime Then
           FPS = FramesPerSecCounter
           FramesPerSecCounter = 1
           FPSLastCheck = timeGetTime
       Else
           FramesPerSecCounter = FramesPerSecCounter + 1
       End If
       
       'Auto-save config every 30 seconds
       If SaveLastCheck + 30000 < timeGetTime Then
           SaveLastCheck = timeGetTime
           Game_Config_Save
       End If
       
   End If

End Sub

Public Function Engine_SkillIDtoGRHID(ByVal SkillID As Byte) As Integer '***************************************************************** 'Takes in a SkillID and returns the GrhIndex used for that SkillID 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_SkillIDtoGRHID '*****************************************************************

   Select Case SkillID
       Case SkID.Bless: Engine_SkillIDtoGRHID = 46
       Case SkID.IronSkin: Engine_SkillIDtoGRHID = 47
       Case SkID.Strengthen: Engine_SkillIDtoGRHID = 48
       Case SkID.Warcry: Engine_SkillIDtoGRHID = 49
       Case SkID.Protection: Engine_SkillIDtoGRHID = 50
       Case SkID.SpikeField: Engine_SkillIDtoGRHID = 62
       Case SkID.Heal: Engine_SkillIDtoGRHID = 63
       Case SkID.SummonBandit: Engine_SkillIDtoGRHID = 1
   End Select

End Function

Public Function Engine_SkillIDtoSkillName(ByVal SkillID As Byte) As String '***************************************************************** 'Takes in a SkillID and returns the name of that skill 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_SkillIDtoSkillName '*****************************************************************

   Select Case SkillID
       Case SkID.Bless: Engine_SkillIDtoSkillName = "Bless"
       Case SkID.IronSkin: Engine_SkillIDtoSkillName = "Iron Skin"
       Case SkID.Strengthen: Engine_SkillIDtoSkillName = "Strengthen"
       Case SkID.Warcry: Engine_SkillIDtoSkillName = "War Cry"
       Case SkID.Protection: Engine_SkillIDtoSkillName = "Protection"
       Case SkID.SpikeField: Engine_SkillIDtoSkillName = "Spike Field"
       Case SkID.Heal: Engine_SkillIDtoSkillName = "Heal"
       Case SkID.SummonBandit: Engine_SkillIDtoSkillName = "Summon Bandit"
       Case Else: Engine_SkillIDtoSkillName = "Unknown Skill"
   End Select

End Function

Public Sub Engine_SortIntArray(TheArray() As Integer, TheIndex() As Integer, ByVal LowerBound As Integer, ByVal UpperBound As Integer) '***************************************************************** 'Sort an array of integers 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_SortIntArray '***************************************************************** Dim indxt As Long 'Stored index Dim swp As Integer 'Swap variable Dim i As Integer 'Subarray Low Scan Index Dim j As Integer 'Subarray High Scan Index

   'Start the loop
   For j = LowerBound + 1 To UpperBound
       indxt = TheIndex(j)
       swp = TheArray(indxt)
       For i = j - 1 To LowerBound Step -1
           If TheArray(TheIndex(i)) <= swp Then Exit For
           TheIndex(i + 1) = TheIndex(i)
       Next i
       TheIndex(i + 1) = indxt
   Next j

End Sub

Sub Engine_UnloadAllForms() '***************************************************************** 'Unloads all forms 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_UnloadAllForms '***************************************************************** Dim frm As Form

   For Each frm In VB.Forms
       Unload frm
   Next

End Sub

Function Engine_Distance(ByVal x1 As Integer, ByVal Y1 As Integer, ByVal x2 As Integer, ByVal Y2 As Integer) As Single '***************************************************************** 'Finds the distance between two points 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_Distance '*****************************************************************

   Engine_Distance = Sqr(((Y1 - Y2) ^ 2 + (x1 - x2) ^ 2))
   

End Function

Sub Engine_UseQuickBar(ByVal Slot As Byte) '***************************************************************** 'Use the object in the quickbar slot 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_UseQuickBar '*****************************************************************

   Select Case QuickBarID(Slot).Type
   'Use an item
   Case QuickBarType_Item
       If QuickBarID(Slot).ID > 0 Then
           sndBuf.Allocate 2
           sndBuf.Put_Byte DataCode.User_Use
           sndBuf.Put_Byte QuickBarID(Slot).ID
       End If
   'Use a skill
   Case QuickBarType_Skill
       If QuickBarID(Slot).ID > 0 Then
           If LastAttackTime + AttackDelay < timeGetTime Then
               If CharList(UserCharIndex).CharStatus.Exhausted = 0 Then
                   LastAttackTime = timeGetTime
                   sndBuf.Allocate 5
                   sndBuf.Put_Byte DataCode.User_CastSkill
                   sndBuf.Put_Byte QuickBarID(Slot).ID
                   sndBuf.Put_Integer TargetCharIndex
                   sndBuf.Put_Byte CharList(UserCharIndex).Heading
               End If
           End If
       End If
   End Select

End Sub

Public Function Engine_GetBlinkTime() As Long '***************************************************************** 'Return a value on how long until the next blink happens 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_GetBlinkTime '*****************************************************************

   Engine_GetBlinkTime = 4000 + Int(Rnd * 5000)
   

End Function

Public Function Engine_RectDistance(ByVal x1 As Long, ByVal Y1 As Long, ByVal x2 As Long, ByVal Y2 As Long, ByVal MaxXDist As Long, ByVal MaxYDist As Long) As Byte '***************************************************************** 'Check if two tile points are in the same area 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_RectDistance '*****************************************************************

   If Abs(x1 - x2) < MaxXDist + 1 Then
       If Abs(Y1 - Y2) < MaxYDist + 1 Then
           Engine_RectDistance = True
       End If
   End If

End Function

Public Function Engine_FindDirection(Pos As Position, Target As Position) As Byte '***************************************************************** 'Returns the direction in which the Target is from the Pos, 0 if equal 'More info: http://www.vbgore.com/GameClient.TileEngine.Engine_FindDirection '***************************************************************** Dim pX As Integer Dim pY As Integer Dim tX As Integer Dim tY As Integer Dim X As Integer Dim Y As Integer

   'Put the bytes into integer variables (causes overflows for negatives, even if the return is an integer)
   pX = Pos.X
   pY = Pos.Y
   tX = Target.X
   tY = Target.Y
   
   'Find the difference
   X = pX - tX
   Y = pY - tY
   Log "Server_FindDirection: Position difference (X:" & X & " Y:" & Y & ") found", CodeTracker '//\\LOGLINE//\\
   'Same position
   If X = 0 Then
       If Y = 0 Then
           Engine_FindDirection = NORTH
           Exit Function
       End If
   End If
   'NE
   If X <= -1 Then
       If Y >= 1 Then
           Engine_FindDirection = NORTHEAST
           Exit Function
       End If
   End If
   'NW
   If X >= 1 Then
       If Y >= 1 Then
           Engine_FindDirection = NORTHWEST
           Exit Function
       End If
   End If
   'SW
   If X >= 1 Then
       If Y <= -1 Then
           Engine_FindDirection = SOUTHWEST
           Exit Function
       End If
   End If
   'SE
   If X <= -1 Then
       If Y <= -1 Then
           Engine_FindDirection = SOUTHEAST
           Exit Function
       End If
   End If
   'South
   If Y <= -1 Then
       Engine_FindDirection = SOUTH
       Exit Function
   End If
   'North
   If Y >= 1 Then
       Engine_FindDirection = NORTH
       Exit Function
   End If
   'West
   If X >= 1 Then
       Engine_FindDirection = WEST
       Exit Function
   End If
   'East
   If X <= -1 Then
       Engine_FindDirection = EAST
       Exit Function
   End If

End Function </vb>

Personal tools