CDrawRoadsOperation.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * CDrawRoadsOperation.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "CDrawRoadsOperation.h"
  12. const std::vector<CDrawRoadsOperation::RoadPattern> CDrawRoadsOperation::patterns =
  13. {
  14. //single tile. fallback patern
  15. {
  16. {
  17. "-","-","-",
  18. "-","+","-",
  19. "-","-","-"
  20. },
  21. {14,14},
  22. {9,9},
  23. false,
  24. false
  25. },
  26. //Road straight with angle
  27. {
  28. {
  29. "?","-","+",
  30. "-","+","+",
  31. "+","+","?"
  32. },
  33. {2,5},
  34. {-1,-1},
  35. true,
  36. true
  37. },
  38. //Turn
  39. {
  40. {
  41. "?","-","?",
  42. "-","+","+",
  43. "?","+","?"
  44. },
  45. {0,1},
  46. {0,3},
  47. true,
  48. true
  49. },
  50. //Dead end horizontal
  51. {
  52. {
  53. "?","-","?",
  54. "-","+","+",
  55. "?","-","?"
  56. },
  57. {15,15},{11,12},
  58. false,
  59. true
  60. },
  61. //Dead end vertical
  62. {
  63. {
  64. "?","-","?",
  65. "-","+","-",
  66. "?","+","?"
  67. },
  68. {14,14},{9,10},
  69. true,
  70. false
  71. },
  72. //T-cross horizontal
  73. {
  74. {
  75. "?","+","?",
  76. "-","+","+",
  77. "?","+","?"
  78. },
  79. {6,7},{7,8},
  80. false,
  81. true
  82. },
  83. //T-cross vertical
  84. {
  85. {
  86. "?","-","?",
  87. "+","+","+",
  88. "?","+","?"
  89. },
  90. {8,9},{5,6},
  91. true,
  92. false
  93. },
  94. //Straight Horizontal
  95. {
  96. {
  97. "?","-","?",
  98. "+","+","+",
  99. "?","-","?"
  100. },
  101. {12,13},{11,12},
  102. false,
  103. false
  104. },
  105. //Straight Vertical
  106. {
  107. {
  108. "?","+","?",
  109. "-","+","-",
  110. "?","+","?"
  111. },
  112. {10,11},{9,10},
  113. false,
  114. false
  115. },
  116. //X-cross
  117. {
  118. {
  119. "?","+","?",
  120. "+","+","+",
  121. "?","+","?"
  122. },
  123. {16,16},{4,4},
  124. false,
  125. false
  126. }
  127. };
  128. static bool ruleIsNone(const std::string & rule)
  129. {
  130. return rule == "-";
  131. }
  132. static bool ruleIsSomething(const std::string & rule)
  133. {
  134. return rule == "+";
  135. }
  136. static bool ruleIsAny(const std::string & rule)
  137. {
  138. return rule == "?";
  139. }
  140. ///CDrawRoadsOperation
  141. CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, ERoadType::ERoadType roadType, CRandomGenerator * gen):
  142. CMapOperation(map),terrainSel(terrainSel), roadType(roadType), gen(gen)
  143. {
  144. }
  145. void CDrawRoadsOperation::execute()
  146. {
  147. std::set<int3> invalidated;
  148. for(const auto & pos : terrainSel.getSelectedItems())
  149. {
  150. auto & tile = map->getTile(pos);
  151. tile.roadType = roadType;
  152. auto rect = extendTileAroundSafely(pos);
  153. rect.forEach([&invalidated](const int3 & pos)
  154. {
  155. invalidated.insert(pos);
  156. });
  157. }
  158. updateTiles(invalidated);
  159. }
  160. void CDrawRoadsOperation::undo()
  161. {
  162. //TODO
  163. }
  164. void CDrawRoadsOperation::redo()
  165. {
  166. //TODO
  167. }
  168. std::string CDrawRoadsOperation::getLabel() const
  169. {
  170. return "Draw Roads";
  171. }
  172. bool CDrawRoadsOperation::canApplyPattern(const RoadPattern & pattern) const
  173. {
  174. //TODO: this method should be virtual for river support
  175. return pattern.roadMapping.first >= 0;
  176. }
  177. void CDrawRoadsOperation::flipPattern(RoadPattern& pattern, int flip) const
  178. {
  179. //todo: use cashing here and also in terrain patterns
  180. if(flip == 0)
  181. {
  182. return;
  183. }
  184. if(flip == FLIP_PATTERN_HORIZONTAL || flip == FLIP_PATTERN_BOTH)
  185. {
  186. for(int i = 0; i < 3; ++i)
  187. {
  188. int y = i * 3;
  189. std::swap(pattern.data[y], pattern.data[y + 2]);
  190. }
  191. }
  192. if(flip == FLIP_PATTERN_VERTICAL || flip == FLIP_PATTERN_BOTH)
  193. {
  194. for(int i = 0; i < 3; ++i)
  195. {
  196. std::swap(pattern.data[i], pattern.data[6 + i]);
  197. }
  198. }
  199. }
  200. bool CDrawRoadsOperation::needUpdateTile(const TerrainTile & tile) const
  201. {
  202. return tile.roadType != ERoadType::NO_ROAD; //TODO: this method should be virtual for river support
  203. }
  204. void CDrawRoadsOperation::updateTiles(std::set<int3> & invalidated)
  205. {
  206. for(int3 coord : invalidated)
  207. {
  208. TerrainTile & tile = map->getTile(coord);
  209. ValidationResult result(false);
  210. if(!needUpdateTile(tile))
  211. continue;
  212. int bestPattern = -1;
  213. for(int k = 0; k < patterns.size(); ++k)
  214. {
  215. result = validateTile(patterns[k], coord);
  216. if(result.result)
  217. {
  218. bestPattern = k;
  219. break;
  220. }
  221. }
  222. if(bestPattern != -1)
  223. {
  224. updateTile(tile, patterns[bestPattern], result.flip);
  225. }
  226. }
  227. };
  228. bool CDrawRoadsOperation::tileHasSomething(const int3& pos) const
  229. {
  230. //TODO: this method should be virtual for river support
  231. return map->getTile(pos).roadType != ERoadType::NO_ROAD;
  232. }
  233. void CDrawRoadsOperation::updateTile(TerrainTile & tile, const RoadPattern & pattern, const int flip)
  234. {
  235. //TODO: this method should be virtual for river support
  236. std::pair<int, int> mapping = pattern.roadMapping;
  237. tile.roadDir = gen->nextInt(mapping.first, mapping.second);
  238. tile.extTileFlags = (tile.extTileFlags & 0xCF) | (flip << 4);
  239. }
  240. CDrawRoadsOperation::ValidationResult CDrawRoadsOperation::validateTile(const RoadPattern & pattern, const int3 & pos)
  241. {
  242. ValidationResult result(false);
  243. if(!canApplyPattern(pattern))
  244. return result;
  245. for(int flip = 0; flip < 4; ++flip)
  246. {
  247. if((flip == FLIP_PATTERN_BOTH) && !(pattern.hasHFlip && pattern.hasVFlip))
  248. continue;
  249. if((flip == FLIP_PATTERN_HORIZONTAL) && !pattern.hasHFlip)
  250. continue;
  251. if((flip == FLIP_PATTERN_VERTICAL) && !(pattern.hasVFlip))
  252. continue;
  253. RoadPattern flipped = pattern;
  254. flipPattern(flipped, flip);
  255. bool validated = true;
  256. for(int i = 0; i < 9; ++i)
  257. {
  258. if(4 == i)
  259. continue;
  260. int cx = pos.x + (i % 3) - 1;
  261. int cy = pos.y + (i / 3) - 1;
  262. int3 currentPos(cx, cy, pos.z);
  263. bool hasSomething;
  264. if(!map->isInTheMap(currentPos))
  265. {
  266. hasSomething = true; //road/river can go out of map
  267. }
  268. else
  269. {
  270. hasSomething = tileHasSomething(pos);
  271. }
  272. if(ruleIsSomething(flipped.data[i]))
  273. {
  274. if(!hasSomething)
  275. {
  276. validated = false;
  277. break;
  278. }
  279. }
  280. else if(ruleIsNone(flipped.data[i]))
  281. {
  282. if(hasSomething)
  283. {
  284. validated = false;
  285. break;
  286. }
  287. }
  288. else
  289. {
  290. assert(ruleIsAny(flipped.data[i]));
  291. }
  292. }
  293. if(validated)
  294. {
  295. result.result = true;
  296. result.flip = flip;
  297. return result;
  298. }
  299. }
  300. return result;
  301. }