Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
455 lines
17 KiB
OpenEdge ABL
455 lines
17 KiB
OpenEdge ABL
VERSION 1.0 CLASS
|
||
BEGIN
|
||
MultiUse = -1 'True
|
||
Persistable = 0 'NotPersistable
|
||
DataBindingBehavior = 0 'vbNone
|
||
DataSourceBehavior = 0 'vbNone
|
||
MTSTransactionMode = 0 'NotAnMTSObject
|
||
END
|
||
Attribute VB_Name = "cPuck"
|
||
Attribute VB_GlobalNameSpace = False
|
||
Attribute VB_Creatable = True
|
||
Attribute VB_PredeclaredId = False
|
||
Attribute VB_Exposed = False
|
||
Option Explicit
|
||
|
||
Private Const mnMaxSpinSpeed As Single = 0.9
|
||
'Here we will encapsulate all of the code needed for the puck
|
||
'Local variables for the properties of the puck
|
||
Private moPosition As D3DVECTOR 'Current position of the puck
|
||
Private moVelocity As D3DVECTOR 'Current velocity of the puck
|
||
Private moLastPosition As D3DVECTOR 'Last position of the puck
|
||
|
||
Public Spinning As Boolean 'Is the puck currently spinning?
|
||
Public MaximumPuckVelocity As Single
|
||
|
||
Private mnSpinDir As Single 'Direction of the pucks spinning
|
||
Private mlPuckTime As Long 'Last time the puck was updated
|
||
Private mnPuckSpin As Single
|
||
|
||
Private moPuck As CD3DFrame 'D3D Mesh for the puck
|
||
'Default spin speed
|
||
Private mnDefaultSpin As Single
|
||
|
||
'Position property
|
||
Public Property Let Position(oPos As D3DVECTOR)
|
||
moPosition = oPos
|
||
End Property
|
||
|
||
Public Property Get Position() As D3DVECTOR
|
||
Position = moPosition
|
||
End Property
|
||
|
||
'Velocity property
|
||
Public Property Let Velocity(oVel As D3DVECTOR)
|
||
moVelocity = oVel
|
||
'Update the velocity, but make sure it isn't too high
|
||
EnsurePuckVelocityIsBelowMax
|
||
End Property
|
||
|
||
Public Property Get Velocity() As D3DVECTOR
|
||
Velocity = moVelocity
|
||
End Property
|
||
|
||
'LastPosition prop
|
||
Public Property Let LastPosition(oLastPos As D3DVECTOR)
|
||
moLastPosition = oLastPos
|
||
End Property
|
||
|
||
Public Property Get LastPosition() As D3DVECTOR
|
||
LastPosition = moLastPosition
|
||
End Property
|
||
|
||
'Different methods from the puck.
|
||
Public Sub Init(ByVal sMedia As String, sFile As String)
|
||
Set moPuck = D3DUtil_LoadFromFile(AddDirSep(sMedia) & sFile, Nothing, Nothing)
|
||
End Sub
|
||
|
||
Public Sub UpdatePosition()
|
||
Dim RealVelocity As D3DVECTOR
|
||
|
||
'Here we will update the position of the puck
|
||
'and move it based on the velocity assigned.
|
||
If mlPuckTime = 0 Then mlPuckTime = timeGetTime
|
||
'First calculate the 'real' velocity (based on the time)
|
||
RealVelocity.X = ((timeGetTime - mlPuckTime) / 100) * moVelocity.X
|
||
RealVelocity.Y = ((timeGetTime - mlPuckTime) / 100) * moVelocity.Y
|
||
RealVelocity.z = ((timeGetTime - mlPuckTime) / 100) * moVelocity.z
|
||
'Let's save our current position
|
||
moLastPosition = moPosition
|
||
|
||
moPosition.X = moPosition.X + RealVelocity.X
|
||
moPosition.Y = moPosition.Y + RealVelocity.Y
|
||
moPosition.z = moPosition.z + RealVelocity.z
|
||
|
||
If Spinning Then
|
||
'Update Puck Spin
|
||
mnPuckSpin = mnPuckSpin + ((((timeGetTime - mlPuckTime) / 100) * mnDefaultSpin) * mnSpinDir)
|
||
If mnPuckSpin > 2 * g_pi Then mnPuckSpin = 0
|
||
End If
|
||
|
||
mlPuckTime = timeGetTime
|
||
End Sub
|
||
|
||
Public Sub Render(dev As Direct3DDevice8)
|
||
Dim matRot As D3DMATRIX, matTrans As D3DMATRIX
|
||
Dim matPuck As D3DMATRIX
|
||
|
||
D3DXMatrixRotationAxis matRot, vec3(0, 1, 0), mnPuckSpin
|
||
D3DXMatrixTranslation matTrans, moPosition.X, moPosition.Y, moPosition.z
|
||
D3DXMatrixMultiply matPuck, matRot, matTrans
|
||
|
||
moPuck.SetMatrix matPuck
|
||
moPuck.Render dev
|
||
|
||
End Sub
|
||
|
||
Public Sub LaunchPuck()
|
||
Randomize
|
||
DefaultStartPosition
|
||
Do While (D3DXVec3Length(moVelocity) < (MaximumPuckVelocity / 4)) And (Abs(moVelocity.z) < 0.2) 'Make sure there is *some* z movement
|
||
moVelocity.z = Rnd * (MaximumPuckVelocity / 3)
|
||
moVelocity.X = Rnd * (MaximumPuckVelocity / 3)
|
||
If Rnd > 0.5 Then moVelocity.X = moVelocity.X * -1
|
||
If Rnd < 0.5 Then moVelocity.z = moVelocity.z * -1
|
||
Loop
|
||
End Sub
|
||
|
||
Public Sub DefaultStartPosition()
|
||
moPosition = vec3(0, 2.5, 0)
|
||
moVelocity = vec3(0, 0, 0)
|
||
moLastPosition = vec3(0, 0, 0)
|
||
End Sub
|
||
|
||
Public Sub ChangePuckVelocity(oPaddle As cPaddle, oAudio As cAudio, Optional ByVal fIgnoreMax As Boolean = False)
|
||
|
||
Dim vDir As D3DVECTOR
|
||
Dim a As Single, b As Single, c As Single
|
||
Dim t0 As Single, t1 As Single
|
||
Dim vIntersect As D3DVECTOR, vIntersectHigh As D3DVECTOR
|
||
Dim oPlane As D3DPLANE, matReflect As D3DMATRIX
|
||
Dim oPoint As D3DVECTOR, vNewVelDir As D3DVECTOR
|
||
Dim vPuck As D3DVECTOR, tSmall As Single
|
||
Dim nVelocity As Single, nVelocityPaddle As Single
|
||
Dim vNewVelPad As D3DVECTOR
|
||
|
||
'We hit with the paddle, randomly change the spin direction
|
||
UpdatePuckSpin
|
||
glPaddleCollideTime = timeGetTime
|
||
'gfRecentlyHitPaddle = True
|
||
'Notify the user that the puck hit the paddle by playing a sound
|
||
If Not (oAudio Is Nothing) Then oAudio.PlayHitSound
|
||
'Let's store the original velocity
|
||
nVelocity = D3DXVec3Length(moVelocity)
|
||
nVelocityPaddle = D3DXVec3Length(oPaddle.Velocity) * gnPaddleMass
|
||
'First we need to find the intersection point
|
||
'To do that we first need to solve for t:
|
||
'x = Dxt + x0
|
||
'z = Dzt + z0
|
||
D3DXVec3Subtract vPuck, moPosition, oPaddle.Position
|
||
D3DXVec3Normalize vDir, moVelocity
|
||
a = 1 ' (vDir.x ^ 2) + (vDir.z ^ 2) will always be one since the vector is normalized
|
||
b = (2 * vPuck.X * vDir.X) + (2 * vPuck.z * vDir.z)
|
||
c = ((vPuck.X ^ 2) + (vPuck.z ^ 2) - ((gnPaddleRadius + gnPuckRadius) ^ 2))
|
||
't = (-b <20> SQR(b<>-4ac))/2a
|
||
If (b ^ 2) - (4 * a * c) > 0 Then
|
||
t0 = (-b + Sqr((b ^ 2) - (4 * a * c))) / (2 * a)
|
||
t1 = (-b - Sqr((b ^ 2) - (4 * a * c))) / (2 * a)
|
||
Else 'We shouldn't hit this case, but just in case.
|
||
t0 = 0
|
||
t1 = 0
|
||
End If
|
||
|
||
Dim vInt1 As D3DVECTOR, vInt2 As D3DVECTOR
|
||
Dim vDifInt1 As D3DVECTOR, vDifInt2 As D3DVECTOR
|
||
'Find both possible intersection points
|
||
vInt1.X = (vDir.X * t0) + vPuck.X: vInt1.z = (vDir.z * t0) + vPuck.z
|
||
vInt2.X = (vDir.X * t1) + vPuck.X: vInt2.z = (vDir.z * t1) + vPuck.z
|
||
'Find the difference from the starting location
|
||
D3DXVec3Subtract vDifInt1, oPaddle.Position, vInt1
|
||
D3DXVec3Subtract vDifInt2, oPaddle.Position, vInt2
|
||
|
||
'Find the smallest t
|
||
'If t0 > t1 Then
|
||
If D3DXVec3Length(vDifInt1) < D3DXVec3Length(vDifInt2) Then
|
||
tSmall = t1
|
||
Else
|
||
tSmall = t0
|
||
End If
|
||
'Let's get the intersected point
|
||
vIntersect.X = (vDir.X * tSmall) + vPuck.X
|
||
vIntersect.z = (vDir.z * tSmall) + vPuck.z
|
||
|
||
'Create a new vector with an enormously high Y field to create our reflection plane
|
||
vIntersectHigh = vIntersect
|
||
vIntersectHigh.Y = 500
|
||
'Let's create a plane from this point
|
||
D3DXPlaneFromPoints oPlane, vec3(0, 0, 0), vIntersect, vIntersectHigh
|
||
|
||
'Now we can create a reflection matrix based on this plane
|
||
D3DXMatrixReflect matReflect, oPlane
|
||
'Create a new point that is reflected
|
||
D3DXVec3TransformCoord oPoint, vPuck, matReflect
|
||
D3DXVec3Subtract vNewVelDir, oPoint, vIntersect
|
||
'Normalize the vector
|
||
D3DXVec3Normalize vNewVelDir, vNewVelDir
|
||
vNewVelDir.X = -vNewVelDir.X
|
||
vNewVelDir.z = -vNewVelDir.z
|
||
D3DXVec3Scale moVelocity, vNewVelDir, nVelocity
|
||
If nVelocityPaddle > 0 Then 'The paddle is moving, add it's velocity
|
||
'Now let's add the velocity of the paddle to our resulting velocity
|
||
D3DXVec3Normalize vNewVelPad, oPaddle.Velocity
|
||
D3DXVec3Scale vNewVelPad, vNewVelPad, nVelocityPaddle
|
||
D3DXVec3Add moVelocity, moVelocity, vNewVelPad
|
||
End If
|
||
Debug.Print "Old Velocity:"; nVelocity; " - New Velocity:"; D3DXVec3Length(moVelocity)
|
||
'If we are limiting the velocity to it's maximum (most times), do so
|
||
If Not fIgnoreMax Then EnsurePuckVelocityIsBelowMax
|
||
End Sub
|
||
|
||
Public Sub CheckCollisions(oPaddle() As cPaddle, Optional oAudio As cAudio = Nothing)
|
||
'First we should check to see if we are scoring in this frame.
|
||
Dim nDistance As Single
|
||
Dim lCount As Long, fCollided As Boolean
|
||
Dim lCollided As Long, nCollideDist As Single
|
||
|
||
If gfScored Then Exit Sub
|
||
'Check to see if the puck has collided with any of the walls
|
||
'We could do an exhaustive check to see if any of the polygons collide, but since the table
|
||
'is static, in the name of faster calculations, we will use a group of constants defining the
|
||
'edges of the walls. We will check those instead.
|
||
|
||
'If the puck does hit one of the walls, we can easily calculate it's new direction by simply reversing
|
||
'it's velocity (of that vector). If we want to be even more accurate we can lower the velocity by a small amount as well
|
||
|
||
'The left and right walls are bound to the X axis
|
||
If moPosition.X > (gnSideLeftWallEdge - (gnPuckRadius)) Then
|
||
'We hit the wall
|
||
'Reverse the velocity of the X axis
|
||
moVelocity = vec3((moVelocity.X * -1) * gnVelocityDamp, 0, moVelocity.z)
|
||
moPosition = vec3((gnSideLeftWallEdge - (gnPuckRadius)), moPosition.Y, moPosition.z)
|
||
If Not (oAudio Is Nothing) Then oAudio.PlayBankSound
|
||
gfRecentlyHitPaddle = False
|
||
ElseIf moPosition.X < (gnSideRightWallEdge + (gnPuckRadius)) Then
|
||
'We hit the wall
|
||
moVelocity = vec3((moVelocity.X * -1) * gnVelocityDamp, 0, moVelocity.z)
|
||
moPosition = vec3((gnSideRightWallEdge + (gnPuckRadius)), moPosition.Y, moPosition.z)
|
||
If Not (oAudio Is Nothing) Then oAudio.PlayBankSound
|
||
gfRecentlyHitPaddle = False
|
||
End If
|
||
|
||
'The front and rear walls are count to the Z axis
|
||
If moPosition.z > (gnNearWallEdge - (gnPuckRadius)) Then
|
||
'Only reverse the velocity if we hit the sides of the 'scoring area'
|
||
If (moPosition.X > (gnScoringEdgeLeft - (gnPuckRadius))) Or (moPosition.X < (gnScoringEdgeRight + (gnPuckRadius))) Then
|
||
'We hit the wall
|
||
'Reverse the velocity of the Z axis
|
||
moVelocity = vec3(moVelocity.X, 0, (moVelocity.z * -1) * gnVelocityDamp)
|
||
moPosition = vec3(moPosition.X, moPosition.Y, gnNearWallEdge - (gnPuckRadius))
|
||
If Not (oAudio Is Nothing) Then oAudio.PlayBankSound
|
||
gfRecentlyHitPaddle = False
|
||
End If
|
||
ElseIf moPosition.z < (gnFarWallEdge + (gnPuckRadius)) Then
|
||
If (moPosition.X > (gnScoringEdgeLeft - (gnPuckRadius))) Or (moPosition.X < (gnScoringEdgeRight - (gnPuckRadius))) Then
|
||
'We hit the wall
|
||
moVelocity = vec3(moVelocity.X, 0, (moVelocity.z * -1) * gnVelocityDamp)
|
||
moPosition = vec3(moPosition.X, moPosition.Y, gnFarWallEdge + (gnPuckRadius))
|
||
If Not (oAudio Is Nothing) Then oAudio.PlayBankSound
|
||
gfRecentlyHitPaddle = False
|
||
End If
|
||
End If
|
||
|
||
'Next we should check to see if the puck has collided with either of the paddles
|
||
'We will use a simple formula to determine if the puck has collided with one of the
|
||
'paddles. Simply put if the distance between the center of the puck, and the center
|
||
'of the paddle in question is greater than the radius of the puck + the radius of the
|
||
'paddle, they haven't collided
|
||
Dim vecDif As D3DVECTOR
|
||
|
||
If ((timeGetTime - glPaddleCollideTime) > glMinDelayPaddleHit) Or (Not gfRecentlyHitPaddle) Then
|
||
gfRecentlyHitPaddle = False
|
||
For lCount = 0 To 1 'Both paddles
|
||
'We only check the X/Z coords because in this demo the puck will never leave the table
|
||
'so it will maintain a constant Y coord.
|
||
D3DXVec3Subtract vecDif, moPosition, oPaddle(lCount).Position
|
||
nDistance = D3DXVec3Length(vecDif)
|
||
If nDistance < (gnPaddleRadius + gnPuckRadius) Then 'They have collided
|
||
nCollideDist = nDistance
|
||
lCollided = lCount
|
||
fCollided = True
|
||
If gfMultiplayer Then
|
||
'Let each client handle it's own collision detection
|
||
'in a multiplayer game. This balances the load between
|
||
'the host machine, and the client machine and gives the
|
||
'most realistic playing feel.
|
||
If glMyPaddleID = lCount Then 'We collided with our paddle
|
||
ChangePuckVelocity oPaddle(lCount), oAudio
|
||
SendPuck
|
||
SendCollidePaddle
|
||
End If
|
||
Else
|
||
ChangePuckVelocity oPaddle(lCount), oAudio
|
||
End If
|
||
End If
|
||
Next
|
||
End If
|
||
' Make sure we aren't colliding anymore
|
||
If fCollided Then EnsurePuckIsNotInPaddle nCollideDist, oPaddle(lCollided)
|
||
'Lastly we should check if we have scored (on either side)
|
||
If gfMultiplayer And (Not gfHost) Then Exit Sub 'Only the host should check for scoring
|
||
If moPosition.z > (gnNearWallEdge) Then
|
||
'We scored!
|
||
goPuck.DropPuckIntoScoringPosition goAudio
|
||
ElseIf moPosition.z < (gnFarWallEdge) Then
|
||
'We scored!
|
||
goPuck.DropPuckIntoScoringPosition goAudio
|
||
End If
|
||
End Sub
|
||
|
||
Public Sub EnsurePuckIsNotInPaddle(ByVal nDistance As Single, oPaddle As cPaddle, Optional ByVal fSentPaddle As Boolean = False)
|
||
'Move the paddle back out so it's not hitting the puck
|
||
Dim vDir As D3DVECTOR, vScale As D3DVECTOR, vPaddleVel As D3DVECTOR
|
||
|
||
If fSentPaddle Then
|
||
D3DXVec3Subtract vPaddleVel, oPaddle.LastPosition, oPaddle.Position
|
||
'Get the direction vector by normalizing the paddle's velocity
|
||
D3DXVec3Normalize vDir, vPaddleVel
|
||
Else
|
||
'Get the direction vector by normalizing the pucks velocity
|
||
D3DXVec3Normalize vDir, moVelocity
|
||
End If
|
||
'Scale the vector, just enough to get it out of the paddle
|
||
D3DXVec3Scale vScale, vDir, (gnPuckRadius + gnPaddleRadius) - nDistance
|
||
'Move the puck to it's new location
|
||
D3DXVec3Add moPosition, moPosition, vScale
|
||
'Now, let's increase the pucks velocity that much as well..
|
||
If fSentPaddle Then D3DXVec3Add moVelocity, moVelocity, vScale
|
||
End Sub
|
||
|
||
Private Sub UpdatePuckSpin()
|
||
Randomize
|
||
If Rnd > 0.5 Then
|
||
mnSpinDir = mnSpinDir * -1
|
||
'Update the spin, change speed from 75%-125% of current speed..
|
||
mnDefaultSpin = (Rnd * (mnSpinDir * 0.75)) + (mnSpinDir * 0.5)
|
||
If Abs(mnDefaultSpin) > mnMaxSpinSpeed Then
|
||
mnDefaultSpin = mnMaxSpinSpeed * (Abs(mnDefaultSpin) \ mnDefaultSpin)
|
||
End If
|
||
End If
|
||
End Sub
|
||
|
||
Public Sub CleanupFrame()
|
||
moPuck.Destroy
|
||
Set moPuck = Nothing
|
||
End Sub
|
||
|
||
Public Sub DropPuckIntoScoringPosition(oAudio As cAudio, Optional ByVal fFromReceive As Boolean = False)
|
||
|
||
gfScored = True
|
||
glTimeCompPaddle = 0
|
||
If Not gfMultiplayer Then
|
||
With goPaddle(1).Velocity
|
||
.X = 0: .z = 0
|
||
End With
|
||
End If
|
||
glTimePuckScored = timeGetTime
|
||
oAudio.PlayScoreSound
|
||
If gfMultiplayer Then
|
||
If Not gfHost And Not fFromReceive Then Exit Sub
|
||
End If
|
||
'First stop the velocity
|
||
moVelocity = vec3(0, 0, 0)
|
||
|
||
With moPosition
|
||
'Now position the puck
|
||
If .z < 0 Then
|
||
gPlayer(1).Score = gPlayer(1).Score + 1
|
||
.z = gnFarWallEdge - 1.2
|
||
ElseIf .z > 0 Then
|
||
.z = gnNearWallEdge + 1.2
|
||
gPlayer(0).Score = gPlayer(0).Score + 1
|
||
End If
|
||
If Abs(.X) > gnScoringEdgeLeft / 3 Then
|
||
If Abs(.X) <> .X Then
|
||
.X = gnScoringEdgeRight / 3
|
||
Else
|
||
.X = gnScoringEdgeLeft / 3
|
||
End If
|
||
End If
|
||
.Y = gnPuckScored
|
||
End With
|
||
|
||
Spinning = False
|
||
'If we are the host, notify everyone that we've scored
|
||
If gfMultiplayer Then NotifyPlayersWeScored
|
||
|
||
End Sub
|
||
|
||
Public Function FadeMesh(FadeInterval As Single) As Boolean
|
||
Dim lNumMaterial As Long
|
||
Dim lCount As Long
|
||
Dim oMaterial As D3DMATERIAL8
|
||
Dim fDoneFading As Boolean
|
||
Dim oMesh As CD3DMesh
|
||
Dim nInternalInterval As Single
|
||
Static lFadeTime As Long
|
||
|
||
nInternalInterval = FadeInterval
|
||
If lFadeTime = 0 Then
|
||
lFadeTime = timeGetTime
|
||
Exit Function 'We'll do the fade next render pass
|
||
End If
|
||
nInternalInterval = (((timeGetTime - lFadeTime) / 1000000) * nInternalInterval)
|
||
|
||
Set oMesh = moPuck.FindChildObject("puck", 0)
|
||
fDoneFading = True
|
||
lNumMaterial = oMesh.GetMaterialCount
|
||
For lCount = 0 To lNumMaterial - 1
|
||
oMaterial = oMesh.GetMaterial(lCount)
|
||
If nInternalInterval > 0 And oMaterial.diffuse.a <= 1 Then
|
||
oMaterial.diffuse.a = oMaterial.diffuse.a + nInternalInterval
|
||
fDoneFading = False
|
||
ElseIf nInternalInterval < 0 And oMaterial.diffuse.a >= -1 Then
|
||
oMaterial.diffuse.a = oMaterial.diffuse.a + nInternalInterval
|
||
fDoneFading = False
|
||
End If
|
||
oMesh.SetMaterial lCount, oMaterial
|
||
Next
|
||
FadeMesh = fDoneFading
|
||
End Function
|
||
|
||
Public Sub PauseSystem(ByVal fPause As Boolean)
|
||
If Not fPause Then
|
||
mlPuckTime = timeGetTime
|
||
End If
|
||
End Sub
|
||
|
||
'************
|
||
'Private functions that the public subs here will call, but the main application doesn't need to know about
|
||
|
||
Private Sub EnsurePuckVelocityIsBelowMax()
|
||
Dim VelVec As D3DVECTOR
|
||
'Let's make sure the puck's velocity isn't above the max,
|
||
'and if it is, lower it to the max velocity
|
||
If D3DXVec3Length(moVelocity) > MaximumPuckVelocity Then
|
||
'Yup, lower the velocity to the max
|
||
Dim vNrm As D3DVECTOR
|
||
|
||
D3DXVec3Normalize vNrm, moVelocity
|
||
D3DXVec3Scale VelVec, vNrm, MaximumPuckVelocity
|
||
moVelocity = VelVec
|
||
End If
|
||
End Sub
|
||
|
||
Private Sub Class_Initialize()
|
||
mnSpinDir = 1
|
||
mnDefaultSpin = 0.15
|
||
Set moPuck = Nothing
|
||
DefaultStartPosition
|
||
End Sub
|
||
|
||
Private Sub Class_Terminate()
|
||
If Not moPuck Is Nothing Then moPuck.Destroy
|
||
Set moPuck = Nothing
|
||
End Sub
|