Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented "level terrain" feature, fix #28 #1017

Merged
merged 3 commits into from
Aug 21, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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 @@ -4,6 +4,7 @@
// #include "WindowManager.hxx"
// #include "basics/Point.hxx"
#include "../Map.hxx"
#include "audio/AudioConfig.hxx"
// #include "../../util/Singleton.hxx"
#include <Singleton.hxx>
#include <Point.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 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