2015-01-03 01:29:42 +02:00
|
|
|
/*
|
|
|
|
* CDrawRoadsOperation.cpp, part of VCMI engine
|
|
|
|
*
|
|
|
|
* Authors: listed in file AUTHORS in main folder
|
|
|
|
*
|
|
|
|
* License: GNU General Public License v2.0 or later
|
|
|
|
* Full text of license available in license.txt file, in main folder
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "StdInc.h"
|
|
|
|
#include "CDrawRoadsOperation.h"
|
2015-12-02 21:05:10 +02:00
|
|
|
#include "CMap.h"
|
2015-01-03 01:29:42 +02:00
|
|
|
|
2024-01-09 16:43:36 +02:00
|
|
|
#include "../CRandomGenerator.h"
|
2023-01-11 15:17:24 +02:00
|
|
|
#include "../RoadHandler.h"
|
|
|
|
#include "../RiverHandler.h"
|
2023-01-09 01:17:37 +02:00
|
|
|
|
2022-07-26 15:07:42 +02:00
|
|
|
VCMI_LIB_NAMESPACE_BEGIN
|
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
const std::vector<CDrawLinesOperation::LinePattern> CDrawLinesOperation::patterns =
|
2015-01-03 02:04:49 +02:00
|
|
|
{
|
2015-01-03 06:37:47 +02:00
|
|
|
//single tile. fall-back pattern
|
2015-01-03 02:04:49 +02:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"-","-","-",
|
|
|
|
"-","+","-",
|
|
|
|
"-","-","-"
|
|
|
|
},
|
|
|
|
{14,14},
|
|
|
|
{9,9},
|
|
|
|
false,
|
|
|
|
false
|
|
|
|
},
|
|
|
|
//Road straight with angle
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"?","-","+",
|
|
|
|
"-","+","+",
|
2016-02-02 10:12:28 +02:00
|
|
|
"+","+","?"
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
{2,5},
|
|
|
|
{-1,-1},
|
|
|
|
true,
|
|
|
|
true
|
|
|
|
},
|
|
|
|
//Turn
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"?","-","?",
|
|
|
|
"-","+","+",
|
2016-02-02 10:12:28 +02:00
|
|
|
"?","+","?"
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
{0,1},
|
|
|
|
{0,3},
|
|
|
|
true,
|
|
|
|
true
|
|
|
|
},
|
|
|
|
//Dead end horizontal
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"?","-","?",
|
|
|
|
"-","+","+",
|
2016-02-02 10:12:28 +02:00
|
|
|
"?","-","?"
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
{15,15},{11,12},
|
2015-01-03 06:37:47 +02:00
|
|
|
true,
|
|
|
|
false
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
//Dead end vertical
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"?","-","?",
|
|
|
|
"-","+","-",
|
2016-02-02 10:12:28 +02:00
|
|
|
"?","+","?"
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
{14,14},{9,10},
|
2015-01-03 06:37:47 +02:00
|
|
|
false,
|
|
|
|
true
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
//T-cross horizontal
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"?","+","?",
|
|
|
|
"-","+","+",
|
2016-02-02 10:12:28 +02:00
|
|
|
"?","+","?"
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
{6,7},{7,8},
|
2015-01-03 06:37:47 +02:00
|
|
|
true,
|
|
|
|
false
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
//T-cross vertical
|
|
|
|
{
|
|
|
|
{
|
|
|
|
"?","-","?",
|
|
|
|
"+","+","+",
|
2016-02-02 10:12:28 +02:00
|
|
|
"?","+","?"
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
{8,9},{5,6},
|
2015-01-03 06:37:47 +02:00
|
|
|
false,
|
|
|
|
true
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
2016-02-02 10:12:28 +02:00
|
|
|
//Straight Horizontal
|
2015-01-03 02:04:49 +02:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"?","-","?",
|
|
|
|
"+","+","+",
|
2016-02-02 10:12:28 +02:00
|
|
|
"?","-","?"
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
{12,13},{11,12},
|
|
|
|
false,
|
|
|
|
false
|
|
|
|
},
|
2016-02-02 10:12:28 +02:00
|
|
|
//Straight Vertical
|
2015-01-03 02:04:49 +02:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"?","+","?",
|
|
|
|
"-","+","-",
|
2016-02-02 10:12:28 +02:00
|
|
|
"?","+","?"
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
{10,11},{9,10},
|
|
|
|
false,
|
|
|
|
false
|
|
|
|
},
|
2016-02-02 10:12:28 +02:00
|
|
|
//X-cross
|
2015-01-03 02:04:49 +02:00
|
|
|
{
|
|
|
|
{
|
|
|
|
"?","+","?",
|
|
|
|
"+","+","+",
|
2016-02-02 10:12:28 +02:00
|
|
|
"?","+","?"
|
2015-01-03 02:04:49 +02:00
|
|
|
},
|
|
|
|
{16,16},{4,4},
|
|
|
|
false,
|
|
|
|
false
|
2016-02-02 10:12:28 +02:00
|
|
|
}
|
|
|
|
|
2015-01-03 02:04:49 +02:00
|
|
|
};
|
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
static bool ruleIsNone(const std::string & rule)
|
|
|
|
{
|
|
|
|
return rule == "-";
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ruleIsSomething(const std::string & rule)
|
|
|
|
{
|
|
|
|
return rule == "+";
|
|
|
|
}
|
|
|
|
|
2016-02-02 10:12:28 +02:00
|
|
|
#ifndef NDEBUG
|
2015-01-03 04:03:44 +02:00
|
|
|
static bool ruleIsAny(const std::string & rule)
|
|
|
|
{
|
|
|
|
return rule == "?";
|
|
|
|
}
|
2016-02-02 10:12:28 +02:00
|
|
|
#endif
|
2015-01-03 04:03:44 +02:00
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
///CDrawLinesOperation
|
2023-02-11 18:30:06 +02:00
|
|
|
CDrawLinesOperation::CDrawLinesOperation(CMap * map, CTerrainSelection terrainSel, CRandomGenerator * gen):
|
2022-09-18 16:39:10 +02:00
|
|
|
CMapOperation(map),
|
2023-02-11 18:30:06 +02:00
|
|
|
terrainSel(std::move(terrainSel)),
|
2022-09-18 16:39:10 +02:00
|
|
|
gen(gen)
|
2022-08-09 07:54:32 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-01-03 01:29:42 +02:00
|
|
|
///CDrawRoadsOperation
|
2022-09-29 11:44:46 +02:00
|
|
|
CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, RoadId roadType, CRandomGenerator * gen):
|
2022-09-18 16:39:10 +02:00
|
|
|
CDrawLinesOperation(map, terrainSel,gen),
|
|
|
|
roadType(roadType)
|
2015-01-03 01:29:42 +02:00
|
|
|
{
|
2022-08-09 07:54:32 +02:00
|
|
|
}
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
///CDrawRiversOperation
|
2022-09-29 11:44:46 +02:00
|
|
|
CDrawRiversOperation::CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, RiverId riverType, CRandomGenerator * gen):
|
2022-09-18 16:39:10 +02:00
|
|
|
CDrawLinesOperation(map, terrainSel, gen),
|
|
|
|
riverType(riverType)
|
2022-08-09 07:54:32 +02:00
|
|
|
{
|
2015-01-03 01:29:42 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
void CDrawLinesOperation::execute()
|
2015-01-03 01:29:42 +02:00
|
|
|
{
|
2015-01-03 04:03:44 +02:00
|
|
|
std::set<int3> invalidated;
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
for(const auto & pos : terrainSel.getSelectedItems())
|
|
|
|
{
|
2022-08-09 07:54:32 +02:00
|
|
|
executeTile(map->getTile(pos));
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
auto rect = extendTileAroundSafely(pos);
|
|
|
|
rect.forEach([&invalidated](const int3 & pos)
|
|
|
|
{
|
|
|
|
invalidated.insert(pos);
|
|
|
|
});
|
|
|
|
}
|
2016-02-02 10:12:28 +02:00
|
|
|
|
|
|
|
updateTiles(invalidated);
|
2015-01-03 01:29:42 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
void CDrawLinesOperation::undo()
|
2015-01-03 01:29:42 +02:00
|
|
|
{
|
2016-02-02 10:12:28 +02:00
|
|
|
//TODO
|
2015-01-03 01:29:42 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
void CDrawLinesOperation::redo()
|
2015-01-03 01:29:42 +02:00
|
|
|
{
|
2016-02-02 10:12:28 +02:00
|
|
|
//TODO
|
2015-01-03 01:29:42 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
void CDrawLinesOperation::flipPattern(LinePattern& pattern, int flip) const
|
2015-01-03 04:03:44 +02:00
|
|
|
{
|
|
|
|
//todo: use cashing here and also in terrain patterns
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
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]);
|
|
|
|
}
|
2016-02-02 10:12:28 +02:00
|
|
|
}
|
2015-01-03 04:03:44 +02:00
|
|
|
}
|
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
void CDrawLinesOperation::updateTiles(std::set<int3> & invalidated)
|
2015-01-03 04:03:44 +02:00
|
|
|
{
|
2023-02-11 18:30:06 +02:00
|
|
|
for(const int3 & coord : invalidated)
|
2015-01-03 04:03:44 +02:00
|
|
|
{
|
|
|
|
TerrainTile & tile = map->getTile(coord);
|
|
|
|
ValidationResult result(false);
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
if(!needUpdateTile(tile))
|
|
|
|
continue;
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
int bestPattern = -1;
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
for(int k = 0; k < patterns.size(); ++k)
|
|
|
|
{
|
|
|
|
result = validateTile(patterns[k], coord);
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
if(result.result)
|
|
|
|
{
|
|
|
|
bestPattern = k;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
if(bestPattern != -1)
|
|
|
|
{
|
|
|
|
updateTile(tile, patterns[bestPattern], result.flip);
|
|
|
|
}
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
}
|
2017-07-12 21:01:10 +02:00
|
|
|
}
|
2015-01-03 04:03:44 +02:00
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
CDrawLinesOperation::ValidationResult CDrawLinesOperation::validateTile(const LinePattern & pattern, const int3 & pos)
|
2015-01-03 04:03:44 +02:00
|
|
|
{
|
|
|
|
ValidationResult result(false);
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
if(!canApplyPattern(pattern))
|
|
|
|
return result;
|
2016-02-02 10:12:28 +02:00
|
|
|
|
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
for(int flip = 0; flip < 4; ++flip)
|
|
|
|
{
|
2016-02-02 10:12:28 +02:00
|
|
|
if((flip == FLIP_PATTERN_BOTH) && !(pattern.hasHFlip && pattern.hasVFlip))
|
2015-01-03 04:03:44 +02:00
|
|
|
continue;
|
2016-02-02 10:12:28 +02:00
|
|
|
if((flip == FLIP_PATTERN_HORIZONTAL) && !pattern.hasHFlip)
|
2015-01-03 04:03:44 +02:00
|
|
|
continue;
|
2016-02-02 10:12:28 +02:00
|
|
|
if((flip == FLIP_PATTERN_VERTICAL) && !(pattern.hasVFlip))
|
2015-01-03 04:03:44 +02:00
|
|
|
continue;
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2022-08-09 07:54:32 +02:00
|
|
|
LinePattern flipped = pattern;
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
flipPattern(flipped, flip);
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
bool validated = true;
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
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;
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
int3 currentPos(cx, cy, pos.z);
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2023-04-13 15:26:03 +02:00
|
|
|
bool hasSomething = map->isInTheMap(currentPos) && tileHasSomething(currentPos);
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
if(ruleIsSomething(flipped.data[i]))
|
|
|
|
{
|
|
|
|
if(!hasSomething)
|
|
|
|
{
|
|
|
|
validated = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(ruleIsNone(flipped.data[i]))
|
|
|
|
{
|
|
|
|
if(hasSomething)
|
|
|
|
{
|
|
|
|
validated = false;
|
|
|
|
break;
|
2016-02-02 10:12:28 +02:00
|
|
|
}
|
2015-01-03 04:03:44 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-02-02 10:12:28 +02:00
|
|
|
assert(ruleIsAny(flipped.data[i]));
|
|
|
|
}
|
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
}
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
if(validated)
|
|
|
|
{
|
|
|
|
result.result = true;
|
|
|
|
result.flip = flip;
|
2016-02-02 10:12:28 +02:00
|
|
|
return result;
|
|
|
|
}
|
2015-01-03 04:03:44 +02:00
|
|
|
}
|
2016-02-02 10:12:28 +02:00
|
|
|
|
2015-01-03 04:03:44 +02:00
|
|
|
return result;
|
|
|
|
}
|
2022-08-09 07:54:32 +02:00
|
|
|
|
|
|
|
std::string CDrawRoadsOperation::getLabel() const
|
|
|
|
{
|
|
|
|
return "Draw Roads";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CDrawRiversOperation::getLabel() const
|
|
|
|
{
|
|
|
|
return "Draw Rivers";
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDrawRoadsOperation::executeTile(TerrainTile & tile)
|
|
|
|
{
|
2022-12-20 18:35:40 +02:00
|
|
|
tile.roadType = const_cast<RoadType*>(VLC->roadTypeHandler->getByIndex(roadType.getNum()));
|
2022-08-09 07:54:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CDrawRiversOperation::executeTile(TerrainTile & tile)
|
|
|
|
{
|
2022-12-20 18:35:40 +02:00
|
|
|
tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getByIndex(riverType.getNum()));
|
2022-08-09 07:54:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CDrawRoadsOperation::canApplyPattern(const LinePattern & pattern) const
|
|
|
|
{
|
|
|
|
return pattern.roadMapping.first >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CDrawRiversOperation::canApplyPattern(const LinePattern & pattern) const
|
|
|
|
{
|
|
|
|
return pattern.riverMapping.first >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CDrawRoadsOperation::needUpdateTile(const TerrainTile & tile) const
|
|
|
|
{
|
2023-01-01 17:10:47 +02:00
|
|
|
return tile.roadType->getId() != Road::NO_ROAD;
|
2022-08-09 07:54:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CDrawRiversOperation::needUpdateTile(const TerrainTile & tile) const
|
|
|
|
{
|
2023-01-01 17:10:47 +02:00
|
|
|
return tile.riverType->getId() != River::NO_RIVER;
|
2022-08-09 07:54:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CDrawRoadsOperation::tileHasSomething(const int3& pos) const
|
|
|
|
{
|
2023-01-01 17:10:47 +02:00
|
|
|
return map->getTile(pos).roadType->getId() != Road::NO_ROAD;
|
2022-08-09 07:54:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CDrawRiversOperation::tileHasSomething(const int3& pos) const
|
|
|
|
{
|
2023-01-01 17:10:47 +02:00
|
|
|
return map->getTile(pos).riverType->getId() != River::NO_RIVER;
|
2022-08-09 07:54:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CDrawRoadsOperation::updateTile(TerrainTile & tile, const LinePattern & pattern, const int flip)
|
|
|
|
{
|
|
|
|
const std::pair<int, int> & mapping = pattern.roadMapping;
|
|
|
|
|
|
|
|
tile.roadDir = gen->nextInt(mapping.first, mapping.second);
|
|
|
|
tile.extTileFlags = (tile.extTileFlags & 0b11001111) | (flip << 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDrawRiversOperation::updateTile(TerrainTile & tile, const LinePattern & pattern, const int flip)
|
|
|
|
{
|
|
|
|
const std::pair<int, int> & mapping = pattern.riverMapping;
|
|
|
|
|
|
|
|
tile.riverDir = gen->nextInt(mapping.first, mapping.second);
|
|
|
|
tile.extTileFlags = (tile.extTileFlags & 0b00111111) | (flip << 2);
|
|
|
|
}
|
2022-07-26 15:07:42 +02:00
|
|
|
|
|
|
|
VCMI_LIB_NAMESPACE_END
|