//---------------------------------------------------------------------- bool ArenaMap::searchHalfCircleUsingManhattenDistForNutrient( const TileCoords& pos, const TileCoords& dir, int radius, TileCoords& nutrientLoc ) const { TileCoords currentPos = pos; int currRadius = radius; bool found = false; for( int i = 0; i < radius && !found; ++i ) { found = searchInDirectionForNutrient( currentPos, TileCoords( 1, 0 ), currRadius, nutrientLoc ); if( !found ) found = searchInDirectionForNutrient( currentPos, TileCoords( -1, 0 ), currRadius, nutrientLoc ); currentPos += dir; --currRadius; } return found; }
//---------------------------------------------------------------------- bool AIController::getEntityIDForClosestEnemyQueen( const TileCoords& pos, const ObservedEntities& entities, int& closestQueenID ) const { int visibleQueenIndices[8] = { -1, -1, -1, -1, -1, -1 }; int queenIndex = 0; for( int i = 0; i < entities.m_numberOfObservedEntities; ++i ) { if( entities.m_entities[i].m_agentType == ENTITY_TYPE_QUEEN && queenIndex < 8 ) visibleQueenIndices[ queenIndex++ ] = i; } TileCoords closestQueenTC( -1, -1 ); float closestDeltaSqrd = (float)m_arenaInfo.width; int closestQueenIndex = -1; for( int q = 0; q < queenIndex; ++q ) { if( visibleQueenIndices[q] != -1 ) { TileCoords nextQueenLoc = TileCoords( entities.m_entities[ visibleQueenIndices[q] ].m_positionX, entities.m_entities[ visibleQueenIndices[q] ].m_positionY ); float deltaSqrd = ( nextQueenLoc - pos ).lengthSqrd(); if( deltaSqrd < closestDeltaSqrd ) { closestQueenTC = nextQueenLoc; closestDeltaSqrd = deltaSqrd; closestQueenIndex = visibleQueenIndices[q]; } } } if( closestQueenIndex != -1 ) closestQueenID = entities.m_entities[ closestQueenIndex ].m_entityID; else closestQueenID = -1; return closestQueenIndex != -1; }
TheGame::TheGame() : m_map(nullptr) , m_isPaused(false) , m_pauseTexture(Texture::CreateOrGetTexture("Data/Images/Pause.png")) { srand(0xDEADBEEF); m_map = new Map( TileCoords( 40, 40 ) ); }
//---------------------------------------------------------------------- TileCoords AIController::getLocationForQueenToGoTo( const ArenaMap& currentMap ) { static OrderCode orderCodeToStartWith = ORDER_MOVE_NORTH; TileCoords newQueenLoc = m_queen->pos; if( m_currentNumWorkers <= 0 ) { OrderCode dirCode = orderCodeToStartWith; float lowestGCost = 100.0f; for( int i = 0; i < 4; ++i ) { dirCode = (OrderCode)Monky::MathFuncs<int>::wrap( orderCodeToStartWith + i, ORDER_MOVE_EAST, ORDER_MOVE_SOUTH ); TileCoords newDir = AStarPathGenerator::getTileCoordsDirFromOrderCode( dirCode ); TileCoords tempNewQueenLoc; newDir = newDir * (float)m_arenaInfo.visibilityRangeQueen; tempNewQueenLoc = m_queen->pos + newDir; float gCostToEnterTile = currentMap.getGCostToEnterTile( ENTITY_TYPE_QUEEN, tempNewQueenLoc.x, tempNewQueenLoc.y ); if( currentMap.isInMap( tempNewQueenLoc.x, tempNewQueenLoc.y ) && currentMap.canMoveOnTile( tempNewQueenLoc.x, tempNewQueenLoc.y ) && gCostToEnterTile < lowestGCost ) { newQueenLoc = tempNewQueenLoc; lowestGCost = gCostToEnterTile; } } int x, y; if( currentMap.getClosestNutrient( m_arenaInfo.visibilityRangeQueen * QUEEN_RANGE_FACTOR, newQueenLoc, x, y ) ) newQueenLoc = TileCoords( x, y ); orderCodeToStartWith = dirCode; } else { int x, y; if( currentMap.getClosestNutrient( m_arenaInfo.visibilityRangeQueen * QUEEN_RANGE_FACTOR, m_queen->pos, x, y ) ) newQueenLoc = TileCoords( x, y ); } if( newQueenLoc == m_queen->pos ) newQueenLoc = currentMap.getRandomLocationInArea( m_queen->pos, (int)( m_arenaInfo.width*0.1 ) ); return newQueenLoc; }
//------------------------------------------------------------ void ArenaMap::updateEnemySightingsHeatMap( const ObservedEntities& entities ) { for( int i = 0; i < entities.m_numberOfObservedEntities; ++i ) { TileCoords entityLoc = TileCoords( entities.m_entities[i].m_positionX, entities.m_entities[i].m_positionY ); MapTile& tile = getMapTile( entityLoc.x, entityLoc.y ); ++tile.enemyAgentsSeenInThisTile; if( tile.enemyAgentsSeenInThisTile > m_maxEnemySeenCountInSingleTile ) m_maxEnemySeenCountInSingleTile = tile.enemyAgentsSeenInThisTile; } }
//---------------------------------------------------------------------- TileCoords ArenaMap::getRandomLocationInArea( const TileCoords& pos, int radius ) const { TileCoords minXY( pos.x - radius, pos.y - radius ); TileCoords maxXY( pos.x + radius, pos.y + radius ); minXY.x = Monky::MathFuncs<int>::clamp( minXY.x, 0, m_arenaInfo.width - 1 ); minXY.y = Monky::MathFuncs<int>::clamp( minXY.y, 0, m_arenaInfo.height - 1 ); maxXY.x = Monky::MathFuncs<int>::clamp( maxXY.x, 0, m_arenaInfo.width - 1 ); maxXY.y = Monky::MathFuncs<int>::clamp( maxXY.y, 0, m_arenaInfo.height - 1 ); return TileCoords( Monky::RandNumGen::randInRangeInt( minXY.x, maxXY.x ), Monky::RandNumGen::randInRangeInt( minXY.y, maxXY.y ) ); }
//------------------------------------------------------------ bool ArenaMap::getClosestNutrient( int startingRadius, const TileCoords& current, int& nutrientX, int& nutrientY ) const { bool found = false; TileCoords nutrientLoc( -1, -1 ); found = searchInDirectionForNutrient( current, TileCoords( 1, 0 ), startingRadius, nutrientLoc ); if( !found ) found = searchInDirectionForNutrient( current, TileCoords( -1, 0 ), startingRadius, nutrientLoc ); TileCoords hCircleDir = TileCoords( 0, 1 ); --startingRadius; if( !found ) found = searchHalfCircleUsingManhattenDistForNutrient( current + hCircleDir, hCircleDir, startingRadius, nutrientLoc ); if( !found ) found = searchHalfCircleUsingManhattenDistForNutrient( current - hCircleDir, -hCircleDir, startingRadius, nutrientLoc ); nutrientX = nutrientLoc.x; nutrientY = nutrientLoc.y; return found; }
//---------------------------------------------------------------------- TileCoords AIController::getTileCoordsForEnemyAgent( int agentID, const ObservedEntities& entities ) const { TileCoords agentLoc( -1, -1 ); for( int i = 0; i < entities.m_numberOfObservedEntities; ++i ) { if( entities.m_entities[i].m_entityID == agentID ) { agentLoc = TileCoords( entities.m_entities[i].m_positionX, entities.m_entities[i].m_positionY ); break; } } return agentLoc; }
//------------------------------------------------------------ void ArenaMap::init() { m_currentTurn = 0; for( int y = 0; y < m_height; ++y ) { for( int x = 0; x < m_width; ++x ) { int index = x + ( y * m_width ); if( index >= 0 && index < ( m_width * m_height ) ) { m_mapTiles[ index ].tileMaterial = (char)ARENA_SQUARE_TYPE_UNKNOWN; m_tilesToScout.addItem( TileCoords( x, y ) ); } } } }
//------------------------------------------------------------ void ArenaMap::updateMap( const ArenaSquares& currentViewOfArena, int currentTurn ) { m_width = currentViewOfArena.m_width; m_height = currentViewOfArena.m_height; m_hasNewMapInfo = false; m_hasNoMoreResources = true; m_currentTurn = currentTurn; m_tilesToScout.clearPool(); for( int y = 0; y < m_height; ++y ) { for( int x = 0; x < m_width; ++x ) { int index = x + ( y * m_width ); if( index >= 0 && index < ( m_width * m_height ) ) { if( currentViewOfArena.m_arenaSquareMaterials[ index ] != ARENA_SQUARE_TYPE_UNKNOWN ) { if( m_mapTiles[ index ].tileMaterial != (char)ARENA_SQUARE_TYPE_STONE ) { if( currentViewOfArena.m_arenaSquareMaterials[ index ] == ARENA_SQUARE_TYPE_STONE ) m_hasNewMapInfo = true; if( m_mapTiles[ index ].tileMaterial == ARENA_SQUARE_TYPE_FOOD || currentViewOfArena.m_arenaSquareMaterials[ index ] == ARENA_SQUARE_TYPE_FOOD ) m_hasNoMoreResources = false; m_mapTiles[ index ].isClaimed = false; m_mapTiles[ index ].tileMaterial = (char)currentViewOfArena.m_arenaSquareMaterials[ index ]; m_mapTiles[ index ].gCostToEnterTileForQueen = getGCostToEnterTileForEntityType( (ArenaSquareType)m_mapTiles[ index ].tileMaterial, ENTITY_TYPE_QUEEN ); m_mapTiles[ index ].gCostToEnterTileForScout = getGCostToEnterTileForEntityType( (ArenaSquareType)m_mapTiles[ index ].tileMaterial, ENTITY_TYPE_SCOUT ); m_mapTiles[ index ].gCostToEnterTileForSoldier = getGCostToEnterTileForEntityType( (ArenaSquareType)m_mapTiles[ index ].tileMaterial, ENTITY_TYPE_SOLDIER ); m_mapTiles[ index ].gCostToEnterTileForWorker = getGCostToEnterTileForEntityType( (ArenaSquareType)m_mapTiles[ index ].tileMaterial, ENTITY_TYPE_WORKER ); } m_mapTiles[ index ].lastTurnUpdated = m_currentTurn; } else if( m_currentTurn - m_mapTiles[ index ].lastTurnUpdated > TOLERANCE_FOR_TILE_FRESHNESS ) m_tilesToScout.addItem( TileCoords( x, y ) ); } } } }
//---------------------------------------------------------------------- void AIController::handleWorkerOrders( Agent* worker, ArenaMap& currentMap, const ObservedEntities& entities, Orders& tentativeOrders ) { if( m_currentQueenState == ATTACK && currentMap.hasNoMoreResources() && worker->specialStatus != ENTITY_SPECIAL_STATUS_CARRYING_FOOD ) { tentativeOrders.AddOrder( worker->entityID, ORDER_SUICIDE ); } else { if( worker->currentPath.empty() && !worker->waitingForPath ) { if( worker->pos == m_queen->pos && worker->specialStatus == ENTITY_SPECIAL_STATUS_CARRYING_FOOD ) { tentativeOrders.AddOrder( worker->entityID, ORDER_DROP_FOOD, true ); --m_numWorkersWithFood; worker->currentTargetAgentID = -1; } else if( worker->pos == worker->currentTargetLoc && currentMap.getMapTileType( worker->pos.x, worker->pos.y ) == ARENA_SQUARE_TYPE_FOOD ) { tentativeOrders.AddOrder( worker->entityID, ORDER_TAKE_FOOD, true ); m_agentsRequestingPaths.push( worker->entityID ); worker->currentTargetLoc = m_queen->pos; worker->currentTargetAgentID = m_queen->entityID; worker->waitingForPath = true; currentMap.getMapTile( worker->pos.x, worker->pos.y ).isClaimed = false; } else if( worker->specialStatus != ENTITY_SPECIAL_STATUS_CARRYING_FOOD ) { int x, y; if( currentMap.getClosestNutrient( m_arenaInfo.visibilityRangeQueen*QUEEN_RANGE_FACTOR, m_queen->pos, x, y ) ) { m_agentsRequestingPaths.push( worker->entityID ); worker->currentTargetLoc = TileCoords( x, y ); worker->currentTargetAgentID = -1; worker->waitingForPath = true; currentMap.getMapTile( x, y ).isClaimed = true; } else { ++m_numWorkersThatCouldntFindFoodThisFrame; if( m_numWorkersThatCouldntFindFoodThisFrame >= NUM_WORKERS_IDLE_BEFORE_QUEEN_MOVES ) { worker->currentTargetAgentID = m_queen->entityID; } else { worker->currentTargetAgentID = -1; worker->currentTargetLoc = currentMap.getRandomLocationInArea( worker->pos, m_arenaInfo.visibilityRangeQueen*QUEEN_RANGE_FACTOR ); } m_agentsRequestingPaths.push( worker->entityID ); worker->waitingForPath = true; } } else { worker->currentTargetAgentID = m_queen->entityID; m_agentsRequestingPaths.push( worker->entityID ); worker->waitingForPath = true; } } else if( currentMap.getMapTileType( worker->currentTargetLoc.x, worker->currentTargetLoc.y ) != ARENA_SQUARE_TYPE_FOOD && worker->specialStatus != ENTITY_SPECIAL_STATUS_CARRYING_FOOD ) { int x, y; if( currentMap.getClosestNutrient( m_arenaInfo.visibilityRangeQueen*QUEEN_RANGE_FACTOR, m_queen->pos, x, y ) ) { m_agentsRequestingPaths.push( worker->entityID ); worker->currentTargetLoc = TileCoords( x, y ); worker->currentTargetAgentID = -1; worker->waitingForPath = true; currentMap.getMapTile( x, y ).isClaimed = true; } } else if( currentMap.hasNewMapInfo() && !worker->waitingForPath && !validPath( currentMap, worker->pos, worker->currentPath ) ) { m_agentsRequestingPaths.push( worker->entityID ); worker->waitingForPath = true; } } }