Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
NPC AI dev - Dominion Protocol
#1
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

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




Attached Files
.zip   UnseenGDK_Dev.zip (Size: 18.24 KB / Downloads: 53)
Reply
#2
Slightly tweaked the code for a better show...it's still random but im tired and bed!
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%))
          ELSE
            IF INT(RND * 60) + 1 = 40 THEN '// roll dice for change of direction
              Player(TeamIndex%, i%).Rotation = Player(TeamIndex%, i%).Rotation + (_PI / 4)

            END IF
          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 \ 2 ' 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
            IF Map_Block(i, j) = 2 OR Map_Block(i, j) = 5 THEN '// enemy owned but we can see it
              BlockFillColor = _RGBA32(0, 0, 120, 200) ' blue - slightly transparent
              BlockBorderColor = _RGB32(0, 0, 150) ' Darker  Border
            ELSE
              BlockFillColor = _RGBA32(255, 0, 0, 200) ' Red - slightly transparent
              BlockBorderColor = _RGB32(150, 0, 0) ' Darker Red Border
            END IF

          ELSE '// blue

            IF Map_Block(i, j) = 1 OR Map_Block(i, j) = 4 THEN '// enemy owned but we can see it
              BlockFillColor = _RGBA32(255, 0, 0, 200) ' Red - slightly transparent
              BlockBorderColor = _RGB32(150, 0, 0) ' Darker Red Border
            ELSE
              BlockFillColor = _RGBA32(0, 0, 120, 200) ' blue - slightly transparent
              BlockBorderColor = _RGB32(0, 0, 150) ' Darker  Border
            END IF
          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


John
Reply
#3
Hi John

better using your Input routine vs Inkey$ keyword

Code: (Select All)


Loop Until (GDK_KB(0).ESC AND NOT GDK_KB(1).ESC)

Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)