Bem haviam me pedido um projétil usando o sistema de animação da engine, no entanto esse sistema vem um pouco mais que quem sabe se eu tiver tempo, adiciono uns "add-on's" com objetos temporários no mapa, etc...(dá até pra brincar de fazer um SandBox se usar bem da lógica dos códigos..Inovação pro sistema de casa aí de jogadores, switch...)
O que o sistema faz?
Projétil por animação. Mas melhorado, já que é uma variável (limitei a 30) de projéteis no mapa. O sistema atual que vi, deixa o projétil como recurso do "TempPlayer" , já eu criei um recurso em "MapObj" que como eu citei, dá pra expandir esse "MapObj" para objetos com duração de tempo, vida, que interage se movendo por matriz pré-definida ou por 'trigger' , até por "scriptStates".
-Projétil é enviado ao jogador que entra no mapa;
-Dono do projétil se sair do mapa enquanto seu projétil tem "range" ele é limpo;
-Animações pelo cliente -> Ao invés de toda movimentação ser um "SendAnimation" consumindo o servidor, deixamos o check de posição/valores no servidor, e o cliente se vira com a animação (como se virava, mas agora sem exigir do servidor);
-Tile Block = Limpar projétil e não deixar ultrapassar;
-X/Y com jogador ou NPC = Ataque;
Mas sem mais delongas, vamos ao tutorial que é bem rapidinho.
Abra seu "Server.Vbp" e crie um módulo novo e o nomeie de "ModMapObj". Agora, dentro desse módulo, adicione isto:
Agora em "ModGeneral" , na "Public Sub ClearGameData" , abaixo de:
Adicione isto:
Agora, em "ModServerLoop" na "Sub ServerLoop" , procure por esta parte:
Logo abaixo adicione isto:
Agora, em "ModEnumerations" , na Enumeração "Public Enum ServerPackets" , antes de:
Enumere com isso:
Agora aqui é bem livre porque é onde você vai criar seu projétil, seja ele atrelado ao item ou ao seu ataque. Para liberdade eu atrelei ao ataque básico, então, em "ModHandleData" na "SubHandleAttack", procure por:
E abaixo adicione isto:
Nesse caso vai criar um projétil:
Animação Número: 1
Range: 4
Velocidade: 500ms = 0.5s por tile
Agora vamos para o cliente. Abra-o também e crie um módulo novo de nome "ModMapObj" e dentro dele adicone isto:
Agora, em "ModGamelogic' , na "Public Sub GameLoop" , abaixo de:
Adicione isto:
Agora, em "ModHandleData", na "Public Sub InitMessages" , abaixo de:
Adicione isto:
E ainda em "ModHandleData" ao final, adicione isto:
Agora, em "ModEnumerations" , na enumeração "Public Enum ServerPackets" , antes de:
Adicione isto:
E pronto! Agora você tem um sistema de projétil por animação mais dinâmico logicamente e mais aberto a novas possibilidades! Qualquer dúvida ou sugestão, pode mandar!
O que o sistema faz?
Projétil por animação. Mas melhorado, já que é uma variável (limitei a 30) de projéteis no mapa. O sistema atual que vi, deixa o projétil como recurso do "TempPlayer" , já eu criei um recurso em "MapObj" que como eu citei, dá pra expandir esse "MapObj" para objetos com duração de tempo, vida, que interage se movendo por matriz pré-definida ou por 'trigger' , até por "scriptStates".
-Projétil é enviado ao jogador que entra no mapa;
-Dono do projétil se sair do mapa enquanto seu projétil tem "range" ele é limpo;
-Animações pelo cliente -> Ao invés de toda movimentação ser um "SendAnimation" consumindo o servidor, deixamos o check de posição/valores no servidor, e o cliente se vira com a animação (como se virava, mas agora sem exigir do servidor);
-Tile Block = Limpar projétil e não deixar ultrapassar;
-X/Y com jogador ou NPC = Ataque;
Mas sem mais delongas, vamos ao tutorial que é bem rapidinho.
Abra seu "Server.Vbp" e crie um módulo novo e o nomeie de "ModMapObj". Agora, dentro desse módulo, adicione isto:
- Spoiler:
- Option Explicit
'For Clear functions
Private Declare Sub ZeroMemory Lib "Kernel32.dll" Alias "RtlZeroMemory" (Destination As Any, ByVal Length As Long)
Public Const MAX_MAP_PROJECTILES As Long = 30
Public MapObj(1 To MAX_MAPS) As MapObjRec
Public ProjsOnMap(1 To MAX_MAPS) As Long
Public Type ProjectileRec
ProjOwner As Long
ProjAnim As Long
ProjX As Long
ProjY As Long
ProjDir As Long
ProjRange As Long
ProjSpeed As Long
ProjTimer As Long
End Type
Public Type MapObjRec
MapProjectile(1 To MAX_MAP_PROJECTILES) As ProjectileRec
End Type
Public Function FreeProjSlot(ByVal MapNum As Long) As Long
Dim i As Long
FreeProjSlot = 0
For i = 1 To MAX_MAP_PROJECTILES
If MapObj(MapNum).MapProjectile(i).ProjOwner = 0 Then
FreeProjSlot = i
Exit For
End If
Next
End Function
Public Sub CreateMapProj(ByVal MapNum As Long, ByVal Owner As Long, ByVal Anim As Long, X As Long, Y As Long, Range As Long, ByVal Direction As Long, ByVal WichSpeed As Long, Optional MapNpcNum As Long = 0)
Dim i As Long
If Owner <= 0 Or Owner > MAX_PLAYERS Then Exit Sub
If Not IsPlaying(Owner) Then Exit Sub
i = FreeProjSlot(MapNum)
If i > 0 Then
With MapObj(MapNum).MapProjectile(i)
.ProjOwner = Owner
.ProjAnim = Anim
.ProjX = X
.ProjY = Y
.ProjDir = Direction
.ProjRange = Range
.ProjSpeed = WichSpeed
.ProjTimer = 0
End With
'Set we have a proj on map
ProjsOnMap(MapNum) = YES
'Send projectile to all in map
SendProjToMap MapNum
End If
End Sub
Public Sub SendProjToMap(ByVal MapNum As Long)
Dim i As Long
Dim Buffer As clsBuffer
Set Buffer = New clsBuffer
Buffer.WriteLong SMapProj
For i = 1 To MAX_MAP_PROJECTILES
With MapObj(MapNum).MapProjectile(i)
Buffer.WriteLong .ProjOwner
Buffer.WriteLong .ProjAnim
Buffer.WriteLong .ProjX
Buffer.WriteLong .ProjY
Buffer.WriteLong .ProjDir
Buffer.WriteLong .ProjRange
Buffer.WriteLong .ProjSpeed
End With
Next
SendDataToMap MapNum, Buffer.ToArray()
Set Buffer = Nothing
End Sub
Public Sub SendProjTo(ByVal Index As Long)
Dim MapNum As Long, i As Long
Dim Buffer As clsBuffer
MapNum = GetPlayerMap(Index)
Set Buffer = New clsBuffer
Buffer.WriteLong SMapProj
For i = 1 To MAX_MAP_PROJECTILES
With MapObj(MapNum).MapProjectile(i)
Buffer.WriteLong .ProjOwner
Buffer.WriteLong .ProjAnim
Buffer.WriteLong .ProjX
Buffer.WriteLong .ProjY
Buffer.WriteLong .ProjDir
Buffer.WriteLong .ProjRange
Buffer.WriteLong .ProjSpeed
End With
Next
SendDataTo Index, Buffer.ToArray()
Set Buffer = Nothing
End Sub
Public Sub HandleMapProjectile(ByVal MapNum As Long)
Dim i As Long
For i = 1 To MAX_MAP_PROJECTILES
If MapObj(MapNum).MapProjectile(i).ProjOwner > 0 Then
With MapObj(MapNum).MapProjectile(i)
If GetTickCount > .ProjSpeed + .ProjTimer Then
Select Case .ProjDir
Case DIR_UP
'Check if is outside valid MaxMapXY
If Not IsValid(.ProjX, .ProjY - 1) Then
EraseMapProj MapNum, i
GoTo 1
Else
.ProjX = .ProjX
.ProjY = .ProjY - 1
.ProjRange = .ProjRange - 1
HasSomethingOn MapNum, .ProjX, .ProjY
End If
Case DIR_DOWN
'Check if is outside valid MaxMapXY
If Not IsValid(.ProjX, .ProjY + 1) Then
EraseMapProj MapNum, i
GoTo 1
Else
.ProjX = .ProjX
.ProjY = .ProjY + 1
.ProjRange = .ProjRange - 1
HasSomethingOn MapNum, .ProjX, .ProjY
End If
Case DIR_LEFT
'Check if is outside valid MaxMapXY
If Not IsValid(.ProjX - 1, .ProjY) Then
EraseMapProj MapNum, i
GoTo 1
Else
.ProjX = .ProjX - 1
.ProjY = .ProjY
.ProjRange = .ProjRange - 1
HasSomethingOn MapNum, .ProjX, .ProjY
End If
Case DIR_RIGHT
'Check if is outside valid MaxMapXY
If Not IsValid(.ProjX + 1, .ProjY) Then
EraseMapProj MapNum, i
GoTo 1
Else
.ProjX = .ProjX + 1
.ProjY = .ProjY
.ProjRange = .ProjRange - 1
HasSomethingOn MapNum, .ProjX, .ProjY
End If
End Select
'Check for range
If .ProjRange <= 0 Then
EraseMapProj MapNum, i
End If
'Check if is outside valid MaxMapXY
If Not IsValid(.ProjX, .ProjY) Then
EraseMapProj MapNum, i
End If
.ProjTimer = GetTickCount
End If
End With
End If
1
Next i
End Sub
Public Sub EraseMapProj(ByVal MapNum As Long, ByVal ProjSlot As Long)
With MapObj(MapNum).MapProjectile(ProjSlot)
.ProjOwner = 0
End With
SendProjToMap MapNum
End Sub
Sub ClearMapProj(ByVal MapNum As Long)
Dim i As Long
For i = 1 To MAX_MAP_PROJECTILES
Call ZeroMemory(ByVal VarPtr(MapObj(MapNum).MapProjectile(i)), LenB(MapObj(MapNum).MapProjectile(i)))
'Clear owner
MapObj(MapNum).MapProjectile(i).ProjOwner = 0
Next
ProjsOnMap(MapNum) = NO
End Sub
Sub ClearMapProjs()
Dim i As Long
For i = 1 To MAX_MAPS
Call ClearMapProj(i)
Next
End Sub
Function GetTotalMapProjs(ByVal MapNum As Long) As Long
Dim i As Long
Dim n As Long
n = 0
For i = 1 To MAX_MAP_PROJECTILES
If MapObj(MapNum).MapProjectile(i).ProjOwner > 0 Then
n = n + 1
End If
Next
GetTotalMapProjs = n
End Function
Public Function HasSomethingOn(ByVal MapNum As Long, ByVal X As Long, ByVal Y As Long)
Dim i As Long, n As Long
For i = 1 To MAX_MAP_PROJECTILES
If MapObj(MapNum).MapProjectile(i).ProjOwner > 0 Then
'Check for block
If IsValid(X, Y) Then
With Map(MapNum).Tile(X, Y)
If .Type = TILE_TYPE_BLOCKED Then
EraseMapProj MapNum, i
End If
End With
Else
EraseMapProj MapNum, i
End If
End If
'A player on it
For n = 1 To Player_HighIndex
If IsPlaying(n) And GetPlayerMap(n) = MapNum Then
If GetPlayerX(n) = X Then
If GetPlayerY(n) = Y Then
PlayerAttackPlayer MapObj(MapNum).MapProjectile(i).ProjOwner, n, 10
End If
End If
End If
Next
'An npc on it
For n = 1 To MAX_MAP_NPCS
If MapNpc(MapNum).NPC(n).Num > 0 Then
If MapNpc(MapNum).NPC(n).X = X Then
If MapNpc(MapNum).NPC(n).Y = Y Then
PlayerAttackNpc MapObj(MapNum).MapProjectile(i).ProjOwner, n, 10
End If
End If
End If
Next
Next
End Function
Public Function IsValid(ByVal x As Long, _
ByVal y As Long) As Boolean
IsValid = True
If x < 0 Or x > MAX_MAPX Or y < 0 Or y > MAX_MAPY Then IsValid = False
End Function
Agora em "ModGeneral" , na "Public Sub ClearGameData" , abaixo de:
- Código:
Call SetStatus("Clearing animations...")
Adicione isto:
- Código:
Call SetStatus("Limpando Index dos projeteis do mapa...")
Call ClearMapProjs
Agora, em "ModServerLoop" na "Sub ServerLoop" , procure por esta parte:
- Código:
' HoT and DoT logic
For X = 1 To MAX_DOTS
HandleDoT_Player i, X
HandleHoT_Player i, X
Next
Logo abaixo adicione isto:
- Código:
For X = 1 To MAX_MAPS
'Check for projectile
If GetTotalMapProjs(X) Then
HandleMapProjectile X
End If
Next
Agora, em "ModEnumerations" , na Enumeração "Public Enum ServerPackets" , antes de:
- Código:
' Make sure SMSG_COUNT is below everything else
smsg_count
End Enum
Enumere com isso:
- Código:
SMapProj
Agora aqui é bem livre porque é onde você vai criar seu projétil, seja ele atrelado ao item ou ao seu ataque. Para liberdade eu atrelei ao ataque básico, então, em "ModHandleData" na "SubHandleAttack", procure por:
- Código:
If MapNum <= 0 Or MapNum > MAX_MAPS Then Exit Sub
E abaixo adicione isto:
- Código:
'Testing
CreateMapProj GetPlayerMap(Index), Index, 1, GetPlayerX(Index), GetPlayerY(Index), 4, GetPlayerDir(Index), 500
Nesse caso vai criar um projétil:
Animação Número: 1
Range: 4
Velocidade: 500ms = 0.5s por tile
Agora vamos para o cliente. Abra-o também e crie um módulo novo de nome "ModMapObj" e dentro dele adicone isto:
- Spoiler:
- Option Explicit
Public Const MAX_MAP_PROJECTILES As Long = 30
Public MapObj As MapObjRec
Public Type ProjectileRec
ProjOwner As Long
ProjAnim As Long
ProjX As Long
ProjY As Long
ProjDir As Long
ProjRange As Long
ProjSpeed As Long
ProjTimer As Long
End Type
Public Type MapObjRec
MapProjectile(1 To MAX_MAP_PROJECTILES) As ProjectileRec
End Type
Public Function FreeProjSlot() As Long
Dim i As Long
FreeProjSlot = 0
For i = 1 To MAX_MAP_PROJECTILES
If MapObj.MapProjectile(i).ProjOwner = 0 Then
FreeProjSlot = i
Exit For
End If
Next
End Function
Public Sub CreateMapProj(ByVal Owner As Long, ByVal Anim As Long, X As Long, Y As Long, Range As Long, ByVal Direction As Long, ByVal WichSpeed As Long, Optional MapNpcNum As Long = 0)
Dim i As Long
If Owner <= 0 Or Owner > MAX_PLAYERS Then Exit Sub
If Not IsPlaying(Owner) Then Exit Sub
i = FreeProjSlot
If i > 0 Then
With MapObj.MapProjectile(i)
.ProjOwner = Owner
.ProjAnim = Anim
.ProjX = X
.ProjY = Y
.ProjDir = Direction
.ProjRange = Range
.ProjSpeed = WichSpeed
.ProjTimer = GetTickCount
End With
End If
End Sub
Public Sub HandleMapProjectile()
Dim i As Long
For i = 1 To MAX_MAP_PROJECTILES
If MapObj.MapProjectile(i).ProjOwner > 0 Then
With MapObj.MapProjectile(i)
If GetTickCount > .ProjSpeed + .ProjTimer Then
Select Case .ProjDir
Case DIR_UP
.ProjX = .ProjX
.ProjY = .ProjY - 1
.ProjRange = .ProjRange - 1
AddAnim .ProjAnim, .ProjX, .ProjY
Case DIR_DOWN
.ProjX = .ProjX
.ProjY = .ProjY + 1
.ProjRange = .ProjRange - 1
AddAnim .ProjAnim, .ProjX, .ProjY
Case DIR_LEFT
.ProjX = .ProjX - 1
.ProjY = .ProjY
.ProjRange = .ProjRange - 1
AddAnim .ProjAnim, .ProjX, .ProjY
Case DIR_RIGHT
.ProjX = .ProjX + 1
.ProjY = .ProjY
.ProjRange = .ProjRange - 1
AddAnim .ProjAnim, .ProjX, .ProjY
End Select
If .ProjRange <= 0 Then
EraseMapProj i
End If
.ProjTimer = GetTickCount
End If
End With
End If
Next
End Sub
Public Sub EraseMapProj(ByVal ProjSlot As Long)
With MapObj.MapProjectile(ProjSlot)
.ProjOwner = 0
End With
End Sub
Public Sub AddAnim(ByVal AnimNum As Long, ByVal X As Long, ByVal Y As Long, Optional ByVal LockType As Byte, Optional ByVal LockIndex As Long)
If AnimNum < 1 Or AnimNum > MAX_ANIMATIONS Then Exit Sub
AnimationIndex = AnimationIndex + 1
If AnimationIndex >= MAX_BYTE Then AnimationIndex = 1
'Clear Before set
ClearAnimInstance AnimationIndex
With AnimInstance(AnimationIndex)
.Animation = AnimNum
.X = X
.Y = Y
.LockType = LockType
.LockIndex = LockIndex
.Used(0) = True
.Used(1) = True
End With
PlayMapSound AnimInstance(AnimationIndex).X, AnimInstance(AnimationIndex).Y, SoundEntity.seAnimation, AnimInstance(AnimationIndex).Animation
End Sub
Agora, em "ModGamelogic' , na "Public Sub GameLoop" , abaixo de:
- Código:
' *** Start GameLoop ***
Do While InGame
Tick = GetTickCount ' Set the inital tick
ElapsedTime = Tick - FrameTime ' Set the time difference for time-based movement
FrameTime = Tick ' Set the time second loop time to the first.
Adicione isto:
- Código:
'Check for projectiles
For i = 1 To MAX_MAP_PROJECTILES
If MapObj.MapProjectile(i).ProjOwner > 0 Then
HandleMapProjectile
End If
Next
Agora, em "ModHandleData", na "Public Sub InitMessages" , abaixo de:
- Código:
HandleDataSub(SPartyVitals) = GetAddress(AddressOf HandlePartyVitals)
Adicione isto:
- Código:
HandleDataSub(SMapProj) = GetAddress(AddressOf HandleMapProj)
E ainda em "ModHandleData" ao final, adicione isto:
- Código:
Private Sub HandleMapProj(ByVal Index As Long, ByRef Data() As Byte, ByVal StartAddR As Long, ByVal ExtraVar As Long)
Dim i As Long
Dim Buffer As clsBuffer
' If debug mode, handle error then exit out
If Options.Debug = 1 Then On Error GoTo errorhandler
Set Buffer = New clsBuffer
Buffer.WriteBytes Data()
For i = 1 To MAX_MAP_PROJECTILES
With MapObj.MapProjectile(i)
.ProjOwner = Buffer.ReadLong
.ProjAnim = Buffer.ReadLong
.ProjX = Buffer.ReadLong
.ProjY = Buffer.ReadLong
.ProjDir = Buffer.ReadLong
.ProjRange = Buffer.ReadLong
.ProjSpeed = Buffer.ReadLong
.ProjTimer = 0
If .ProjOwner > 0 Then
CreateMapProj .ProjOwner, .ProjAnim, .ProjX, .ProjY, .ProjRange, .ProjDir, .ProjSpeed
Else
EraseMapProj i
End If
End With
Next
Set Buffer = Nothing
' Error handler
Exit Sub
errorhandler:
HandleError "HandleMapProj", "modHandleData", Err.Number, Err.Description, Err.Source, Err.HelpContext
Err.Clear
Exit Sub
End Sub
Agora, em "ModEnumerations" , na enumeração "Public Enum ServerPackets" , antes de:
- Código:
' Make sure SMSG_COUNT is below everything else
SMsg_count
End Enum
Adicione isto:
- Código:
SMapProj
E pronto! Agora você tem um sistema de projétil por animação mais dinâmico logicamente e mais aberto a novas possibilidades! Qualquer dúvida ou sugestão, pode mandar!
Créditos
Kotol