Upgrade 1.0.5 to 1.0.6
From VbGORE Visual Basic Online RPG Engine
This guide will lead you through how to upgrade Version 1.0.5 to Version 1.0.6. For help, please refer to the How to upgrade article. It is highly recommended you read it before ever upgrading.
Add - Client-side attack pre-check
Open GameClient.vbp.
In module TileEngine, add:
<vb> 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 '***************************************************************** 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 </vb>
Find:
<vb>
Else
sndBuf.Allocate 2
sndBuf.Put_Byte DataCode.User_Attack
sndBuf.Put_Byte CharList(UserCharIndex).Heading
End If
</vb>
Replace with:
<vb>
Else
If Engine_UserIsFacingChar Then
sndBuf.Allocate 2
sndBuf.Put_Byte DataCode.User_Attack
sndBuf.Put_Byte CharList(UserCharIndex).Heading
End If
End If
</vb>
Add - Buffer sizing constants
Open GameClient.vbp.
Find:
<vb> Public Const ScreenHeight As Long = 600 'Keep this identical to the value on the server! </vb>
After, add:
<vb> 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 </vb>
Find both cases of:
<vb>
Set BlurStencil = D3DDevice.CreateDepthStencilSurface(1024, 1024, D3DFMT_D16, D3DMULTISAMPLE_NONE)
Set BlurTexture = D3DX.CreateTexture(D3DDevice, 1024, 1024, 0, D3DUSAGE_RENDERTARGET, DispMode.Format, D3DPOOL_DEFAULT)
</vb>
Replace both with:
<vb>
Set BlurStencil = D3DDevice.CreateDepthStencilSurface(BufferWidth, BufferHeight, D3DFMT_D16, D3DMULTISAMPLE_NONE)
Set BlurTexture = D3DX.CreateTexture(D3DDevice, BufferWidth, BufferHeight, 0, D3DUSAGE_RENDERTARGET, DispMode.Format, D3DPOOL_DEFAULT)
</vb>
Change - Integrated manifest files
Open EditorMap.vbp.
Replace all of module Manifest with:
<vb> Option Explicit
Private Declare Sub InitCommonControls Lib "comctl32.dll" ()
Public Sub InitManifest()
'This routine will make sure the application hooks to ComCtrl32.DLL 'This must be called on the very first line of sub Main (whichever is called first)
InitCommonControls
End Sub </vb>
Copy \Data\XP.Manifest.Res from the latest version to the same location in your version.
Things are a bit trickier for the map editor's resource file because it already contains information, so copy over \GrhMapEditor\MapEditor.RES to the same location in your version, then delete EditorMap.exe.manifest from the root directory.
These next steps must be done for every project using a .manifest file:
- EditorParticle
- GameConfig
- ToolColorCon
- ToolFileProcessor
- ToolFreeNumber
- ToolGrhCategorizer
- ToolGrhRawAssistant
- ToolRebooter
- ToolServerFPSViewer
Delete the .manifest file corresponding to the project (ie ToolFileProcessor.exe.manifest). Open the project and press Ctrl + D to add a new file. Browse to the \Data\XP.Manifest.Res file and add it.
Add - Tons of GM commands
Open GameServer.vbp.
Find:
<vb>
GM_GiveSkill As Byte
</vb>
After, add:
<vb>
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
</vb>
Find:
<vb>
.GM_GiveSkill = 113
</vb>
After, add:
<vb>
.GM_GiveGold = 114
.GM_GiveObject = 115
.GM_KillMap = 116
.GM_Kill = 117
.GM_WarpToMap = 118
.GM_IPInfo = 119
.GM_BanList = 120
</vb>
Find:
<vb>
Case .GM_Warp: Data_GM_Warp rBuf, Index
</vb>
After, add:
<vb>
Case .GM_GiveGold: Data_GM_GiveGold rBuf, Index
Case .GM_GiveObject: Data_GM_GiveObject rBuf, Index
Case .GM_KillMap: Data_GM_KillMap rBuf, Index
Case .GM_Kill: Data_GM_Kill rBuf, Index
Case .GM_WarpToMap: Data_GM_WarpToMap rBuf, Index
Case .GM_IPInfo: Data_GM_IPInfo rBuf, Index
Case .GM_BanList: Data_GM_BanList rBuf, Index
</vb>
In module TCP, add:
<vb> Sub Data_GM_GiveGold(ByRef rBuf As DataBuffer, ByVal UserIndex As Integer)
'***************************************************************** 'GM gives themself gold '<Amount(L)> '***************************************************************** Dim Amount As Long
'Get the gold amount Amount = rBuf.Get_Long 'Confirm that the user has a high enough GM level If UserList(UserIndex).Flags.GMLevel = 0 Then Exit Sub 'Give the GM the gold UserList(UserIndex).Stats.BaseStat(SID.Gold) = UserList(UserIndex).Stats.BaseStat(SID.Gold) + Amount '"You got gold" message ConBuf.PreAllocate 5 ConBuf.Put_Byte DataCode.Server_Message ConBuf.Put_Byte 138 ConBuf.Put_Long Amount Data_Send ToIndex, UserIndex, ConBuf.Get_Buffer, , PP_GMMessages
End Sub
Sub Data_GM_GiveObject(ByRef rBuf As DataBuffer, ByVal UserIndex As Integer)
'***************************************************************** 'GM gives themself an object '<ObjIndex(I)><Amount(I)> '***************************************************************** Dim ObjIndex As Integer Dim Amount As Integer
'Get the values ObjIndex = rBuf.Get_Integer Amount = rBuf.Get_Integer 'Confirm that the user has a high enough GM level If UserList(UserIndex).Flags.GMLevel = 0 Then Exit Sub 'Let the GiveObj routine handle the rest User_GiveObj UserIndex, ObjIndex, Amount
End Sub
Sub Data_GM_KillMap(ByRef rBuf As DataBuffer, ByVal UserIndex As Integer)
'***************************************************************** 'GM kills all of the NPCs on the map '<> '***************************************************************** Dim i As Integer
'Confirm that the user has a high enough GM level
If UserList(UserIndex).Flags.GMLevel = 0 Then Exit Sub
'Loop through every NPC
For i = 1 To LastNPC
'Check if the NPC is on the user's map
If NPCList(i).Pos.Map = UserList(UserIndex).Pos.Map Then
'Check for valid NPC flags
If NPCList(i).Flags.NPCAlive Then
If NPCList(i).Flags.NPCActive Then
'Kill them!
NPC_Kill i
End If
End If
End If
Next i
End Sub
Sub Data_GM_Kill(ByRef rBuf As DataBuffer, ByVal UserIndex As Integer)
'***************************************************************** 'GM kills their target '<> '***************************************************************** Dim TargetIndex As Integer Dim TargetType As Byte
'Confirm that the user has a high enough GM level
If UserList(UserIndex).Flags.GMLevel = 0 Then Exit Sub
'Get the target index and type
TargetIndex = UserList(UserIndex).Flags.TargetIndex
TargetType = UserList(UserIndex).Flags.Target
'Check for PC
If TargetType = CharType_PC Then
'Make sure the GM isn't targeting themself
If TargetIndex = 0 Then Exit Sub
If TargetIndex = UserIndex Then Exit Sub
'Check for a valid target
If TargetIndex <= 0 Then Exit Sub
If TargetIndex > LastUser Then Exit Sub
If UserList(TargetIndex).Flags.Disconnecting = 1 Then Exit Sub
If UserList(TargetIndex).Flags.UserLogged = 0 Then Exit Sub
'Kill the user
User_Kill TargetIndex
'Check for NPC
Else
'Check for a valid target
If TargetIndex <= 0 Then Exit Sub
If TargetIndex > LastNPC Then Exit Sub
If NPCList(TargetIndex).Flags.NPCActive = 0 Then Exit Sub
If NPCList(TargetIndex).Flags.NPCAlive = 0 Then Exit Sub
'Kill the NPC
NPC_Kill TargetIndex
End If
End Sub
Sub Data_GM_WarpToMap(ByRef rBuf As DataBuffer, ByVal UserIndex As Integer)
'***************************************************************** 'GM warps to a map by number '<MapIndex(I)> '***************************************************************** Dim MapIndex As Integer Dim X As Byte Dim Y As Byte
'Get the map index
MapIndex = rBuf.Get_Integer
'Confirm that the user has a high enough GM level
If UserList(UserIndex).Flags.GMLevel = 0 Then Exit Sub
'Check for a valid map
If MapIndex <= 0 Then Exit Sub
If MapIndex > NumMaps Then Exit Sub
'Make sure the map is loaded
Load_Maps_Temp MapIndex
'Warp the user to the very first legal tile
For Y = 1 To MapInfo(MapIndex).Width
For X = 1 To MapInfo(MapIndex).Height
If MapInfo(MapIndex).Data(X, Y).Blocked = 0 Then
User_WarpChar UserIndex, MapIndex, X, Y
Exit Sub
End If
Next X
Next Y
End Sub
Sub Data_GM_IPInfo(ByRef rBuf As DataBuffer, ByVal UserIndex As Integer)
'***************************************************************** 'GM requests information on an IP '<IP(S)> '***************************************************************** Dim s() As String Dim IP As String Dim i As Long Dim j As Long
'Get the IP (we assume it is valid, since it IS a GM, so they shouldn't be trying to crash the server)
IP = rBuf.Get_String
'Confirm that the user has a high enough GM level
If UserList(UserIndex).Flags.GMLevel = 0 Then Exit Sub
'Clear the conversion buffer
ConBuf.Clear
'Make a query to the database that contains the IP in the ban list
DB_RS.Open "SELECT name,ip FROM users WHERE ip LIKE '%" & IP & "%'", DB_Conn, adOpenStatic, adLockOptimistic
'Check if there are any results
If Not DB_RS.EOF Then
'Loop through all of the results and return the names
i = 0
Do While Not DB_RS.EOF
'Confirm that the IP is really what we wanted since the LIKE operator won't always give us just what we want
s = Split(DB_RS!IP, vbNewLine) 'Split up the IPs
If UBound(s) > 0 Then 'Make sure there is more than one IP
For j = 0 To UBound(s) 'Loop through and check every value of s()
If s(j) = IP Then Exit For 'Valid match found
Next j
If j = UBound(s) + 1 Then GoTo NextRS 'If no match was found, skip
End If
'Add the IP to the list
i = i + 1
ConBuf.Allocate 5 + Len(DB_RS!Name) + Len(CStr(i))
ConBuf.Put_Byte DataCode.Comm_Talk
ConBuf.Put_String i & ". " & DB_RS!Name
ConBuf.Put_Byte DataCode.Comm_FontType_Info
'Move to the next recordset
NextRS:
DB_RS.MoveNext
Loop
End If
'Close the record set
DB_RS.Close
'Check if the data buffer has anything in it (if so, there are results)
If ConBuf.HasBuffer = 0 Then
'No results :(
ConBuf.Allocate 17 'The buffer is already cleared, so no need for PreAllocate
ConBuf.Put_Byte DataCode.Comm_Talk
ConBuf.Put_String "No IP matches."
ConBuf.Put_Byte DataCode.Comm_FontType_Info
End If
'Send whatever we created (either list of names, or "No IP matches") to the user
Data_Send ToIndex, UserIndex, ConBuf.Get_Buffer, , PP_GMMessages
End Sub
Sub Data_GM_BanList(ByRef rBuf As DataBuffer, ByVal UserIndex As Integer)
'***************************************************************** 'GM requests the list of banned IPs '<> '***************************************************************** Dim i As Long
'Confirm that the user has a high enough GM level
If UserList(UserIndex).Flags.GMLevel = 0 Then Exit Sub
'Generate the ban list
DB_RS.Open "SELECT * FROM banned_ips", DB_Conn, adOpenStatic, adLockOptimistic
'Check if the are any IP bans
If DB_RS.EOF Then
DB_RS.Close
ConBuf.PreAllocate 28
ConBuf.Put_Byte DataCode.Comm_Talk
ConBuf.Put_String "There are no banned IPs."
ConBuf.Put_Byte DataCode.Comm_FontType_Info
Data_Send ToIndex, UserIndex, ConBuf.Get_Buffer, , PP_GMMessages
Exit Sub
End If
'Loop through the IP bans
i = 0
Do While Not DB_RS.EOF
i = i + 1
ConBuf.Allocate 8 + Len(DB_RS!IP) + Len(DB_RS!Reason) + Len(CStr(i))
ConBuf.Put_Byte DataCode.Comm_Talk
ConBuf.Put_String i & ". |" & DB_RS!IP & "| " & DB_RS!Reason
ConBuf.Put_Byte DataCode.Comm_FontType_Info
DB_RS.MoveNext 'Move to the next IP
Loop
'Close the record set
DB_RS.Close
'Send the list to the user
Data_Send ToIndex, UserIndex, ConBuf.Get_Buffer, , PP_GMMessages
End Sub </vb>
Open GameClient.vbp.
Find:
<vb>
ElseIf Input_GetCommand("/RAISE") Then
</vb>
Before, add:
<vb>
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
</vb>
Fix - Needless MipMap level
Open GameClient.vbp.
Find both cases of:
<vb>
Set BlurTexture = D3DX.CreateTexture(D3DDevice, 1024, 1024, 1, D3DUSAGE_RENDERTARGET, DispMode.Format, D3DPOOL_DEFAULT)
</vb>
Replace with:
<vb>
Set BlurTexture = D3DX.CreateTexture(D3DDevice, 1024, 1024, 0, D3DUSAGE_RENDERTARGET, DispMode.Format, D3DPOOL_DEFAULT)</vb>
Fix - Motion blur rendering
Open GameClient.vbp.
Find:
<vb>
With D3DDevice
'Check if using motion blur / zooming
If UseMotionBlur Then
'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 If
End With
</vb>
Move it to the very bottom of sub Engine_Render_Screen, after:
<vb>
'Show FPS Engine_Render_Text "FPS: " & FPS, ScreenWidth - 80, 2, -1
</vb>
Add - Client object grab check
Open GameClient.vbp.
Find:
<vb>
'Get object off ground (alt)
If Input_Keys_IsPressed(KeyDefinitions.PickUpObj, KeyCode) Then
If LastLootTime < timeGetTime Then
LastLootTime = timeGetTime + LootDelay
sndBuf.Put_Byte DataCode.User_Get
End If
End If
</vb>
Replace with:
<vb>
'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
</vb>
In sub TileEngine, add:
<vb> Function Engine_OBJ_AtTile(ByVal X As Byte, ByVal Y As Byte) As Boolean
'***************************************************************** 'Checks for an object at tile (X,Y) '***************************************************************** 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 </vb>
Add - ToolGrhRawAssistant
Copy over ToolGrhRawAssistant from Version 1.0.6.
Fix - NPC spawning
Open GameServer.vbp.
Find:
<vb>
'Close down the NPC NPCList(NPCIndex).Flags.NPCActive = 0 CharList(NPCList(NPCIndex).Char.CharIndex).Index = 0 CharList(NPCList(NPCIndex).Char.CharIndex).CharType = 0
</vb>
Replace with:
<vb>
'Close down the NPC CharList(NPCList(NPCIndex).Char.CharIndex).Index = 0 CharList(NPCList(NPCIndex).Char.CharIndex).CharType = 0
</vb>
Find:
<vb>
'Clear the variables NPCList(NPCIndex).Char.CharIndex = 0 NPCList(NPCIndex).Flags.NPCAlive = 0 NPCList(NPCIndex).Flags.NPCActive = 0
</vb>
Replace with:
<vb>
'Clear the variables NPCList(NPCIndex).Char.CharIndex = 0 NPCList(NPCIndex).Flags.NPCAlive = 0
</vb>
Fix - Map object erasing
Open GameServer.vbp.
Find:
<vb> Public Sub Obj_Erase(ByVal Num As Integer, ByVal ObjSlot As Byte, ByVal Map As Byte, ByVal X As Integer, ByVal Y As Integer) </vb>
Replace the whole sub with:
<vb> Public Sub Obj_Erase(ByVal Num As Integer, ByVal ObjSlot As Byte, ByVal Map As Byte, ByVal X As Integer, ByVal Y As Integer)
'***************************************************************** 'Erase a object '*****************************************************************
Log "Call Obj_Erase(" & Num & "," & ObjSlot & "," & Map & "," & X & "," & Y & ")", CodeTracker '//\\LOGLINE//\\
'Check for a valid index
If ObjSlot > MapInfo(Map).ObjTile(X, Y).NumObjs Then
Log "Obj_Erase: Invalid ObjSlot specified (" & ObjSlot & ")", CriticalError '//\\LOGLINE//\\
Exit Sub
End If
'Check to erase every object
If Num = -1 Then Num = MapInfo(Map).ObjTile(X, Y).ObjInfo(ObjSlot).Amount
'Remove the amount
Log "Obj_Erase: Removing " & Num & " objects from (" & Map & "," & X & "," & Y & ") - current amount = " & MapInfo(Map).ObjTile(X, Y).ObjInfo(ObjSlot).Amount, CodeTracker '//\\LOGLINE//\\
MapInfo(Map).ObjTile(X, Y).ObjInfo(ObjSlot).Amount = MapInfo(Map).ObjTile(X, Y).ObjInfo(ObjSlot).Amount - Num
'Check if they are all gone
If MapInfo(Map).ObjTile(X, Y).ObjInfo(ObjSlot).Amount <= 0 Then
Log "Obj_Erase: Erasing object from client screens at (" & Map & "," & X & "," & Y & ")", CodeTracker '//\\LOGLINE//\\
ConBuf.PreAllocate 7
ConBuf.Put_Byte DataCode.Server_EraseObject
ConBuf.Put_Byte CByte(X)
ConBuf.Put_Byte CByte(Y)
ConBuf.Put_Long ObjData.GrhIndex(MapInfo(Map).ObjTile(X, Y).ObjInfo(ObjSlot).ObjIndex)
Data_Send ToMap, 0, ConBuf.Get_Buffer, Map, PP_GroundObjects
With MapInfo(Map).ObjTile(X, Y)
.ObjInfo(ObjSlot).ObjIndex = 0
.ObjInfo(ObjSlot).Amount = 0
.ObjLife(ObjSlot) = 0
End With
End If
End Sub </vb>