|
@@ -11,7 +11,7 @@
|
|
|
#include "StdInc.h"
|
|
|
#include "CDrawRoadsOperation.h"
|
|
|
|
|
|
-const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::rules =
|
|
|
+const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::patterns =
|
|
|
{
|
|
|
//single tile. fallback patern
|
|
|
{
|
|
@@ -129,6 +129,21 @@ const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::rules =
|
|
|
|
|
|
};
|
|
|
|
|
|
+static bool ruleIsNone(const std::string & rule)
|
|
|
+{
|
|
|
+ return rule == "-";
|
|
|
+}
|
|
|
+
|
|
|
+static bool ruleIsSomething(const std::string & rule)
|
|
|
+{
|
|
|
+ return rule == "+";
|
|
|
+}
|
|
|
+
|
|
|
+static bool ruleIsAny(const std::string & rule)
|
|
|
+{
|
|
|
+ return rule == "?";
|
|
|
+}
|
|
|
+
|
|
|
///CDrawRoadsOperation
|
|
|
CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, ERoadType::ERoadType roadType, CRandomGenerator * gen):
|
|
|
CMapOperation(map),terrainSel(terrainSel), roadType(roadType), gen(gen)
|
|
@@ -138,7 +153,21 @@ CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & t
|
|
|
|
|
|
void CDrawRoadsOperation::execute()
|
|
|
{
|
|
|
+ std::set<int3> invalidated;
|
|
|
|
|
|
+ for(const auto & pos : terrainSel.getSelectedItems())
|
|
|
+ {
|
|
|
+ auto & tile = map->getTile(pos);
|
|
|
+ tile.roadType = roadType;
|
|
|
+
|
|
|
+ auto rect = extendTileAroundSafely(pos);
|
|
|
+ rect.forEach([&invalidated](const int3 & pos)
|
|
|
+ {
|
|
|
+ invalidated.insert(pos);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ updateTiles(invalidated);
|
|
|
}
|
|
|
|
|
|
void CDrawRoadsOperation::undo()
|
|
@@ -156,3 +185,169 @@ std::string CDrawRoadsOperation::getLabel() const
|
|
|
return "Draw Roads";
|
|
|
}
|
|
|
|
|
|
+bool CDrawRoadsOperation::canApplyPattern(const RoadPattern & pattern) const
|
|
|
+{
|
|
|
+ //TODO: this method should be virtual for river support
|
|
|
+ return pattern.roadMapping.first >= 0;
|
|
|
+}
|
|
|
+
|
|
|
+void CDrawRoadsOperation::flipPattern(RoadPattern& pattern, int flip) const
|
|
|
+{
|
|
|
+ //todo: use cashing here and also in terrain patterns
|
|
|
+
|
|
|
+ if(flip == 0)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if(flip == FLIP_PATTERN_HORIZONTAL || flip == FLIP_PATTERN_BOTH)
|
|
|
+ {
|
|
|
+ for(int i = 0; i < 3; ++i)
|
|
|
+ {
|
|
|
+ int y = i * 3;
|
|
|
+ std::swap(pattern.data[y], pattern.data[y + 2]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(flip == FLIP_PATTERN_VERTICAL || flip == FLIP_PATTERN_BOTH)
|
|
|
+ {
|
|
|
+ for(int i = 0; i < 3; ++i)
|
|
|
+ {
|
|
|
+ std::swap(pattern.data[i], pattern.data[6 + i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+bool CDrawRoadsOperation::needUpdateTile(const TerrainTile & tile) const
|
|
|
+{
|
|
|
+ return tile.roadType != ERoadType::NO_ROAD; //TODO: this method should be virtual for river support
|
|
|
+}
|
|
|
+
|
|
|
+void CDrawRoadsOperation::updateTiles(std::set<int3> & invalidated)
|
|
|
+{
|
|
|
+ for(int3 coord : invalidated)
|
|
|
+ {
|
|
|
+ TerrainTile & tile = map->getTile(coord);
|
|
|
+ ValidationResult result(false);
|
|
|
+
|
|
|
+ if(!needUpdateTile(tile))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ int bestPattern = -1;
|
|
|
+
|
|
|
+ for(int k = 0; k < patterns.size(); ++k)
|
|
|
+ {
|
|
|
+ result = validateTile(patterns[k], coord);
|
|
|
+
|
|
|
+ if(result.result)
|
|
|
+ {
|
|
|
+ bestPattern = k;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(bestPattern != -1)
|
|
|
+ {
|
|
|
+ updateTile(tile, patterns[bestPattern], result.flip);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+bool CDrawRoadsOperation::tileHasSomething(const int3& pos) const
|
|
|
+{
|
|
|
+//TODO: this method should be virtual for river support
|
|
|
+
|
|
|
+ return map->getTile(pos).roadType != ERoadType::NO_ROAD;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void CDrawRoadsOperation::updateTile(TerrainTile & tile, const RoadPattern & pattern, const int flip)
|
|
|
+{
|
|
|
+ //TODO: this method should be virtual for river support
|
|
|
+
|
|
|
+ std::pair<int, int> mapping = pattern.roadMapping;
|
|
|
+
|
|
|
+ tile.roadDir = gen->nextInt(mapping.first, mapping.second);
|
|
|
+ tile.extTileFlags = (tile.extTileFlags & 0xCF) | (flip << 4);
|
|
|
+}
|
|
|
+
|
|
|
+CDrawRoadsOperation::ValidationResult CDrawRoadsOperation::validateTile(const RoadPattern & pattern, const int3 & pos)
|
|
|
+{
|
|
|
+ ValidationResult result(false);
|
|
|
+
|
|
|
+ if(!canApplyPattern(pattern))
|
|
|
+ return result;
|
|
|
+
|
|
|
+
|
|
|
+ for(int flip = 0; flip < 4; ++flip)
|
|
|
+ {
|
|
|
+ if((flip == FLIP_PATTERN_BOTH) && !(pattern.hasHFlip && pattern.hasVFlip))
|
|
|
+ continue;
|
|
|
+ if((flip == FLIP_PATTERN_HORIZONTAL) && !pattern.hasHFlip)
|
|
|
+ continue;
|
|
|
+ if((flip == FLIP_PATTERN_VERTICAL) && !(pattern.hasVFlip))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ RoadPattern flipped = pattern;
|
|
|
+
|
|
|
+ flipPattern(flipped, flip);
|
|
|
+
|
|
|
+ bool validated = true;
|
|
|
+
|
|
|
+ for(int i = 0; i < 9; ++i)
|
|
|
+ {
|
|
|
+ if(4 == i)
|
|
|
+ continue;
|
|
|
+ int cx = pos.x + (i % 3) - 1;
|
|
|
+ int cy = pos.y + (i / 3) - 1;
|
|
|
+
|
|
|
+ int3 currentPos(cx, cy, pos.z);
|
|
|
+
|
|
|
+ bool hasSomething;
|
|
|
+
|
|
|
+ if(!map->isInTheMap(currentPos))
|
|
|
+ {
|
|
|
+ hasSomething = true; //road/river can go out of map
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ hasSomething = tileHasSomething(pos);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(ruleIsSomething(flipped.data[i]))
|
|
|
+ {
|
|
|
+ if(!hasSomething)
|
|
|
+ {
|
|
|
+ validated = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(ruleIsNone(flipped.data[i]))
|
|
|
+ {
|
|
|
+ if(hasSomething)
|
|
|
+ {
|
|
|
+ validated = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ assert(ruleIsAny(flipped.data[i]));
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if(validated)
|
|
|
+ {
|
|
|
+ result.result = true;
|
|
|
+ result.flip = flip;
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|