Skip to content

Commit

Permalink
Implemented "level terrain" feature, fix #28 (#1017)
Browse files Browse the repository at this point in the history
* Implemented "level terrain" feature, fix #28

* Removed unused import from MapFunctions header.

* Fixed include and usage of std::vector in MapFunctions.

Co-authored-by: Thelie <[email protected]>
  • Loading branch information
DanielMowitz and Thelie authored Aug 21, 2022
1 parent 1c1ec60 commit cb327cd
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 85 deletions.
39 changes: 22 additions & 17 deletions src/engine/EventManager.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,21 @@ void EventManager::pickTileUnderCursor(Point mouseIsoCoords)
// update placement mode
switch (topMostActiveLayer)
{
case Layer::BUILDINGS:
GameStates::instance().placementMode = PlacementMode::SINGLE;
break;
case Layer::ROAD:
case Layer::POWERLINES:
case Layer::UNDERGROUND:
GameStates::instance().placementMode = PlacementMode::LINE;
break;
case Layer::GROUND_DECORATION:
case Layer::WATER:
case Layer::ZONE:
GameStates::instance().placementMode = PlacementMode::RECTANGLE;
break;
default:
break;
case Layer::BUILDINGS:
GameStates::instance().placementMode = PlacementMode::SINGLE;
break;
case Layer::ROAD:
case Layer::POWERLINES:
case Layer::UNDERGROUND:
GameStates::instance().placementMode = PlacementMode::LINE;
break;
case Layer::GROUND_DECORATION:
case Layer::WATER:
case Layer::ZONE:
GameStates::instance().placementMode = PlacementMode::RECTANGLE;
break;
default:
break;
}
mapNodeData = node.getMapNodeData();
tileToPlace = mapNodeData[topMostActiveLayer].tileID;
Expand Down Expand Up @@ -408,7 +408,7 @@ void EventManager::checkEvents(SDL_Event &event)
break;
case SDL_MOUSEBUTTONDOWN:
m_placementAllowed = false;

if (event.button.button == SDL_BUTTON_RIGHT)
{
m_panning = true;
Expand Down Expand Up @@ -488,6 +488,10 @@ void EventManager::checkEvents(SDL_Event &event)
{
MapFunctions::instance().changeHeight(mouseIsoCoords, false);
}
else if (terrainEditMode == TerrainEdit::LEVEL)
{
MapFunctions::instance().levelHeight(m_clickDownCoords, m_nodesToPlace);
}
else if (demolishMode)
{
MapFunctions::instance().demolishNode(m_nodesToHighlight, true);
Expand Down Expand Up @@ -518,7 +522,8 @@ void EventManager::checkEvents(SDL_Event &event)
if (highlightSelection)
{
m_nodesToHighlight.push_back(mouseIsoCoords);
if (!uiManager.isMouseHovered() && !tileToPlace.empty() && !MapFunctions::instance().setTileID(tileToPlace, mouseIsoCoords))
if (!uiManager.isMouseHovered() && !tileToPlace.empty() &&
!MapFunctions::instance().setTileID(tileToPlace, mouseIsoCoords))
{
MapFunctions::instance().highlightNode(mouseIsoCoords, SpriteHighlightColor::RED);
}
Expand Down
76 changes: 72 additions & 4 deletions src/engine/map/MapFunctions.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
#include <set>
#include <queue>

MapFunctions::MapFunctions()
{
SignalMediator::instance().registerCbSaveGame(Signal::slot(this, &MapFunctions::saveMapToFile));
}
MapFunctions::MapFunctions() { SignalMediator::instance().registerCbSaveGame(Signal::slot(this, &MapFunctions::saveMapToFile)); }

bool MapFunctions::updateHeight(Point coordinate, const bool elevate)
{
Expand Down Expand Up @@ -63,6 +60,77 @@ void MapFunctions::changeHeight(const Point &isoCoordinates, const bool elevate)
}
}

void MapFunctions::levelHeight(const Point &startCoordinate, const std::vector<Point> levelArea)
{
const MapNode &startNode = getMapNode(startCoordinate);
int initialHeight = startCoordinate.height;

/* If the initial node is sloped, check if a majority of it's neighbors,
* not counting other sloped nodes are elevated.
* If so, the initial node should be regarded as elevated too,
* i.e. the height should be incremented.
* This seems to yield the most intuitive behavior on slopes.
*/
if (startNode.isSlopeNode())
{
char directNeighbors =
NeighborNodesPosition::LEFT | NeighborNodesPosition::TOP | NeighborNodesPosition::RIGHT | NeighborNodesPosition::BOTTOM;
char elBitmask = startNode.getElevationBitmask();

int nonSelectedNeighborsCount = 0;
int elevatedNonSelectedNeighborsCount = 0;

for (Point neighbor : PointFunctions::getNeighbors(startCoordinate, false))
{
const NeighborNodesPosition neighborPosToOrigin = PointFunctions::getNeighborPositionToOrigin(neighbor, startCoordinate);
const bool isSloped = getMapNode(neighbor).isSlopeNode();

// Only look at non-sloped direct neighbors not in the selection.
if (!isSloped && (neighborPosToOrigin & directNeighbors) > 0 &&
std::find(levelArea.begin(), levelArea.end(), neighbor) == levelArea.end())
{
nonSelectedNeighborsCount++;

if ((neighborPosToOrigin & elBitmask) > 0)
elevatedNonSelectedNeighborsCount++;
}
}

if (elevatedNonSelectedNeighborsCount * 2 > nonSelectedNeighborsCount)
// No need to check max height since there are elevated neighbors.
initialHeight++;
}

Vector<Point> neighborsToLower;

for (const Point &levelPoint : levelArea)
{
// If a node gets lowered, save all it's neighbors to be lowered aswell.
// We have to get the node first, since the coordinates in the area are generated with height=0
if (getMapNode(levelPoint).getCoordinates().height > initialHeight)
{
// This possibly stores nodes that have already been processed for another round.
// It's faster than checking if each node is in levelArea first though.
Vector<Point> neighbors = PointFunctions::getNeighbors(levelPoint, false);
neighborsToLower.insert(neighborsToLower.end(), neighbors.begin(), neighbors.end());
}

Point newCoordinates = Point(levelPoint.x, levelPoint.y, levelPoint.z, initialHeight);
MapNode &levelNode = getMapNode(levelPoint);
levelNode.setCoordinates(newCoordinates);
}

for (const Point &levelPoint : neighborsToLower)
{
Point newCoordinates = Point(levelPoint.x, levelPoint.y, levelPoint.z, initialHeight);
MapNode &levelNode = getMapNode(levelPoint);
levelNode.setCoordinates(newCoordinates);
}

updateNodeNeighbors(levelArea);
updateNodeNeighbors(neighborsToLower);
}

void MapFunctions::updateNodeNeighbors(const std::vector<Point> &nodes)
{
// those bitmask combinations require the tile to be elevated.
Expand Down
9 changes: 8 additions & 1 deletion src/engine/map/MapFunctions.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

// #include "WindowManager.hxx"
// #include "basics/Point.hxx"
#include <vector>
#include "../Map.hxx"
// #include "../../util/Singleton.hxx"
#include <Singleton.hxx>
Expand All @@ -27,6 +28,12 @@ public:
*/
bool updateHeight(Point coordinate, const bool elevate);

/** \brief level area of map nodes.
* @param startcoordinate the starting point whose height is used for levelling
* @param levelArea the area that is to be leveled.
*/
void levelHeight(const Point &startCoordinate, const std::vector<Point> levelArea);

/** \brief Get pointer to a single mapNode at specific iso coordinates.
* @param isoCoords The node to retrieve.
*/
Expand Down Expand Up @@ -197,4 +204,4 @@ private:
std::vector<uint8_t> calculateAutotileBitmask(Point coordinate);
};

#endif
#endif
Loading

0 comments on commit cb327cd

Please sign in to comment.