map_data.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. #include "map_data.h"
  2. #include <QFile>
  3. #include <QJsonDocument>
  4. namespace MapEditor {
  5. MapData::MapData(QObject *parent) : QObject(parent) { clear(); }
  6. void MapData::clear() {
  7. m_name = "New Map";
  8. m_description.clear();
  9. m_coordSystem = "grid";
  10. m_maxTroopsPerPlayer = 2000;
  11. m_grid = GridSettings{100, 100, 1.0F};
  12. m_terrain.clear();
  13. m_firecamps.clear();
  14. m_linearElements.clear();
  15. m_structures.clear();
  16. m_biome = QJsonObject();
  17. m_camera = QJsonObject();
  18. m_spawns = QJsonArray();
  19. m_victory = QJsonObject();
  20. m_rain = QJsonObject();
  21. m_undoStack.clear();
  22. m_redoStack.clear();
  23. setModified(false);
  24. emit dataChanged();
  25. emit undoRedoChanged();
  26. }
  27. void MapData::setName(const QString &name) {
  28. if (m_name != name) {
  29. m_name = name;
  30. setModified(true);
  31. emit dataChanged();
  32. }
  33. }
  34. void MapData::setGrid(const GridSettings &grid) {
  35. m_grid = grid;
  36. setModified(true);
  37. emit dataChanged();
  38. }
  39. void MapData::setModified(bool modified) {
  40. if (m_modified != modified) {
  41. m_modified = modified;
  42. emit modifiedChanged(modified);
  43. }
  44. }
  45. void MapData::executeCommand(std::unique_ptr<Command> cmd) {
  46. if (!cmd) {
  47. return;
  48. }
  49. cmd->execute();
  50. m_undoStack.push_back(std::move(cmd));
  51. m_redoStack.clear();
  52. setModified(true);
  53. emit undoRedoChanged();
  54. }
  55. void MapData::undo() {
  56. if (m_undoStack.empty()) {
  57. return;
  58. }
  59. auto cmd = std::move(m_undoStack.back());
  60. m_undoStack.pop_back();
  61. cmd->undo();
  62. m_redoStack.push_back(std::move(cmd));
  63. setModified(true);
  64. emit dataChanged();
  65. emit undoRedoChanged();
  66. }
  67. void MapData::redo() {
  68. if (m_redoStack.empty()) {
  69. return;
  70. }
  71. auto cmd = std::move(m_redoStack.back());
  72. m_redoStack.pop_back();
  73. cmd->execute();
  74. m_undoStack.push_back(std::move(cmd));
  75. setModified(true);
  76. emit dataChanged();
  77. emit undoRedoChanged();
  78. }
  79. bool MapData::loadFromJson(const QString &filePath) {
  80. QFile file(filePath);
  81. if (!file.open(QIODevice::ReadOnly)) {
  82. return false;
  83. }
  84. QByteArray data = file.readAll();
  85. file.close();
  86. QJsonParseError error;
  87. QJsonDocument doc = QJsonDocument::fromJson(data, &error);
  88. if (error.error != QJsonParseError::NoError) {
  89. return false;
  90. }
  91. QJsonObject root = doc.object();
  92. m_name = root["name"].toString("Untitled Map");
  93. m_description = root["description"].toString();
  94. m_coordSystem = root["coordSystem"].toString("grid");
  95. m_maxTroopsPerPlayer = root["maxTroopsPerPlayer"].toInt(2000);
  96. if (root.contains("grid")) {
  97. QJsonObject gridObj = root["grid"].toObject();
  98. m_grid.width = gridObj["width"].toInt(100);
  99. m_grid.height = gridObj["height"].toInt(100);
  100. m_grid.tileSize = static_cast<float>(gridObj["tileSize"].toDouble(1.0));
  101. }
  102. m_biome = root["biome"].toObject();
  103. m_camera = root["camera"].toObject();
  104. m_spawns = root["spawns"].toArray();
  105. m_victory = root["victory"].toObject();
  106. m_rain = root["rain"].toObject();
  107. m_terrain.clear();
  108. m_firecamps.clear();
  109. m_linearElements.clear();
  110. m_structures.clear();
  111. if (root.contains("terrain")) {
  112. parseTerrainArray(root["terrain"].toArray());
  113. }
  114. if (root.contains("firecamps")) {
  115. parseFirecampsArray(root["firecamps"].toArray());
  116. }
  117. if (root.contains("rivers")) {
  118. parseRiversArray(root["rivers"].toArray());
  119. }
  120. if (root.contains("roads")) {
  121. parseRoadsArray(root["roads"].toArray());
  122. }
  123. if (root.contains("bridges")) {
  124. parseBridgesArray(root["bridges"].toArray());
  125. }
  126. if (root.contains("spawns")) {
  127. parseStructuresFromSpawns(root["spawns"].toArray());
  128. }
  129. m_undoStack.clear();
  130. m_redoStack.clear();
  131. setModified(false);
  132. emit dataChanged();
  133. emit undoRedoChanged();
  134. return true;
  135. }
  136. bool MapData::saveToJson(const QString &filePath) const {
  137. QJsonObject root;
  138. root["name"] = m_name;
  139. if (!m_description.isEmpty()) {
  140. root["description"] = m_description;
  141. }
  142. root["coordSystem"] = m_coordSystem;
  143. root["maxTroopsPerPlayer"] = m_maxTroopsPerPlayer;
  144. QJsonObject gridObj;
  145. gridObj["width"] = m_grid.width;
  146. gridObj["height"] = m_grid.height;
  147. gridObj["tileSize"] = static_cast<double>(m_grid.tileSize);
  148. root["grid"] = gridObj;
  149. if (!m_biome.isEmpty()) {
  150. root["biome"] = m_biome;
  151. }
  152. if (!m_camera.isEmpty()) {
  153. root["camera"] = m_camera;
  154. }
  155. if (!m_victory.isEmpty()) {
  156. root["victory"] = m_victory;
  157. }
  158. if (!m_rain.isEmpty()) {
  159. root["rain"] = m_rain;
  160. }
  161. QJsonArray terrainArr = terrainToJson();
  162. if (!terrainArr.isEmpty()) {
  163. root["terrain"] = terrainArr;
  164. }
  165. QJsonArray firecampsArr = firecampsToJson();
  166. if (!firecampsArr.isEmpty()) {
  167. root["firecamps"] = firecampsArr;
  168. }
  169. QJsonArray riversArr = riversToJson();
  170. if (!riversArr.isEmpty()) {
  171. root["rivers"] = riversArr;
  172. }
  173. QJsonArray roadsArr = roadsToJson();
  174. if (!roadsArr.isEmpty()) {
  175. root["roads"] = roadsArr;
  176. }
  177. QJsonArray bridgesArr = bridgesToJson();
  178. if (!bridgesArr.isEmpty()) {
  179. root["bridges"] = bridgesArr;
  180. }
  181. QJsonArray spawnsArr = structuresToSpawnsJson();
  182. for (const auto &spawn : m_spawns) {
  183. QJsonObject spawnObj = spawn.toObject();
  184. QString type = spawnObj["type"].toString();
  185. if (type != "barracks" && type != "village") {
  186. spawnsArr.append(spawn);
  187. }
  188. }
  189. if (!spawnsArr.isEmpty()) {
  190. root["spawns"] = spawnsArr;
  191. }
  192. QJsonDocument doc(root);
  193. QFile file(filePath);
  194. if (!file.open(QIODevice::WriteOnly)) {
  195. return false;
  196. }
  197. file.write(doc.toJson(QJsonDocument::Indented));
  198. file.close();
  199. return true;
  200. }
  201. void MapData::parseTerrainArray(const QJsonArray &arr) {
  202. for (const auto &val : arr) {
  203. QJsonObject obj = val.toObject();
  204. TerrainElement elem;
  205. elem.type = obj["type"].toString();
  206. elem.x = static_cast<float>(obj["x"].toDouble());
  207. elem.z = static_cast<float>(obj["z"].toDouble());
  208. elem.radius = static_cast<float>(obj["radius"].toDouble(10.0));
  209. elem.width = static_cast<float>(obj["width"].toDouble(10.0));
  210. elem.depth = static_cast<float>(obj["depth"].toDouble(10.0));
  211. elem.height = static_cast<float>(obj["height"].toDouble(3.0));
  212. elem.rotation = static_cast<float>(obj["rotation"].toDouble(0.0));
  213. elem.entrances = obj["entrances"].toArray();
  214. QStringList knownKeys = {"type", "x", "z", "radius", "width",
  215. "depth", "height", "rotation", "entrances"};
  216. for (const QString &key : obj.keys()) {
  217. if (!knownKeys.contains(key)) {
  218. elem.extraFields[key] = obj[key];
  219. }
  220. }
  221. m_terrain.append(elem);
  222. }
  223. }
  224. void MapData::parseFirecampsArray(const QJsonArray &arr) {
  225. for (const auto &val : arr) {
  226. QJsonObject obj = val.toObject();
  227. FirecampElement elem;
  228. elem.x = static_cast<float>(obj["x"].toDouble());
  229. elem.z = static_cast<float>(obj["z"].toDouble());
  230. elem.intensity = static_cast<float>(obj["intensity"].toDouble(1.0));
  231. elem.radius = static_cast<float>(obj["radius"].toDouble(3.0));
  232. QStringList knownKeys = {"x", "z", "intensity", "radius"};
  233. for (const QString &key : obj.keys()) {
  234. if (!knownKeys.contains(key)) {
  235. elem.extraFields[key] = obj[key];
  236. }
  237. }
  238. m_firecamps.append(elem);
  239. }
  240. }
  241. void MapData::parseRiversArray(const QJsonArray &arr) {
  242. for (const auto &val : arr) {
  243. QJsonObject obj = val.toObject();
  244. LinearElement elem;
  245. elem.type = "river";
  246. QJsonArray startArr = obj["start"].toArray();
  247. QJsonArray endArr = obj["end"].toArray();
  248. if (startArr.size() >= 2 && endArr.size() >= 2) {
  249. elem.start = QVector2D(static_cast<float>(startArr[0].toDouble()),
  250. static_cast<float>(startArr[1].toDouble()));
  251. elem.end = QVector2D(static_cast<float>(endArr[0].toDouble()),
  252. static_cast<float>(endArr[1].toDouble()));
  253. }
  254. elem.width = static_cast<float>(obj["width"].toDouble(3.0));
  255. QStringList knownKeys = {"start", "end", "width"};
  256. for (const QString &key : obj.keys()) {
  257. if (!knownKeys.contains(key)) {
  258. elem.extraFields[key] = obj[key];
  259. }
  260. }
  261. m_linearElements.append(elem);
  262. }
  263. }
  264. void MapData::parseRoadsArray(const QJsonArray &arr) {
  265. for (const auto &val : arr) {
  266. QJsonObject obj = val.toObject();
  267. LinearElement elem;
  268. elem.type = "road";
  269. QJsonArray startArr = obj["start"].toArray();
  270. QJsonArray endArr = obj["end"].toArray();
  271. if (startArr.size() >= 2 && endArr.size() >= 2) {
  272. elem.start = QVector2D(static_cast<float>(startArr[0].toDouble()),
  273. static_cast<float>(startArr[1].toDouble()));
  274. elem.end = QVector2D(static_cast<float>(endArr[0].toDouble()),
  275. static_cast<float>(endArr[1].toDouble()));
  276. }
  277. elem.width = static_cast<float>(obj["width"].toDouble(3.0));
  278. elem.style = obj["style"].toString("default");
  279. QStringList knownKeys = {"start", "end", "width", "style"};
  280. for (const QString &key : obj.keys()) {
  281. if (!knownKeys.contains(key)) {
  282. elem.extraFields[key] = obj[key];
  283. }
  284. }
  285. m_linearElements.append(elem);
  286. }
  287. }
  288. void MapData::parseBridgesArray(const QJsonArray &arr) {
  289. for (const auto &val : arr) {
  290. QJsonObject obj = val.toObject();
  291. LinearElement elem;
  292. elem.type = "bridge";
  293. QJsonArray startArr = obj["start"].toArray();
  294. QJsonArray endArr = obj["end"].toArray();
  295. if (startArr.size() >= 2 && endArr.size() >= 2) {
  296. elem.start = QVector2D(static_cast<float>(startArr[0].toDouble()),
  297. static_cast<float>(startArr[1].toDouble()));
  298. elem.end = QVector2D(static_cast<float>(endArr[0].toDouble()),
  299. static_cast<float>(endArr[1].toDouble()));
  300. }
  301. elem.width = static_cast<float>(obj["width"].toDouble(4.0));
  302. elem.height = static_cast<float>(obj["height"].toDouble(0.5));
  303. QStringList knownKeys = {"start", "end", "width", "height"};
  304. for (const QString &key : obj.keys()) {
  305. if (!knownKeys.contains(key)) {
  306. elem.extraFields[key] = obj[key];
  307. }
  308. }
  309. m_linearElements.append(elem);
  310. }
  311. }
  312. QJsonArray MapData::terrainToJson() const {
  313. QJsonArray arr;
  314. for (const auto &elem : m_terrain) {
  315. QJsonObject obj;
  316. obj["type"] = elem.type;
  317. obj["x"] = static_cast<double>(elem.x);
  318. obj["z"] = static_cast<double>(elem.z);
  319. if (elem.type == "hill") {
  320. bool hasCustomDimensions = (elem.width != 10.0F && elem.width > 0.0F) ||
  321. (elem.depth != 10.0F && elem.depth > 0.0F);
  322. if (hasCustomDimensions) {
  323. if (elem.width > 0.0F) {
  324. obj["width"] = static_cast<double>(elem.width);
  325. }
  326. if (elem.depth > 0.0F) {
  327. obj["depth"] = static_cast<double>(elem.depth);
  328. }
  329. } else if (elem.radius > 0.0F) {
  330. obj["radius"] = static_cast<double>(elem.radius);
  331. }
  332. } else {
  333. obj["radius"] = static_cast<double>(elem.radius);
  334. }
  335. obj["height"] = static_cast<double>(elem.height);
  336. if (elem.rotation != 0.0F) {
  337. obj["rotation"] = static_cast<double>(elem.rotation);
  338. }
  339. if (!elem.entrances.isEmpty()) {
  340. obj["entrances"] = elem.entrances;
  341. }
  342. for (const QString &key : elem.extraFields.keys()) {
  343. obj[key] = elem.extraFields[key];
  344. }
  345. arr.append(obj);
  346. }
  347. return arr;
  348. }
  349. QJsonArray MapData::firecampsToJson() const {
  350. QJsonArray arr;
  351. for (const auto &elem : m_firecamps) {
  352. QJsonObject obj;
  353. obj["x"] = static_cast<double>(elem.x);
  354. obj["z"] = static_cast<double>(elem.z);
  355. obj["intensity"] = static_cast<double>(elem.intensity);
  356. obj["radius"] = static_cast<double>(elem.radius);
  357. for (const QString &key : elem.extraFields.keys()) {
  358. obj[key] = elem.extraFields[key];
  359. }
  360. arr.append(obj);
  361. }
  362. return arr;
  363. }
  364. QJsonArray MapData::riversToJson() const {
  365. QJsonArray arr;
  366. for (const auto &elem : m_linearElements) {
  367. if (elem.type != "river") {
  368. continue;
  369. }
  370. QJsonObject obj;
  371. obj["start"] = QJsonArray{static_cast<double>(elem.start.x()),
  372. static_cast<double>(elem.start.y())};
  373. obj["end"] = QJsonArray{static_cast<double>(elem.end.x()),
  374. static_cast<double>(elem.end.y())};
  375. obj["width"] = static_cast<double>(elem.width);
  376. for (const QString &key : elem.extraFields.keys()) {
  377. obj[key] = elem.extraFields[key];
  378. }
  379. arr.append(obj);
  380. }
  381. return arr;
  382. }
  383. QJsonArray MapData::roadsToJson() const {
  384. QJsonArray arr;
  385. for (const auto &elem : m_linearElements) {
  386. if (elem.type != "road") {
  387. continue;
  388. }
  389. QJsonObject obj;
  390. obj["start"] = QJsonArray{static_cast<double>(elem.start.x()),
  391. static_cast<double>(elem.start.y())};
  392. obj["end"] = QJsonArray{static_cast<double>(elem.end.x()),
  393. static_cast<double>(elem.end.y())};
  394. obj["width"] = static_cast<double>(elem.width);
  395. obj["style"] = elem.style.isEmpty() ? "default" : elem.style;
  396. for (const QString &key : elem.extraFields.keys()) {
  397. obj[key] = elem.extraFields[key];
  398. }
  399. arr.append(obj);
  400. }
  401. return arr;
  402. }
  403. QJsonArray MapData::bridgesToJson() const {
  404. QJsonArray arr;
  405. for (const auto &elem : m_linearElements) {
  406. if (elem.type != "bridge") {
  407. continue;
  408. }
  409. QJsonObject obj;
  410. obj["start"] = QJsonArray{static_cast<double>(elem.start.x()),
  411. static_cast<double>(elem.start.y())};
  412. obj["end"] = QJsonArray{static_cast<double>(elem.end.x()),
  413. static_cast<double>(elem.end.y())};
  414. obj["width"] = static_cast<double>(elem.width);
  415. obj["height"] = static_cast<double>(elem.height);
  416. for (const QString &key : elem.extraFields.keys()) {
  417. obj[key] = elem.extraFields[key];
  418. }
  419. arr.append(obj);
  420. }
  421. return arr;
  422. }
  423. void MapData::addTerrainElement(const TerrainElement &element) {
  424. m_terrain.append(element);
  425. setModified(true);
  426. emit dataChanged();
  427. }
  428. void MapData::updateTerrainElement(int index, const TerrainElement &element) {
  429. if (index >= 0 && index < m_terrain.size()) {
  430. m_terrain[index] = element;
  431. setModified(true);
  432. emit dataChanged();
  433. }
  434. }
  435. void MapData::removeTerrainElement(int index) {
  436. if (index >= 0 && index < m_terrain.size()) {
  437. m_terrain.removeAt(index);
  438. setModified(true);
  439. emit dataChanged();
  440. }
  441. }
  442. void MapData::addFirecamp(const FirecampElement &element) {
  443. m_firecamps.append(element);
  444. setModified(true);
  445. emit dataChanged();
  446. }
  447. void MapData::updateFirecamp(int index, const FirecampElement &element) {
  448. if (index >= 0 && index < m_firecamps.size()) {
  449. m_firecamps[index] = element;
  450. setModified(true);
  451. emit dataChanged();
  452. }
  453. }
  454. void MapData::removeFirecamp(int index) {
  455. if (index >= 0 && index < m_firecamps.size()) {
  456. m_firecamps.removeAt(index);
  457. setModified(true);
  458. emit dataChanged();
  459. }
  460. }
  461. void MapData::addLinearElement(const LinearElement &element) {
  462. m_linearElements.append(element);
  463. setModified(true);
  464. emit dataChanged();
  465. }
  466. void MapData::updateLinearElement(int index, const LinearElement &element) {
  467. if (index >= 0 && index < m_linearElements.size()) {
  468. m_linearElements[index] = element;
  469. setModified(true);
  470. emit dataChanged();
  471. }
  472. }
  473. void MapData::removeLinearElement(int index) {
  474. if (index >= 0 && index < m_linearElements.size()) {
  475. m_linearElements.removeAt(index);
  476. setModified(true);
  477. emit dataChanged();
  478. }
  479. }
  480. void MapData::addStructure(const StructureElement &element) {
  481. m_structures.append(element);
  482. setModified(true);
  483. emit dataChanged();
  484. }
  485. void MapData::updateStructure(int index, const StructureElement &element) {
  486. if (index >= 0 && index < m_structures.size()) {
  487. m_structures[index] = element;
  488. setModified(true);
  489. emit dataChanged();
  490. }
  491. }
  492. void MapData::removeStructure(int index) {
  493. if (index >= 0 && index < m_structures.size()) {
  494. m_structures.removeAt(index);
  495. setModified(true);
  496. emit dataChanged();
  497. }
  498. }
  499. void MapData::parseStructuresFromSpawns(const QJsonArray &arr) {
  500. for (const auto &val : arr) {
  501. QJsonObject obj = val.toObject();
  502. QString type = obj["type"].toString();
  503. if (type == "barracks" || type == "village") {
  504. StructureElement elem;
  505. elem.type = type;
  506. elem.x = static_cast<float>(obj["x"].toDouble());
  507. elem.z = static_cast<float>(obj["z"].toDouble());
  508. elem.playerId = obj["playerId"].toInt(0);
  509. elem.maxPopulation = obj["maxPopulation"].toInt(150);
  510. elem.nation = obj["nation"].toString();
  511. QStringList knownKeys = {"type", "x", "z", "playerId",
  512. "maxPopulation", "nation"};
  513. for (const QString &key : obj.keys()) {
  514. if (!knownKeys.contains(key)) {
  515. elem.extraFields[key] = obj[key];
  516. }
  517. }
  518. m_structures.append(elem);
  519. }
  520. }
  521. }
  522. QJsonArray MapData::structuresToSpawnsJson() const {
  523. QJsonArray arr;
  524. for (const auto &elem : m_structures) {
  525. QJsonObject obj;
  526. obj["type"] = elem.type;
  527. obj["x"] = static_cast<double>(elem.x);
  528. obj["z"] = static_cast<double>(elem.z);
  529. if (elem.playerId > 0) {
  530. obj["playerId"] = elem.playerId;
  531. }
  532. obj["maxPopulation"] = elem.maxPopulation;
  533. if (!elem.nation.isEmpty()) {
  534. obj["nation"] = elem.nation;
  535. }
  536. for (const QString &key : elem.extraFields.keys()) {
  537. obj[key] = elem.extraFields[key];
  538. }
  539. arr.append(obj);
  540. }
  541. return arr;
  542. }
  543. } // namespace MapEditor