Unit* PlaceToolInputState::CreateUnitAtScreenCoords( const Vec2f& screenCoords ) { Unit* unit = nullptr; if( mSelectedUnitType ) { if( mSelectedFaction ) { EditorState* owner = GetOwnerDerived(); MapView* mapView = owner->GetMapView(); Map* map = owner->GetMap(); // Get the Tile at the screen coords. Vec2f worldPos = mapView->ScreenToWorldCoords( screenCoords ); Vec2s tilePos = mapView->WorldToTileCoords( worldPos ); Map::Iterator tile = map->GetTile( tilePos ); if( tile.IsValid() && tile->IsEmpty() && mSelectedUnitType->CanMoveAcrossTerrain( tile->GetTerrainType() ) ) { // If the selected UnitType can be placed into the Tile, create a new Unit. DebugPrintf( "Placing Unit at tile (%d,%d)!", tilePos.x, tilePos.y ); unit = map->CreateUnit( mSelectedUnitType, mSelectedFaction, tilePos ); } else { WarnFail( "Cannot place Unit into tile (%d,%d)!", tilePos.x, tilePos.y ); } } else { WarnFail( "Cannot create Unit because no Faction was selected!" ); } } else { WarnFail( "Cannot create Unit because no UnitType was selected!" ); } return unit; }
void UnitAttackAbility::DetermineAvailableActions( const Unit* unit, const Path& movementPath, Actions& result ) const { // Get the UnitType of the Unit. UnitType* unitType = unit->GetUnitType(); if( unitType->HasWeapons() ) { // If this Unit has weapons, determine whether the Unit can wait in the destination square. Map* map = unit->GetMap(); Map::Iterator destinationTile = map->GetTile( movementPath.GetDestination() ); if( unit->CanOccupyTile( destinationTile ) ) { // If the Unit can stop here, get the list of Units in attack range from this location. Map::Units unitsInRange; map->FindUnitsInRange( destinationTile.GetPosition(), unitType->GetAttackRange(), unitsInRange ); for( auto it = unitsInRange.begin(); it != unitsInRange.end(); ++it ) { Unit* target = *it; if( unit->CanAttack( target ) ) { // Add an attack action to the list of actions. UnitAttackAbility::Action* attackAction = new UnitAttackAbility::Action(); attackAction->UnitID = unit->GetID(); attackAction->TargetID = target->GetID(); attackAction->MovementPath = movementPath; result.push_back( attackAction ); } } } } }
void Map::FindBestPathToTile( const Unit* unit, const Vec2s& tilePos, Path& result ) { std::vector< PrimaryDirection > reverseDirections; int searchIndex = ReserveSearchIndex(); // Clear the list of results. result.Clear(); result.SetOrigin( unit->GetTilePos() ); // Clear the open list. mOpenList.clear(); // Get the Unit's current Tile and type. Iterator originTile = unit->GetTile(); UnitType* unitType = unit->GetUnitType(); MovementType* movementType = unit->GetMovementType(); // Get the starting movement range of the Unit. int movementRange = unit->GetMovementRange(); // Add the origin tile to the open list. originTile->Open( searchIndex ); originTile->SetPreviousTileDirection( PrimaryDirection::NONE ); originTile->SetBestTotalCostToEnter( 0 ); mOpenList.insert( 0, originTile ); while( !mOpenList.isEmpty() ) { // Pop the first element off the open list. Map::Iterator tile = mOpenList.popMinElement(); if( tile.GetPosition() != tilePos ) { // If this isn't the goal tile, close it. tile->Close( searchIndex ); for( int i = 0; i < CARDINAL_DIRECTION_COUNT; ++i ) { // Determine the direction to search. PrimaryDirection direction = CARDINAL_DIRECTIONS[ i ]; // If the adjacent tile isn't in the previous direction, get the adjacent tile. Iterator adjacent = tile.GetAdjacent( direction ); if( adjacent.IsValid() && !adjacent->IsClosed( searchIndex ) ) { // If the adjacent tile is valid and isn't already closed, get the TerrainType of the adjacent tile. TerrainType* adjacentTerrainType = adjacent->GetTerrainType(); if( movementType->CanMoveAcrossTerrain( adjacentTerrainType ) ) { // If the adjacent tile is passable, find the total cost of entering the tile. int costToEnterAdjacent = movementType->GetMovementCostAcrossTerrain( adjacentTerrainType ); int adjacentTotalCost = ( tile->GetBestTotalCostToEnter() + costToEnterAdjacent ); int distanceToGoal = ( originTile.GetPosition().GetManhattanDistanceTo( tilePos ) ); int adjacentWeight = ( adjacentTotalCost + distanceToGoal ); if( adjacentTotalCost <= movementRange ) { if( !adjacent->IsOpen( searchIndex ) ) { // If the tile info isn't already on the open list, add it. adjacent->SetPreviousTileDirection( direction.GetOppositeDirection() ); adjacent->SetBestTotalCostToEnter( adjacentTotalCost ); mOpenList.insert( adjacentWeight, adjacent ); } else if( adjacentTotalCost < adjacent->GetBestTotalCostToEnter() ) { // If the node is already on the open list but has a larger total cost, // update the value. adjacent->SetPreviousTileDirection( direction.GetOppositeDirection() ); adjacent->SetBestTotalCostToEnter( adjacentTotalCost ); mOpenList.update( adjacentWeight, adjacent ); } } } } } } else { // If this is the goal tile, construct the path to the location. PrimaryDirection previousDirection = tile->GetPreviousTileDirection(); while( previousDirection != PrimaryDirection::NONE ) { // Construct the list of directions from the goal back to the origin tile. reverseDirections.push_back( previousDirection ); tile = tile.GetAdjacent( previousDirection ); previousDirection = tile->GetPreviousTileDirection(); } // End the search. break; } } for( auto it = reverseDirections.rbegin(); it != reverseDirections.rend(); ++it ) { // Construct the path by reversing the directions from the goal to the origin. PrimaryDirection direction = *it; result.AddDirection( direction.GetOppositeDirection() ); } }
void Map::FindReachableTiles( const Unit* unit, TileSet& result ) { int searchIndex = ReserveSearchIndex(); // Clear the list of results. result.clear(); // Clear the open list. mOpenList.clear(); // Get the Unit's current Tile and type. Iterator originTile = unit->GetTile(); UnitType* unitType = unit->GetUnitType(); MovementType* movementType = unit->GetMovementType(); // Get the starting movement range of the Unit. int movementRange = unit->GetMovementRange(); // Add the origin tile to the open list. originTile->Open( searchIndex ); originTile->SetPreviousTileDirection( PrimaryDirection::NONE ); originTile->SetBestTotalCostToEnter( 0 ); mOpenList.insert( 0, originTile ); while( !mOpenList.isEmpty() ) { // Pop the first element off the open list. Map::Iterator tile = mOpenList.popMinElement(); // Close the tile and add it to the result. tile->Close( searchIndex ); result.insert( tile ); for( int i = 0; i < CARDINAL_DIRECTION_COUNT; ++i ) { // Determine the direction to search. PrimaryDirection direction = CARDINAL_DIRECTIONS[ i ]; // If the adjacent tile isn't in the previous direction, get the adjacent tile. Iterator adjacent = tile.GetAdjacent( direction ); if( adjacent.IsValid() && !adjacent->IsClosed( searchIndex ) ) { // If the adjacent tile is valid and isn't already closed, get the TerrainType of the adjacent tile. TerrainType* adjacentTerrainType = adjacent->GetTerrainType(); if( movementType->CanMoveAcrossTerrain( adjacentTerrainType ) ) { // If the adjacent tile is passable, find the total cost of entering the tile. int costToEnterAdjacent = movementType->GetMovementCostAcrossTerrain( adjacentTerrainType ); int adjacentTotalCost = ( tile->GetBestTotalCostToEnter() + costToEnterAdjacent ); if( adjacentTotalCost <= movementRange ) { if( !adjacent->IsOpen( searchIndex ) ) { // If the tile info isn't already on the open list, add it. adjacent->SetPreviousTileDirection( direction.GetOppositeDirection() ); adjacent->SetBestTotalCostToEnter( adjacentTotalCost ); mOpenList.insert( adjacentTotalCost, adjacent ); } else if( adjacentTotalCost < adjacent->GetBestTotalCostToEnter() ) { // If the node is already on the open list but has a larger total cost, // update the value. adjacent->SetPreviousTileDirection( direction.GetOppositeDirection() ); adjacent->SetBestTotalCostToEnter( adjacentTotalCost ); mOpenList.update( adjacentTotalCost, adjacent ); } } } } } } }