08-22-2025, 05:20 AM
Hey folks,
Im making it as a way to learn/develop AI for NPC's - It's limited as im just starting but i thought that some off you might want to code your own AI's, path finders, etc..and we can pit them against each other!
Press V to change views, currently players are very stupid and randomly roam...youll see that its structured so adding actions/reactions is quite simple....and yes, Google answers AI helped me make it...
Unseen
Im making it as a way to learn/develop AI for NPC's - It's limited as im just starting but i thought that some off you might want to code your own AI's, path finders, etc..and we can pit them against each other!
Press V to change views, currently players are very stupid and randomly roam...youll see that its structured so adding actions/reactions is quite simple....and yes, Google answers AI helped me make it...
Unseen
Code: (Select All)
'// GDK2 Dev and Demo - Player AI's - Objective, action and more \\\
RANDOMIZE TIMER
CONST PI_SINGLE = _PI
CONST Rot_Inc! = _PI / 30 '// maximum rotation each frame
CONST Player_Speed! = 1 '// For vector calcs to update positions
CONST BLOCK_SIZE = 10
CONST CLAIM_TIME_UNOWNED = 2.0 '// Seconds to claim an unowned square
CONST CLAIM_TIME_OPPONENT = 5.0 '// Seconds to claim an opponent's square (longer)
'// map blocks -defines a block
CONST MAP_BLOCK_UNOWNED = 0
CONST MAP_BLOCK_RED_EMPTY = 1
CONST MAP_BLOCK_BLUE_EMPTY = 2
CONST MAP_BLOCK_UNOWNED_CHECKPOINT = 3 ' Unclaimed Checkpoint
CONST MAP_BLOCK_RED_CHECKPOINT = 4 ' Red Team owned Checkpoint
CONST MAP_BLOCK_BLUE_CHECKPOINT = 5 ' Blue Team owned Checkpoint
CONST MAP_BLOCK_WALL = 6 '// Will stop movment in this direction and possibly vision
CONST MAP_BLOCK_HIDDEN = 0 '// not known by the team
CONST MAP_BLOCK_KNOWN = 1 '// explored and mapped
' Player Action States
CONST PLAYER_ACTION_IDLE = 0
CONST PLAYER_ACTION_EXPLORING = 1
CONST PLAYER_ACTION_CLAIMING = 2
CONST PLAYER_ACTION_SEEK_SQUARE = 3
CONST PLAYER_ACTION_SEEK_ENEMY = 4
CONST PLAYER_ACTION_SEEK_CP = 5
CONST PLAYER_ACTION_SHOOTING = 6
CONST PLAYER_ACTION_ROTATING_TO_TARGET = 7 '// Target is a block, it may have a enemy on it but its also for movement
CONST PLAYER_ACTION_WAITING_TO_RESPAWN = 8 ' Player is dead, awaiting team cooldown
CONST PLAYER_ACTION_DEFENDING = 9
CONST PLAYER_ACTION_MOVING = 10
CONST MIN_PLAYERS_PER_TEAM = 3
CONST TEAM_RED_INDEX = 0 ' Assuming Team(0) is Red
CONST TEAM_BLUE_INDEX = 1 ' Assuming Team(1) is Blue
CONST TEAM_VALUE_UNOWNED = 0 ' Value stored in Checkpoint.Team for unowned
CONST TEAM_VALUE_RED = 1 ' Value stored in Checkpoint.Team for Red
CONST TEAM_VALUE_BLUE = 2 ' Value stored in Checkpoint.Team for Blue
'// team directives
CONST TEAM_DEFEND = 1
CONST TEAM_EXPLORE = 2
CONST TEAM_HUNT = 3
CONST TEAM_GATHER = 4
CONST TEAM_BUILD = 5
CONST TEAM_EXPAND = 6
TYPE Checkpoint
Col AS _BYTE
Row AS _BYTE
Team AS _BYTE '// (0 = Unowned, 1 = red, 2 = blue)
Rot AS SINGLE
END TYPE
TYPE DP_Map '// Basics of the map
Columns AS INTEGER ' Number of columns (width) in the grid
Rows AS INTEGER ' Number of rows (height) in the grid
CellWidth AS INTEGER ' Size in pixels of a single map block (width)
CellHeight AS INTEGER ' Size in pixels of a single map block (height)
OffsetX AS INTEGER ' X-offset (pixels) for drawing the map on screen
OffsetY AS INTEGER ' Y-offset (pixels) for drawing the map on screen
Num_CP AS _BYTE '// minimum of 2, 1 for each team, max 9
END TYPE
TYPE Player
IsAlive AS _BYTE ' 0 = Eliminated, 1 = Active, 2 = Waiting (e.g., for individual respawn if implemented)
Action AS INTEGER ' Current action being performed (refer to PLAYER_ACTION_ constants) - finsihed reset to idle which wil trigger new action decision
Objective AS _BYTE '// set by team directive - search and destry would mean, need to be able to shot so claim a square then explore until an entmy is found, shot the enemy and go find another square to claim to reload
'// the overriding Team AI may be able to give individual task to players, i,e one defends a block whilst two others claim
Col AS _BYTE '// For when claiming blocks - easy indexing
Row AS _BYTE
X AS INTEGER ' Current X-coordinate
Y AS INTEGER ' Current Y-coordinate
Rotation AS SINGLE ' 0=North, 90=East, 180=South, 270=West
Range AS INTEGER ' Attack range (number of squares)
FOV_Angle AS SINGLE
CanShoot AS _BYTE ' 0 = GDK_False, 1 = GDK_true (Allows shooting if true, set to false after a shot)
ClaimTime AS SINGLE '// when we start to claim the blocks value sets this
ClaimTimer AS SINGLE '// time since we started claimng if action is claimng then if >= claimtime then we have claimed this suqare
Target_Col AS _BYTE '// if payer is moving or rotating its to this block
Target_Row AS _BYTE
TargetRotation AS SINGLE
END TYPE
TYPE Team
NumActivePlayers AS _BYTE
Reserves AS _BYTE
Checkpoints AS _BYTE
Directive AS _BYTE '// AI main focus - i.e explore, claim terratory, search and destroy, defend \\
IS_CPU AS _BYTE '// if true then no user input for now we wont do user input and both teams are cpu
END TYPE
REM $INCLUDE:'UnseenGDK_Dev\GDk2.bi'
'//////////////////////// Screen setup \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
GDK2_System_New "UnseenGDK_Dev\", 800, 600, GDK_FALSE, "Dominion Protocol"
_SCREENMOVE 0, 0
'////////////////////////////////////////////////////////////////
DIM SHARED deltaTime AS DOUBLE ' Make deltaTime directly accessible
REDIM SHARED Map_Block(49, 49) AS _BYTE
REDIM SHARED Team_Map(1, 49, 49) AS _BYTE '// team map is for showing what team knows i.e what its expored, and also what it can see
REDIM SHARED Map AS DP_Map
REDIM SHARED Map_CP(8) AS Checkpoint '// incase we want to expand later this allows a little flex
REDIM SHARED Team(1) AS Team
REDIM SHARED Player(1, 10) AS Player
DIM SHARED View_Mode AS _BYTE '// 0 is show everything, 1 = show red team, 2 = show blue team, 3 = both red and blue overlayed
RESTORE Level_Data
Load_Level
'// Temp to force CPU for both teams
Team(0).IS_CPU = GDK_TRUE
Team(1).IS_CPU = GDK_TRUE
Team(0).Directive = TEAM_EXPLORE
Team(1).Directive = TEAM_EXPLORE
'// Ensure the teams are initialised before starting
FOR i% = 0 TO 2
Team_Check_Spawn 0
Team_Check_Spawn 1
NEXT
DO
GDK2_System_Update
Update_Delta_Time
CLS
'// Game logic \\ First check each team top make sure they have the correct number of players activeated, if not
'// check reserves and if avbailable spawn new player
Team_Check_Spawn 0
Team_Check_Spawn 1
'// Update out players \\
Team_Update_Player 0
Team_Update_Player 1
'// NON LOGIC STUFF \\\
'// View settings \\
IF GDK_KB(0).V AND NOT GDK_KB(1).V THEN '// press v to switch views
IF View_Mode < 4 THEN View_Mode = View_Mode + 1 ELSE View_Mode = 0
END IF
SELECT CASE View_Mode
CASE 0
Draw_Map '// draws everything
Draw_Players 0
Draw_Players 1
CASE 1 '// draw red team map
Draw_Team_Map 0
Draw_Players 0
CASE 2 '// draw blue team map
Draw_Team_Map 1
Draw_Players 1
CASE 3 '// draw both teams maps
Draw_Team_Map 0
Draw_Team_Map 1
Draw_Players 0
Draw_Players 1
CASE 4 '// everthing overlayed with both team maps
Draw_Map '// draws everything
Draw_Team_Map 0
Draw_Team_Map 1
Draw_Players 0
Draw_Players 1
END SELECT
_DISPLAY
LOOP UNTIL INKEY$ = CHR$(27)
Level_Data: '// col,row,cell w,h,num cp's and then col,row,owner *(num cps) and finally reerves count
DATA 50,50,10,10,5,10,10,1,40,40,2,30,5,2,5,35,1,25,20,0,10
REM $INCLUDE:'UnseenGDK_Dev\GDk2.bm'
SUB Team_Check_Spawn_new (TeamIndex AS INTEGER)
DIM MaxPlayers AS INTEGER, CP_Spawn AS INTEGER, CpCnt AS INTEGER, i AS INTEGER, j AS INTEGER
DIM currentCPTeamVal AS _BYTE
IF Team(TeamIndex).Checkpoints > 3 THEN MaxPlayers = Team(TeamIndex).Checkpoints ELSE MaxPlayers = 3
IF MaxPlayers > 11 THEN MaxPlayers = 11
IF Team(TeamIndex).NumActivePlayers < MaxPlayers AND Team(TeamIndex).Reserves > 0 THEN
CP_Spawn = INT(RND * Team(TeamIndex).Checkpoints)
FOR i = 0 TO Map.Num_CP - 1
currentCPTeamVal = Map_CP(i).Team ' Value is 0, 1, or 2
IF currentCPTeamVal = (TeamIndex + 1) THEN
IF CpCnt = CP_Spawn THEN
FOR j = 0 TO 10
IF Player(TeamIndex, j).IsAlive = 0 THEN
' Set player's pixel position based on checkpoint's Col/Row
Player(TeamIndex, j).X = Map.OffsetX + Map_CP(i).Col * Map.CellWidth + (Map.CellWidth / 2)
Player(TeamIndex, j).Y = Map.OffsetY + Map_CP(i).Row * Map.CellHeight + (Map.CellHeight / 2)
' Update the player's block coordinates based on spawn location
Player(TeamIndex, j).Col = Map_CP(i).Col
Player(TeamIndex, j).Row = Map_CP(i).Row
' --- Rotation Logic Start ---
' Increment the checkpoint's rotation by 45 degrees (_PI / 4 radians)
IF Map_CP(i).Rot < 2 * _PI THEN ' 2 * _PI is 360 degrees in radians
Map_CP(i).Rot = Map_CP(i).Rot + (_PI / 4)
ELSE
Map_CP(i).Rot = 0 ' Reset to 0 after full circle
END IF
' Assign this new rotation to the spawned player
Player(TeamIndex, j).Rotation = Map_CP(i).Rot
' --- Rotation Logic End ---
CALL SpawnPlayer(TeamIndex, j, Map_CP(i)) ' This call needs to handle the updated player data
Team(TeamIndex).Reserves = Team(TeamIndex).Reserves - 1
EXIT FOR ' Exit player slot loop
END IF
NEXT
EXIT FOR ' Exit checkpoint loop
END IF
CpCnt = CpCnt + 1
END IF
NEXT
ELSE
END IF
END SUB
SUB Team_Update_Player (TeamIndex%)
DIM oldp AS Player
FOR i% = 0 TO 10
IF Player(TeamIndex%, i%).IsAlive = 1 THEN
oldp = Player(TeamIndex%, i%)
SELECT CASE Player(TeamIndex%, i%).Action
CASE PLAYER_ACTION_IDLE '// This will trigger a new action dictated by the teams directive
SELECT CASE Team(TeamIndex%).Directive
CASE TEAM_DEFEND '// make players cover claimed check points -
Player(TeamIndex%, i%).Action = PLAYER_ACTION_DEFENDING
CASE TEAM_EXPLORE '// roam without trying to claim sqaures, just gather tapography data
Player(TeamIndex%, i%).Action = PLAYER_ACTION_EXPLORING
CASE TEAM_HUNT '// seek out enemy players and shoot them
Player(TeamIndex%, i%).Action = PLAYER_ACTION_SEEK_ENEMY
CASE TEAM_EXPAND '// claim new sqaures for the team
Player(TeamIndex%, i%).Action = PLAYER_ACTION_SEEK_SQUARE '// once a adjacent empty or enemy sqaure is found, move to it and claim
END SELECT
CASE PLAYER_ACTION_EXPLORING '// Move forward until an unowned block is hit or wall/boundary
Player_Update_Position Player(TeamIndex%, i%) '//
IF NOT IsPlayerOnMap(Player(TeamIndex%, i%)) THEN
Player(TeamIndex%, i%) = oldp
Player(TeamIndex%, i%).Action = PLAYER_ACTION_IDLE
DO
Player(TeamIndex%, i%) = oldp
Player(TeamIndex%, i%).Rotation = Player(TeamIndex%, i%).Rotation + (_PI / INT(RND * 32))
Player_Update_Position Player(TeamIndex%, i%) '//
LOOP UNTIL IsPlayerOnMap(Player(TeamIndex%, i%))
END IF
CASE PLAYER_ACTION_CLAIMING '// claiming a square
CASE PLAYER_ACTION_SEEK_SQUARE '// looking for a new sqaure to claim - only sqaures adjacent to team owned sqaures can be claimed
'// find nearest square we can claim,
CASE PLAYER_ACTION_SEEK_ENEMY '// Hunting enemies - wont do this unless it can shot
CASE PLAYER_ACTION_SEEK_CP '// searching for a cp
CASE PLAYER_ACTION_SHOOTING '// enemy found, in range and player has rotated to face it - lasers! No health, instant death and then resppawn
CASE PLAYER_ACTION_ROTATING_TO_TARGET '// a target was found in fov but we have to rotate the get a good shot - a default global rot val will be set
'// dont need a specific functions as this is simple
IF Player(TeamIndex%, i%).Rotation >= Player(TeamIndex%, i%).TargetRotation - Rot_Inc! THEN
IF Player(TeamIndex%, i%).Rotation <= Player(TeamIndex%, i%).TargetRotation + Rot_Inc! THEN '// within range '// go back to idle and team directive will set next order
Player(TeamIndex%, i%).Action = PLAYER_ACTION_IDLE
ELSE '// over range so reduce
Player(TeamIndex%, i%).Rotation = Player(TeamIndex%, i%).Rotation - Rot_Inc!
END IF
ELSE '// Under range so increase
Player(TeamIndex%, i%).Rotation = Player(TeamIndex%, i%).Rotation + Rot_Inc!
END IF
'//
CASE PLAYER_ACTION_DEFENDING '// team has instructed to defend...if not at at check point, find one that no one else is defending and set target, rotate move and etc
CASE PLAYER_ACTION_MOVING
CASE ELSE
'// Sometings wrong!
END SELECT
Team_Map_Update TeamIndex%, Player(TeamIndex%, i%)
END IF
NEXT
END SUB
SUB Player_Update_Position (PlayerRef AS Player) ' No deltaTime parameter needed
PlayerRef.X = PlayerRef.X + COS(PlayerRef.Rotation) * Player_Speed!
PlayerRef.Y = PlayerRef.Y + SIN(PlayerRef.Rotation) * Player_Speed!
END SUB
SUB Player_Update_COLROW (p AS Player)
' Calculate Col and Row based on player's pixel position (p.X, p.Y)
' and map's offset/cell dimensions.
' Note: (p.X - Map.OffsetX) shifts coordinates relative to the map's drawing origin.
p.Col = INT((p.X - Map.OffsetX) / Map.CellWidth)
p.Row = INT((p.Y - Map.OffsetY) / Map.CellHeight)
END SUB
SUB Team_Map_Update (TeamIndex AS INTEGER, p AS Player)
' Ensure p.Col and p.Row are up-to-date based on p.X, p.Y
Player_Update_COLROW p
DIM colCenter AS INTEGER, rowCenter AS INTEGER
DIM playerPixelX AS SINGLE, playerPixelY AS SINGLE
DIM scanCol AS INTEGER, scanRow AS INTEGER
DIM xOffset AS INTEGER, yOffset AS INTEGER
DIM currentRange AS INTEGER
DIM playerRot AS SINGLE ' Player's facing direction (in radians)
DIM playerFOV AS SINGLE ' Player's total FOV angle (in radians)
DIM halfFOV AS SINGLE
DIM relativeAngle AS SINGLE
DIM angleToBlock AS SINGLE
DIM targetBlockPixelX AS SINGLE, targetBlockPixelY AS SINGLE
DIM stepCount AS INTEGER
DIM pathBlocked AS _BYTE
DIM currentPathX AS SINGLE, currentPathY AS SINGLE
DIM pathCol AS INTEGER, pathRow AS INTEGER
DIM distanceToTarget AS SINGLE
' Get player's current block as the center of the scan and their FOV parameters
colCenter = p.Col
rowCenter = p.Row
playerPixelX = p.X ' Use player's center pixel for line-of-sight calculations
playerPixelY = p.Y
currentRange = p.Range
playerRot = p.Rotation
playerFOV = p.FOV_Angle
halfFOV = playerFOV / 2
' --- Loop through a square region around the player based on currentRange ---
FOR xOffset = -currentRange TO currentRange
FOR yOffset = -currentRange TO currentRange
scanCol = colCenter + xOffset
scanRow = rowCenter + yOffset
' 1. Check if the block is within map boundaries
IF scanCol >= 0 AND scanCol < Map.Columns AND scanRow >= 0 AND scanRow < Map.Rows THEN
' 2. Ensure player's own tile is always known
IF xOffset = 0 AND yOffset = 0 THEN
IF Team_Map(TeamIndex, scanCol, scanRow) = MAP_BLOCK_HIDDEN THEN
Team_Map(TeamIndex, scanCol, scanRow) = MAP_BLOCK_KNOWN
END IF
ELSE ' Not the player's own tile, proceed with FOV and LOS check
' 3. FOV Cone Check (Angle Check)
' Get center pixel of the target block
targetBlockPixelX = Map.OffsetX + scanCol * Map.CellWidth + (Map.CellWidth / 2)
targetBlockPixelY = Map.OffsetY + scanRow * Map.CellHeight + (Map.CellHeight / 2)
' Calculate angle from player's center to target block's center
angleToBlock = _ATAN2(targetBlockPixelY - playerPixelY, targetBlockPixelX - playerPixelX)
' Normalize angleToBlock to be relative to playerRot's 0 reference
' This makes the comparison simpler: is angleToBlock within (playerRot - halfFOV) and (playerRot + halfFOV)?
relativeAngle = angleToBlock - playerRot
' Normalize relativeAngle to be within -PI to +PI
DO WHILE relativeAngle <= -_PI
relativeAngle = relativeAngle + 2 * _PI
LOOP
DO WHILE relativeAngle > _PI
relativeAngle = relativeAngle - 2 * _PI
LOOP
IF ABS(relativeAngle) <= halfFOV THEN ' If within FOV angle
' 4. Basic Line-of-Sight Check (Raycast) - From player center to target block center
distanceToTarget = GDK_Distance!(playerPixelX, playerPixelY, targetBlockPixelX, targetBlockPixelY)
stepCount = INT(distanceToTarget / Map.CellWidth) + 1 ' Estimate steps, at least 1
pathBlocked = GDK_FALSE
FOR s = 1 TO stepCount ' Corrected: Changed 'step' to 's'
' Interpolate point along the line from player to target
currentPathX = playerPixelX + (targetBlockPixelX - playerPixelX) * (s / stepCount) ' Use 's' here
currentPathY = playerPixelY + (targetBlockPixelY - playerPixelY) * (s / stepCount) ' Use 's' here
' Convert interpolated pixel point to block coordinates
pathCol = INT((currentPathX - Map.OffsetX) / Map.CellWidth)
pathRow = INT((currentPathY - Map.OffsetY) / Map.CellHeight)
' Check if this path block is within bounds and is a wall
IF pathCol >= 0 AND pathCol < Map.Columns AND pathRow >= 0 AND pathRow < Map.Rows THEN
IF Map_Block(pathCol, pathRow) = MAP_BLOCK_WALL THEN
pathBlocked = GDK_TRUE
EXIT FOR ' Path is blocked, no need to check further along this line
END IF
END IF
NEXT s ' Use 's' here
' 5. If not blocked AND it's an unknown block, mark it as known
IF pathBlocked = GDK_FALSE THEN
IF Team_Map(TeamIndex, scanCol, scanRow) = MAP_BLOCK_HIDDEN THEN
Team_Map(TeamIndex, scanCol, scanRow) = MAP_BLOCK_KNOWN
END IF
END IF
END IF ' End FOV angle check
END IF ' End Player's own tile check
END IF ' End Boundary Check
NEXT yOffset
NEXT xOffset
END SUB
SUB Team_Check_Spawn (TeamIndex AS INTEGER)
DIM MaxPlayers AS INTEGER, CP_Spawn AS INTEGER, CpCnt AS INTEGER, i AS INTEGER, j AS INTEGER
DIM currentCPTeamVal AS _BYTE
IF Team(TeamIndex).Checkpoints > 3 THEN MaxPlayers = Team(TeamIndex).Checkpoints ELSE MaxPlayers = 3
IF MaxPlayers > 11 THEN MaxPlayers = 11
IF Team(TeamIndex).NumActivePlayers < MaxPlayers AND Team(TeamIndex).Reserves > 0 THEN
CP_Spawn = INT(RND * Team(TeamIndex).Checkpoints)
FOR i = 0 TO Map.Num_CP - 1
currentCPTeamVal = Map_CP(i).Team ' Value is 0, 1, or 2
IF currentCPTeamVal = (TeamIndex + 1) THEN
IF CpCnt = CP_Spawn THEN
FOR j = 0 TO 10
IF Player(TeamIndex, j).IsAlive = 0 THEN
CALL SpawnPlayer(TeamIndex, j, Map_CP(i))
Player(TeamIndex, j).X = Map.OffsetX + Map_CP(i).Col * Map.CellWidth + (Map.CellWidth / 2)
Player(TeamIndex, j).Y = Map.OffsetY + Map_CP(i).Row * Map.CellHeight + (Map.CellHeight / 2)
Player(TeamIndex, j).FOV_Angle = _PI / 4
Team(TeamIndex).Reserves = Team(TeamIndex).Reserves - 1
EXIT FOR ' Exit player slot loop
END IF
NEXT
EXIT FOR ' Exit checkpoint loop
END IF
CpCnt = CpCnt + 1
END IF
NEXT
ELSE
END IF
END SUB
SUB Draw_Players (TeamIndex AS INTEGER)
DIM playerIdx AS INTEGER
DIM p AS Player ' Local Player variable to hold data for current player
DIM screenX AS INTEGER
DIM screenY AS INTEGER
DIM playerRadius AS INTEGER
DIM playerColor AS _UNSIGNED LONG
' Determine player color based on the team
SELECT CASE TeamIndex
CASE 0 ' 0 for Red
playerColor = _RGB32(255, 0, 0) ' Red
CASE 1 'TEAM_BLUE_INDEX ' 1 for Blue
playerColor = _RGB32(0, 0, 255) ' Blue
CASE ELSE
playerColor = _RGB32(128, 128, 128) ' Grey for unknown team
END SELECT
playerRadius = Map.CellWidth \ 3 ' A reasonable radius for the player circle
' Loop through all possible player slots for this team
FOR playerIdx = 0 TO 10 ' Assuming Player array is indexed 0 to 10
p = Player(TeamIndex, playerIdx) ' Get player data
IF p.IsAlive = 1 THEN ' Only draw players that are currently active
' Use player's direct pixel coordinates (p.X, p.Y) for drawing
screenX = p.X '+ (Map.CellWidth / 2) ' p.X is already the center pixel X
screenY = p.Y '+ (Map.CellHeight / 2) ' p.Y is already the center pixel Y
' Draw the player's body (circle)
CIRCLE (screenX, screenY), playerRadius, playerColor
PAINT (screenX, screenY), playerColor, playerColor
' Draw the player's facing direction line
' Use p.X, p.Y and p.Rotation directly (already in radians)
LINE (screenX, screenY)-(screenX + COS(p.Rotation) * (playerRadius + 3), screenY + SIN(p.Rotation) * (playerRadius + 3)), _RGB32(255, 255, 255)
END IF
NEXT playerIdx
END SUB
FUNCTION IsPlayerOnMap (p AS Player)
' Check if the player's X coordinate is within map bounds
IF p.X >= Map.OffsetX AND p.X < Map.OffsetX + ((Map.Columns - 1) * Map.CellWidth) THEN
' If X is within bounds, check if Y coordinate is within map bounds
IF p.Y >= Map.OffsetY AND p.Y < Map.OffsetY + ((Map.Rows - 1) * Map.CellHeight) THEN
IsPlayerOnMap = GDK_TRUE ' Both X and Y are within bounds
ELSE
IsPlayerOnMap = GDK_FALSE ' Y is out of bounds
END IF
ELSE
IsPlayerOnMap = GDK_FALSE ' X is out of bounds
END IF
END FUNCTION
' --- SUB Implementation ---
SUB SpawnPlayer (TeamIndex AS INTEGER, PlayerIndex AS INTEGER, CP AS Checkpoint)
DIM p AS Player ' Local copy of player data
DIM angleIncrement AS SINGLE
angleIncrement = _PI / 4 ' 45 degrees in radians
p = Player(TeamIndex, PlayerIndex) ' Get current player data from array
' Initialize/Revive the player
p.IsAlive = 1
p.Action = PLAYER_ACTION_IDLE ' Start in idle state
' Set player's Col/Row from the Checkpoint
p.Col = CP.Col
p.Row = CP.Row
' Set player's pixel position based on checkpoint's Col/Row (using Map.OffsetX/Y)
p.X = Map.OffsetX + CP.Col * Map.CellWidth + (Map.CellWidth / 2)
p.Y = Map.OffsetY + CP.Row * Map.CellHeight + (Map.CellHeight / 2)
' Handle rotation increment and assignment
IF CP.Rot < 2 * _PI THEN ' 2 * _PI is 360 degrees in radians
CP.Rot = CP.Rot + angleIncrement
ELSE
CP.Rot = 0 ' Reset to 0 after full circle
END IF
p.Rotation = CP.Rot ' Assign this new rotation to the spawned player
p.Range = 3 ' Example range
p.CanShoot = 1 ' Can shoot immediately after spawning
' Reset claim timers
p.ClaimTime = 0
p.ClaimTimer = 0
p.Target_Col = p.Col ' Default target is self
p.Target_Row = p.Row
' Update team's active player count
Team(TeamIndex).NumActivePlayers = Team(TeamIndex).NumActivePlayers + 1
Player(TeamIndex, PlayerIndex) = p ' Save changes back to the global array
END SUB
'// Mainly for dev - this shows everything
SUB Draw_Map
DIM j AS INTEGER, i AS INTEGER
DIM currentY AS INTEGER
DIM currentX AS INTEGER
DIM BlockFillColor AS _UNSIGNED LONG
DIM BlockBorderColor AS _UNSIGNED LONG
currentY = Map.OffsetY ' Use calculated offset
FOR j = 0 TO Map.Rows - 1
currentX = Map.OffsetX ' Use calculated offset
FOR i = 0 TO Map.Columns - 1
'// based on ownership draw a rectangle to represent the block
SELECT CASE Map_Block(i, j)
CASE MAP_BLOCK_UNOWNED
BlockFillColor = _RGB32(5, 245, 25) ' green
BlockBorderColor = _RGB32(200, 200, 200) ' Light Grey Border
CASE MAP_BLOCK_RED_EMPTY
BlockFillColor = _RGB32(255, 0, 0) ' Red
BlockBorderColor = _RGB32(150, 0, 0) ' Darker Red Border
CASE MAP_BLOCK_BLUE_EMPTY
BlockFillColor = _RGB32(0, 0, 255) ' Blue
BlockBorderColor = _RGB32(0, 0, 150) ' Darker Blue Border
CASE MAP_BLOCK_UNOWNED_CHECKPOINT
BlockFillColor = _RGB32(150, 150, 150) ' Grey for Unowned Checkpoint
BlockBorderColor = _RGB32(100, 100, 100) ' Dark Grey Border
CASE MAP_BLOCK_RED_CHECKPOINT
BlockFillColor = _RGB32(255, 100, 100) ' Light Red for Red Checkpoint
BlockBorderColor = _RGB32(255, 0, 0) ' Red Border
CASE MAP_BLOCK_BLUE_CHECKPOINT
BlockFillColor = _RGB32(100, 100, 255) ' Light Blue for Blue Checkpoint
BlockBorderColor = _RGB32(0, 0, 255) ' Blue Border
CASE ELSE ' Fallback for unexpected values
BlockFillColor = _RGB32(0, 0, 0) ' Black
BlockBorderColor = _RGB32(255, 255, 0) ' Yellow for error visualization
END SELECT
LINE (currentX, currentY)-(currentX + Map.CellWidth, currentY + Map.CellHeight), BlockFillColor, BF
'LINE (currentX, currentY)-(currentX + Map.CellWidth, currentY + Map.CellHeight), BlockBorderColor, B
currentX = currentX + Map.CellWidth
NEXT
currentY = currentY + Map.CellHeight
NEXT
END SUB
SUB Draw_Team_Map (TeamIndex%)
DIM j AS INTEGER, i AS INTEGER
DIM currentY AS INTEGER
DIM currentX AS INTEGER
DIM BlockFillColor AS _UNSIGNED LONG
DIM BlockBorderColor AS _UNSIGNED LONG
currentY = Map.OffsetY ' Use calculated offset
FOR j = 0 TO Map.Rows - 1
currentX = Map.OffsetX ' Use calculated offset
FOR i = 0 TO Map.Columns - 1
'// based on ownership draw a rectangle to represent the block
SELECT CASE Team_Map(TeamIndex%, i, j)
CASE MAP_BLOCK_HIDDEN
BlockFillColor = _RGBA32(0, 0, 0, 100) ' black
BlockBorderColor = _RGB32(20, 20, 20) ' Light Grey Border
CASE MAP_BLOCK_KNOWN
IF TeamIndex% = 0 THEN '// red
BlockFillColor = _RGBA32(255, 0, 0, 200) ' Red - slightly transparent
BlockBorderColor = _RGB32(150, 0, 0) ' Darker Red Border
ELSE '// blue
BlockFillColor = _RGBA32(0, 0, 120, 200) ' blue - slightly transparent
BlockBorderColor = _RGB32(0, 0, 150) ' Darker Border
END IF
END SELECT
LINE (currentX, currentY)-(currentX + Map.CellWidth, currentY + Map.CellHeight), BlockFillColor, BF
LINE (currentX, currentY)-(currentX + Map.CellWidth, currentY + Map.CellHeight), BlockBorderColor, B
currentX = currentX + Map.CellWidth
NEXT
currentY = currentY + Map.CellHeight
NEXT
END SUB
SUB SetProtectedZone (CPCol AS INTEGER, CPRow AS INTEGER, OwnerTeamBlockValue AS _BYTE)
DIM k AS INTEGER, l AS INTEGER
DIM teamMapRadius AS INTEGER ' Define a separate radius for Team_Map visibility
' --- Protected Area (Map_Block) radius ---
FOR l = -3 TO 3 ' Loop for rows (Y) within the protected radius
FOR k = -3 TO 3 ' Loop for columns (X) within the protected radius
' Ensure coordinates are within map bounds
IF CPCol + k >= 0 AND CPCol + k < Map.Columns AND _
CPRow + l >= 0 AND CPRow + l < Map.Rows THEN
Map_Block(CPCol + k, CPRow + l) = OwnerTeamBlockValue
END IF
NEXT k
NEXT l
' --- Team_Map visibility radius (larger) ---
teamMapRadius = 5 ' Set the desired larger radius for Team_Map
FOR l = -teamMapRadius TO teamMapRadius ' Loop for rows (Y) within the Team_Map radius
FOR k = -teamMapRadius TO teamMapRadius ' Loop for columns (X) within the Team_Map radius
' Ensure coordinates are within map bounds
IF CPCol + k >= 0 AND CPCol + k < Map.Columns AND _
CPRow + l >= 0 AND CPRow + l < Map.Rows THEN
IF OwnerTeamBlockValue > 0 THEN
Team_Map(OwnerTeamBlockValue - 1, CPCol + k, CPRow + l) = MAP_BLOCK_KNOWN
END IF
END IF
NEXT k
NEXT l
END SUB
'// Before calling this RESTORE to your choosen level data - for now we only have 1
SUB Load_Level
'//load map data from data block
READ Map.Columns, Map.Rows, Map.CellWidth, Map.CellHeight, Map.Num_CP
'// xy offset for grid display
Map.OffsetX = (_WIDTH(0) - (Map.Columns * Map.CellWidth)) / 2
Map.OffsetY = (_HEIGHT(0) - (Map.Rows * Map.CellHeight)) / 2
'// Read checkpoint data
FOR CP% = 0 TO Map.Num_CP - 1
READ Map_CP(CP%).Col, Map_CP(CP%).Row, Map_CP(CP%).Team
Map_Block(Map_CP(CP%).Col, Map_CP(CP%).Row) = Map_CP(CP%).Team + 3
'// apply the checkpoint to the map and team map
SetProtectedZone Map_CP(CP%).Col, Map_CP(CP%).Row, Map_CP(CP%).Team
IF Map_CP(CP%).Team > 0 THEN Team(Map_CP(CP%).Team - 1).Checkpoints = Team(Map_CP(CP%).Team - 1).Checkpoints + 1 '// Increment checkpoint count for the related player
NEXT
READ Reserves%
Team(0).Reserves = Reserves%
Team(1).Reserves = Reserves%
END SUB
SUB Update_Delta_Time
deltaTime = GDK_System.GT - GDK_System.Last_GT
END SUB

