mapsettings.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  1. /*
  2. * mapsettings.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 "mapsettings.h"
  11. #include "ui_mapsettings.h"
  12. #include "mainwindow.h"
  13. #include "../lib/CSkillHandler.h"
  14. #include "../lib/spells/CSpellHandler.h"
  15. #include "../lib/CArtHandler.h"
  16. #include "../lib/CHeroHandler.h"
  17. #include "../lib/CGeneralTextHandler.h"
  18. #include "../lib/CModHandler.h"
  19. #include "../lib/mapObjects/CGHeroInstance.h"
  20. #include "../lib/mapObjects/MiscObjects.h"
  21. #include "../lib/mapping/CMapService.h"
  22. #include "../lib/StringConstants.h"
  23. #include "inspector/townbulidingswidget.h" //to convert BuildingID to string
  24. //parses date for lose condition (1m 1w 1d)
  25. int expiredDate(const QString & date)
  26. {
  27. int result = 0;
  28. for(auto component : date.split(" "))
  29. {
  30. int days = component.left(component.lastIndexOf('d')).toInt();
  31. int weeks = component.left(component.lastIndexOf('w')).toInt();
  32. int months = component.left(component.lastIndexOf('m')).toInt();
  33. result += days > 0 ? days - 1 : 0;
  34. result += (weeks > 0 ? weeks - 1 : 0) * 7;
  35. result += (months > 0 ? months - 1 : 0) * 28;
  36. }
  37. return result;
  38. }
  39. QString expiredDate(int date)
  40. {
  41. QString result;
  42. int m = date / 28;
  43. int w = (date % 28) / 7;
  44. int d = date % 7;
  45. if(m)
  46. result += QString::number(m) + "m";
  47. if(w)
  48. {
  49. if(!result.isEmpty())
  50. result += " ";
  51. result += QString::number(w) + "w";
  52. }
  53. if(d)
  54. {
  55. if(!result.isEmpty())
  56. result += " ";
  57. result += QString::number(d) + "d";
  58. }
  59. return result;
  60. }
  61. int3 posFromJson(const JsonNode & json)
  62. {
  63. return int3(json.Vector()[0].Integer(), json.Vector()[1].Integer(), json.Vector()[2].Integer());
  64. }
  65. std::vector<JsonNode> linearJsonArray(const JsonNode & json)
  66. {
  67. std::vector<JsonNode> result;
  68. if(json.getType() == JsonNode::JsonType::DATA_STRUCT)
  69. result.push_back(json);
  70. if(json.getType() == JsonNode::JsonType::DATA_VECTOR)
  71. {
  72. for(auto & node : json.Vector())
  73. {
  74. auto subvector = linearJsonArray(node);
  75. result.insert(result.end(), subvector.begin(), subvector.end());
  76. }
  77. }
  78. return result;
  79. }
  80. void traverseNode(QTreeWidgetItem * item, std::function<void(QTreeWidgetItem*)> action)
  81. {
  82. // Do something with item
  83. action(item);
  84. for (int i = 0; i < item->childCount(); ++i)
  85. traverseNode(item->child(i), action);
  86. }
  87. MapSettings::MapSettings(MapController & ctrl, QWidget *parent) :
  88. QDialog(parent),
  89. ui(new Ui::MapSettings),
  90. controller(ctrl)
  91. {
  92. ui->setupUi(this);
  93. assert(controller.map());
  94. ui->mapNameEdit->setText(tr(controller.map()->name.c_str()));
  95. ui->mapDescriptionEdit->setPlainText(tr(controller.map()->description.c_str()));
  96. ui->heroLevelLimit->setValue(controller.map()->levelLimit);
  97. ui->heroLevelLimitCheck->setChecked(controller.map()->levelLimit);
  98. show();
  99. for(int i = 0; i < controller.map()->allowedAbilities.size(); ++i)
  100. {
  101. auto * item = new QListWidgetItem(QString::fromStdString(VLC->skillh->objects[i]->getNameTranslated()));
  102. item->setData(Qt::UserRole, QVariant::fromValue(i));
  103. item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
  104. item->setCheckState(controller.map()->allowedAbilities[i] ? Qt::Checked : Qt::Unchecked);
  105. ui->listAbilities->addItem(item);
  106. }
  107. for(int i = 0; i < controller.map()->allowedSpell.size(); ++i)
  108. {
  109. auto * item = new QListWidgetItem(QString::fromStdString(VLC->spellh->objects[i]->getNameTranslated()));
  110. item->setData(Qt::UserRole, QVariant::fromValue(i));
  111. item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
  112. item->setCheckState(controller.map()->allowedSpell[i] ? Qt::Checked : Qt::Unchecked);
  113. ui->listSpells->addItem(item);
  114. }
  115. for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
  116. {
  117. auto * item = new QListWidgetItem(QString::fromStdString(VLC->arth->objects[i]->getNameTranslated()));
  118. item->setData(Qt::UserRole, QVariant::fromValue(i));
  119. item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
  120. item->setCheckState(controller.map()->allowedArtifact[i] ? Qt::Checked : Qt::Unchecked);
  121. ui->listArts->addItem(item);
  122. }
  123. for(int i = 0; i < controller.map()->allowedHeroes.size(); ++i)
  124. {
  125. auto * item = new QListWidgetItem(QString::fromStdString(VLC->heroh->objects[i]->getNameTranslated()));
  126. item->setData(Qt::UserRole, QVariant::fromValue(i));
  127. item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
  128. item->setCheckState(controller.map()->allowedHeroes[i] ? Qt::Checked : Qt::Unchecked);
  129. ui->listHeroes->addItem(item);
  130. }
  131. //set difficulty
  132. switch(controller.map()->difficulty)
  133. {
  134. case 0:
  135. ui->diffRadio1->setChecked(true);
  136. break;
  137. case 1:
  138. ui->diffRadio2->setChecked(true);
  139. break;
  140. case 2:
  141. ui->diffRadio3->setChecked(true);
  142. break;
  143. case 3:
  144. ui->diffRadio4->setChecked(true);
  145. break;
  146. case 4:
  147. ui->diffRadio5->setChecked(true);
  148. break;
  149. };
  150. //victory & loss messages
  151. ui->victoryMessageEdit->setText(QString::fromStdString(controller.map()->victoryMessage));
  152. ui->defeatMessageEdit->setText(QString::fromStdString(controller.map()->defeatMessage));
  153. //victory & loss conditions
  154. const std::array<std::string, 8> conditionStringsWin = {
  155. QT_TR_NOOP("No special victory"),
  156. QT_TR_NOOP("Capture artifact"),
  157. QT_TR_NOOP("Hire creatures"),
  158. QT_TR_NOOP("Accumulate resources"),
  159. QT_TR_NOOP("Construct building"),
  160. QT_TR_NOOP("Capture town"),
  161. QT_TR_NOOP("Defeat hero"),
  162. QT_TR_NOOP("Transport artifact")
  163. };
  164. const std::array<std::string, 5> conditionStringsLose = {
  165. QT_TR_NOOP("No special loss"),
  166. QT_TR_NOOP("Lose castle"),
  167. QT_TR_NOOP("Lose hero"),
  168. QT_TR_NOOP("Time expired"),
  169. QT_TR_NOOP("Days without town")
  170. };
  171. for(auto & s : conditionStringsWin)
  172. {
  173. ui->victoryComboBox->addItem(QString::fromStdString(s));
  174. }
  175. ui->standardVictoryCheck->setChecked(false);
  176. ui->onlyForHumansCheck->setChecked(false);
  177. for(auto & s : conditionStringsLose)
  178. {
  179. ui->loseComboBox->addItem(QString::fromStdString(s));
  180. }
  181. ui->standardLoseCheck->setChecked(false);
  182. auto conditionToJson = [](const EventCondition & event) -> JsonNode
  183. {
  184. JsonNode result;
  185. result["condition"].Integer() = event.condition;
  186. result["value"].Integer() = event.value;
  187. result["objectType"].Integer() = event.objectType;
  188. result["objectSubytype"].Integer() = event.objectSubtype;
  189. result["objectInstanceName"].String() = event.objectInstanceName;
  190. result["metaType"].Integer() = (ui8)event.metaType;
  191. {
  192. auto & position = result["position"].Vector();
  193. position.resize(3);
  194. position[0].Float() = event.position.x;
  195. position[1].Float() = event.position.y;
  196. position[2].Float() = event.position.z;
  197. }
  198. return result;
  199. };
  200. for(auto & ev : controller.map()->triggeredEvents)
  201. {
  202. if(ev.effect.type == EventEffect::VICTORY)
  203. {
  204. if(ev.identifier == "standardVictory")
  205. ui->standardVictoryCheck->setChecked(true);
  206. if(ev.identifier == "specialVictory")
  207. {
  208. auto readjson = ev.trigger.toJson(conditionToJson);
  209. auto linearNodes = linearJsonArray(readjson);
  210. for(auto & json : linearNodes)
  211. {
  212. switch(json["condition"].Integer())
  213. {
  214. case EventCondition::HAVE_ARTIFACT: {
  215. ui->victoryComboBox->setCurrentIndex(1);
  216. assert(victoryTypeWidget);
  217. victoryTypeWidget->setCurrentIndex(json["objectType"].Integer());
  218. break;
  219. }
  220. case EventCondition::HAVE_CREATURES: {
  221. ui->victoryComboBox->setCurrentIndex(2);
  222. assert(victoryTypeWidget);
  223. assert(victoryValueWidget);
  224. auto idx = victoryTypeWidget->findData(int(json["objectType"].Integer()));
  225. victoryTypeWidget->setCurrentIndex(idx);
  226. victoryValueWidget->setText(QString::number(json["value"].Integer()));
  227. break;
  228. }
  229. case EventCondition::HAVE_RESOURCES: {
  230. ui->victoryComboBox->setCurrentIndex(3);
  231. assert(victoryTypeWidget);
  232. assert(victoryValueWidget);
  233. auto idx = victoryTypeWidget->findData(int(json["objectType"].Integer()));
  234. victoryTypeWidget->setCurrentIndex(idx);
  235. victoryValueWidget->setText(QString::number(json["value"].Integer()));
  236. break;
  237. }
  238. case EventCondition::HAVE_BUILDING: {
  239. ui->victoryComboBox->setCurrentIndex(4);
  240. assert(victoryTypeWidget);
  241. assert(victorySelectWidget);
  242. auto idx = victoryTypeWidget->findData(int(json["objectType"].Integer()));
  243. victoryTypeWidget->setCurrentIndex(idx);
  244. int townIdx = getObjectByPos<CGTownInstance>(posFromJson(json["position"]));
  245. if(townIdx >= 0)
  246. {
  247. auto idx = victorySelectWidget->findData(townIdx);
  248. victorySelectWidget->setCurrentIndex(idx);
  249. }
  250. break;
  251. }
  252. case EventCondition::CONTROL: {
  253. ui->victoryComboBox->setCurrentIndex(5);
  254. assert(victoryTypeWidget);
  255. if(json["objectType"].Integer() == Obj::TOWN)
  256. {
  257. int townIdx = getObjectByPos<CGTownInstance>(posFromJson(json["position"]));
  258. if(townIdx >= 0)
  259. {
  260. auto idx = victoryTypeWidget->findData(townIdx);
  261. victoryTypeWidget->setCurrentIndex(idx);
  262. }
  263. }
  264. //TODO: support control other objects (dwellings, mines)
  265. break;
  266. }
  267. case EventCondition::DESTROY: {
  268. ui->victoryComboBox->setCurrentIndex(6);
  269. assert(victoryTypeWidget);
  270. if(json["objectType"].Integer() == Obj::HERO)
  271. {
  272. int heroIdx = getObjectByPos<CGHeroInstance>(posFromJson(json["position"]));
  273. if(heroIdx >= 0)
  274. {
  275. auto idx = victoryTypeWidget->findData(heroIdx);
  276. victoryTypeWidget->setCurrentIndex(idx);
  277. }
  278. }
  279. //TODO: support control other objects (monsters)
  280. break;
  281. }
  282. case EventCondition::TRANSPORT: {
  283. ui->victoryComboBox->setCurrentIndex(7);
  284. assert(victoryTypeWidget);
  285. assert(victorySelectWidget);
  286. victoryTypeWidget->setCurrentIndex(json["objectType"].Integer());
  287. int townIdx = getObjectByPos<CGTownInstance>(posFromJson(json["position"]));
  288. if(townIdx >= 0)
  289. {
  290. auto idx = victorySelectWidget->findData(townIdx);
  291. victorySelectWidget->setCurrentIndex(idx);
  292. }
  293. break;
  294. }
  295. case EventCondition::IS_HUMAN: {
  296. ui->onlyForHumansCheck->setChecked(true);
  297. break;
  298. }
  299. };
  300. }
  301. }
  302. }
  303. if(ev.effect.type == EventEffect::DEFEAT)
  304. {
  305. if(ev.identifier == "standardDefeat")
  306. ui->standardLoseCheck->setChecked(true);
  307. if(ev.identifier == "specialDefeat")
  308. {
  309. auto readjson = ev.trigger.toJson(conditionToJson);
  310. auto linearNodes = linearJsonArray(readjson);
  311. for(auto & json : linearNodes)
  312. {
  313. switch(json["condition"].Integer())
  314. {
  315. case EventCondition::CONTROL: {
  316. if(json["objectType"].Integer() == Obj::TOWN)
  317. {
  318. ui->loseComboBox->setCurrentIndex(1);
  319. assert(loseTypeWidget);
  320. int townIdx = getObjectByPos<CGTownInstance>(posFromJson(json["position"]));
  321. if(townIdx >= 0)
  322. {
  323. auto idx = loseTypeWidget->findData(townIdx);
  324. loseTypeWidget->setCurrentIndex(idx);
  325. }
  326. }
  327. if(json["objectType"].Integer() == Obj::HERO)
  328. {
  329. ui->loseComboBox->setCurrentIndex(2);
  330. assert(loseTypeWidget);
  331. int heroIdx = getObjectByPos<CGHeroInstance>(posFromJson(json["position"]));
  332. if(heroIdx >= 0)
  333. {
  334. auto idx = loseTypeWidget->findData(heroIdx);
  335. loseTypeWidget->setCurrentIndex(idx);
  336. }
  337. }
  338. break;
  339. }
  340. case EventCondition::DAYS_PASSED: {
  341. ui->loseComboBox->setCurrentIndex(3);
  342. assert(loseValueWidget);
  343. loseValueWidget->setText(expiredDate(json["value"].Integer()));
  344. break;
  345. }
  346. case EventCondition::DAYS_WITHOUT_TOWN: {
  347. ui->loseComboBox->setCurrentIndex(4);
  348. assert(loseValueWidget);
  349. loseValueWidget->setText(QString::number(json["value"].Integer()));
  350. break;
  351. case EventCondition::IS_HUMAN:
  352. break; //ignore because always applicable for defeat conditions
  353. }
  354. };
  355. }
  356. }
  357. }
  358. }
  359. //mods management
  360. //collect all active mods
  361. QMap<QString, QTreeWidgetItem*> addedMods;
  362. QSet<QString> modsToProcess;
  363. ui->treeMods->blockSignals(true);
  364. auto createModTreeWidgetItem = [&](QTreeWidgetItem * parent, const CModInfo & modInfo)
  365. {
  366. auto item = new QTreeWidgetItem(parent, {QString::fromStdString(modInfo.name), QString::fromStdString(modInfo.version.toString())});
  367. item->setData(0, Qt::UserRole, QVariant(QString::fromStdString(modInfo.identifier)));
  368. item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
  369. item->setCheckState(0, controller.map()->mods.count(modInfo.identifier) ? Qt::Checked : Qt::Unchecked);
  370. //set parent check
  371. if(parent && item->checkState(0) == Qt::Checked)
  372. parent->setCheckState(0, Qt::Checked);
  373. return item;
  374. };
  375. for(const auto & modName : VLC->modh->getActiveMods())
  376. {
  377. QString qmodName = QString::fromStdString(modName);
  378. if(qmodName.split(".").size() == 1)
  379. {
  380. const auto & modInfo = VLC->modh->getModInfo(modName);
  381. addedMods[qmodName] = createModTreeWidgetItem(nullptr, modInfo);
  382. ui->treeMods->addTopLevelItem(addedMods[qmodName]);
  383. }
  384. else
  385. {
  386. modsToProcess.insert(qmodName);
  387. }
  388. }
  389. for(auto qmodIter = modsToProcess.begin(); qmodIter != modsToProcess.end();)
  390. {
  391. auto qmodName = *qmodIter;
  392. auto pieces = qmodName.split(".");
  393. assert(pieces.size() > 1);
  394. QString qs;
  395. for(int i = 0; i < pieces.size() - 1; ++i)
  396. qs += pieces[i];
  397. if(addedMods.count(qs))
  398. {
  399. const auto & modInfo = VLC->modh->getModInfo(qmodName.toStdString());
  400. addedMods[qmodName] = createModTreeWidgetItem(addedMods[qs], modInfo);
  401. modsToProcess.erase(qmodIter);
  402. qmodIter = modsToProcess.begin();
  403. }
  404. else
  405. ++qmodIter;
  406. }
  407. ui->treeMods->blockSignals(false);
  408. }
  409. MapSettings::~MapSettings()
  410. {
  411. delete ui;
  412. }
  413. std::string MapSettings::getTownName(int townObjectIdx)
  414. {
  415. std::string name;
  416. if(auto town = dynamic_cast<CGTownInstance*>(controller.map()->objects[townObjectIdx].get()))
  417. {
  418. auto * ctown = town->town;
  419. if(!ctown)
  420. ctown = VLC->townh->randomTown;
  421. name = ctown->faction ? town->getObjectName() : town->getNameTranslated() + ", (random)";
  422. }
  423. return name;
  424. }
  425. std::string MapSettings::getHeroName(int townObjectIdx)
  426. {
  427. std::string name;
  428. if(auto hero = dynamic_cast<CGHeroInstance*>(controller.map()->objects[townObjectIdx].get()))
  429. {
  430. name = hero->getNameTranslated();
  431. }
  432. return name;
  433. }
  434. std::string MapSettings::getMonsterName(int monsterObjectIdx)
  435. {
  436. std::string name;
  437. [[maybe_unused]] auto monster = dynamic_cast<CGCreature*>(controller.map()->objects[monsterObjectIdx].get());
  438. if(monster)
  439. {
  440. //TODO: get proper name
  441. //name = hero->name;
  442. }
  443. return name;
  444. }
  445. void MapSettings::updateModWidgetBasedOnMods(const ModCompatibilityInfo & mods)
  446. {
  447. //Mod management
  448. auto widgetAction = [&](QTreeWidgetItem * item)
  449. {
  450. auto modName = item->data(0, Qt::UserRole).toString().toStdString();
  451. item->setCheckState(0, mods.count(modName) ? Qt::Checked : Qt::Unchecked);
  452. };
  453. for (int i = 0; i < ui->treeMods->topLevelItemCount(); ++i)
  454. {
  455. QTreeWidgetItem *item = ui->treeMods->topLevelItem(i);
  456. traverseNode(item, widgetAction);
  457. }
  458. }
  459. void MapSettings::on_pushButton_clicked()
  460. {
  461. controller.map()->name = ui->mapNameEdit->text().toStdString();
  462. controller.map()->description = ui->mapDescriptionEdit->toPlainText().toStdString();
  463. if(ui->heroLevelLimitCheck->isChecked())
  464. controller.map()->levelLimit = ui->heroLevelLimit->value();
  465. else
  466. controller.map()->levelLimit = 0;
  467. controller.commitChangeWithoutRedraw();
  468. for(int i = 0; i < controller.map()->allowedAbilities.size(); ++i)
  469. {
  470. auto * item = ui->listAbilities->item(i);
  471. controller.map()->allowedAbilities[i] = item->checkState() == Qt::Checked;
  472. }
  473. for(int i = 0; i < controller.map()->allowedSpell.size(); ++i)
  474. {
  475. auto * item = ui->listSpells->item(i);
  476. controller.map()->allowedSpell[i] = item->checkState() == Qt::Checked;
  477. }
  478. for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
  479. {
  480. auto * item = ui->listArts->item(i);
  481. controller.map()->allowedArtifact[i] = item->checkState() == Qt::Checked;
  482. }
  483. for(int i = 0; i < controller.map()->allowedHeroes.size(); ++i)
  484. {
  485. auto * item = ui->listHeroes->item(i);
  486. controller.map()->allowedHeroes[i] = item->checkState() == Qt::Checked;
  487. }
  488. //set difficulty
  489. if(ui->diffRadio1->isChecked()) controller.map()->difficulty = 0;
  490. if(ui->diffRadio2->isChecked()) controller.map()->difficulty = 1;
  491. if(ui->diffRadio3->isChecked()) controller.map()->difficulty = 2;
  492. if(ui->diffRadio4->isChecked()) controller.map()->difficulty = 3;
  493. if(ui->diffRadio5->isChecked()) controller.map()->difficulty = 4;
  494. //victory & loss messages
  495. controller.map()->victoryMessage = ui->victoryMessageEdit->text().toStdString();
  496. controller.map()->defeatMessage = ui->defeatMessageEdit->text().toStdString();
  497. //victory & loss conditions
  498. EventCondition victoryCondition(EventCondition::STANDARD_WIN);
  499. EventCondition defeatCondition(EventCondition::DAYS_WITHOUT_TOWN);
  500. defeatCondition.value = 7;
  501. //Victory condition - defeat all
  502. TriggeredEvent standardVictory;
  503. standardVictory.effect.type = EventEffect::VICTORY;
  504. standardVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[5];
  505. standardVictory.identifier = "standardVictory";
  506. standardVictory.description.clear(); // TODO: display in quest window
  507. standardVictory.onFulfill = VLC->generaltexth->allTexts[659];
  508. standardVictory.trigger = EventExpression(victoryCondition);
  509. //Loss condition - 7 days without town
  510. TriggeredEvent standardDefeat;
  511. standardDefeat.effect.type = EventEffect::DEFEAT;
  512. standardDefeat.effect.toOtherMessage = VLC->generaltexth->allTexts[8];
  513. standardDefeat.identifier = "standardDefeat";
  514. standardDefeat.description.clear(); // TODO: display in quest window
  515. standardDefeat.onFulfill = VLC->generaltexth->allTexts[7];
  516. standardDefeat.trigger = EventExpression(defeatCondition);
  517. controller.map()->triggeredEvents.clear();
  518. //VICTORY
  519. if(ui->victoryComboBox->currentIndex() == 0)
  520. {
  521. controller.map()->triggeredEvents.push_back(standardVictory);
  522. controller.map()->victoryIconIndex = 11;
  523. controller.map()->victoryMessage = VLC->generaltexth->victoryConditions[0];
  524. }
  525. else
  526. {
  527. int vicCondition = ui->victoryComboBox->currentIndex() - 1;
  528. TriggeredEvent specialVictory;
  529. specialVictory.effect.type = EventEffect::VICTORY;
  530. specialVictory.identifier = "specialVictory";
  531. specialVictory.description.clear(); // TODO: display in quest window
  532. controller.map()->victoryIconIndex = vicCondition;
  533. controller.map()->victoryMessage = VLC->generaltexth->victoryConditions[size_t(vicCondition) + 1];
  534. switch(vicCondition)
  535. {
  536. case 0: {
  537. EventCondition cond(EventCondition::HAVE_ARTIFACT);
  538. assert(victoryTypeWidget);
  539. cond.objectType = victoryTypeWidget->currentData().toInt();
  540. specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[281];
  541. specialVictory.onFulfill = VLC->generaltexth->allTexts[280];
  542. specialVictory.trigger = EventExpression(cond);
  543. break;
  544. }
  545. case 1: {
  546. EventCondition cond(EventCondition::HAVE_CREATURES);
  547. assert(victoryTypeWidget);
  548. cond.objectType = victoryTypeWidget->currentData().toInt();
  549. cond.value = victoryValueWidget->text().toInt();
  550. specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[277];
  551. specialVictory.onFulfill = VLC->generaltexth->allTexts[276];
  552. specialVictory.trigger = EventExpression(cond);
  553. break;
  554. }
  555. case 2: {
  556. EventCondition cond(EventCondition::HAVE_RESOURCES);
  557. assert(victoryTypeWidget);
  558. cond.objectType = victoryTypeWidget->currentData().toInt();
  559. cond.value = victoryValueWidget->text().toInt();
  560. specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[279];
  561. specialVictory.onFulfill = VLC->generaltexth->allTexts[278];
  562. specialVictory.trigger = EventExpression(cond);
  563. break;
  564. }
  565. case 3: {
  566. EventCondition cond(EventCondition::HAVE_BUILDING);
  567. assert(victoryTypeWidget);
  568. cond.objectType = victoryTypeWidget->currentData().toInt();
  569. int townIdx = victorySelectWidget->currentData().toInt();
  570. if(townIdx > -1)
  571. cond.position = controller.map()->objects[townIdx]->pos;
  572. specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[283];
  573. specialVictory.onFulfill = VLC->generaltexth->allTexts[282];
  574. specialVictory.trigger = EventExpression(cond);
  575. break;
  576. }
  577. case 4: {
  578. EventCondition cond(EventCondition::CONTROL);
  579. assert(victoryTypeWidget);
  580. cond.objectType = Obj::TOWN;
  581. int townIdx = victoryTypeWidget->currentData().toInt();
  582. cond.position = controller.map()->objects[townIdx]->pos;
  583. specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[250];
  584. specialVictory.onFulfill = VLC->generaltexth->allTexts[249];
  585. specialVictory.trigger = EventExpression(cond);
  586. break;
  587. }
  588. case 5: {
  589. EventCondition cond(EventCondition::DESTROY);
  590. assert(victoryTypeWidget);
  591. cond.objectType = Obj::HERO;
  592. int heroIdx = victoryTypeWidget->currentData().toInt();
  593. cond.position = controller.map()->objects[heroIdx]->pos;
  594. specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[253];
  595. specialVictory.onFulfill = VLC->generaltexth->allTexts[252];
  596. specialVictory.trigger = EventExpression(cond);
  597. break;
  598. }
  599. case 6: {
  600. EventCondition cond(EventCondition::TRANSPORT);
  601. assert(victoryTypeWidget);
  602. cond.objectType = victoryTypeWidget->currentData().toInt();
  603. int townIdx = victorySelectWidget->currentData().toInt();
  604. if(townIdx > -1)
  605. cond.position = controller.map()->objects[townIdx]->pos;
  606. specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[293];
  607. specialVictory.onFulfill = VLC->generaltexth->allTexts[292];
  608. specialVictory.trigger = EventExpression(cond);
  609. break;
  610. }
  611. }
  612. // if condition is human-only turn it into following construction: AllOf(human, condition)
  613. if(ui->onlyForHumansCheck->isChecked())
  614. {
  615. EventExpression::OperatorAll oper;
  616. EventCondition notAI(EventCondition::IS_HUMAN);
  617. notAI.value = 1;
  618. oper.expressions.push_back(notAI);
  619. oper.expressions.push_back(specialVictory.trigger.get());
  620. specialVictory.trigger = EventExpression(oper);
  621. }
  622. // if normal victory allowed - add one more quest
  623. if(ui->standardVictoryCheck->isChecked())
  624. {
  625. controller.map()->victoryMessage += " / ";
  626. controller.map()->victoryMessage += VLC->generaltexth->victoryConditions[0];
  627. controller.map()->triggeredEvents.push_back(standardVictory);
  628. }
  629. controller.map()->triggeredEvents.push_back(specialVictory);
  630. }
  631. //DEFEAT
  632. if(ui->loseComboBox->currentIndex() == 0)
  633. {
  634. controller.map()->triggeredEvents.push_back(standardDefeat);
  635. controller.map()->defeatIconIndex = 3;
  636. controller.map()->defeatMessage = VLC->generaltexth->lossCondtions[0];
  637. }
  638. else
  639. {
  640. int lossCondition = ui->loseComboBox->currentIndex() - 1;
  641. TriggeredEvent specialDefeat;
  642. specialDefeat.effect.type = EventEffect::DEFEAT;
  643. specialDefeat.identifier = "specialDefeat";
  644. specialDefeat.description.clear(); // TODO: display in quest window
  645. controller.map()->defeatIconIndex = lossCondition;
  646. controller.map()->defeatMessage = VLC->generaltexth->lossCondtions[size_t(lossCondition) + 1];
  647. switch(lossCondition)
  648. {
  649. case 0: {
  650. EventExpression::OperatorNone noneOf;
  651. EventCondition cond(EventCondition::CONTROL);
  652. cond.objectType = Obj::TOWN;
  653. assert(loseTypeWidget);
  654. int townIdx = loseTypeWidget->currentData().toInt();
  655. cond.position = controller.map()->objects[townIdx]->pos;
  656. noneOf.expressions.push_back(cond);
  657. specialDefeat.onFulfill = VLC->generaltexth->allTexts[251];
  658. specialDefeat.trigger = EventExpression(noneOf);
  659. break;
  660. }
  661. case 1: {
  662. EventExpression::OperatorNone noneOf;
  663. EventCondition cond(EventCondition::CONTROL);
  664. cond.objectType = Obj::HERO;
  665. assert(loseTypeWidget);
  666. int townIdx = loseTypeWidget->currentData().toInt();
  667. cond.position = controller.map()->objects[townIdx]->pos;
  668. noneOf.expressions.push_back(cond);
  669. specialDefeat.onFulfill = VLC->generaltexth->allTexts[253];
  670. specialDefeat.trigger = EventExpression(noneOf);
  671. break;
  672. }
  673. case 2: {
  674. EventCondition cond(EventCondition::DAYS_PASSED);
  675. assert(loseValueWidget);
  676. cond.value = expiredDate(loseValueWidget->text());
  677. specialDefeat.onFulfill = VLC->generaltexth->allTexts[254];
  678. specialDefeat.trigger = EventExpression(cond);
  679. break;
  680. }
  681. case 3: {
  682. EventCondition cond(EventCondition::DAYS_WITHOUT_TOWN);
  683. assert(loseValueWidget);
  684. cond.value = loseValueWidget->text().toInt();
  685. specialDefeat.onFulfill = VLC->generaltexth->allTexts[7];
  686. specialDefeat.trigger = EventExpression(cond);
  687. break;
  688. }
  689. }
  690. EventExpression::OperatorAll allOf;
  691. EventCondition isHuman(EventCondition::IS_HUMAN);
  692. isHuman.value = 1;
  693. allOf.expressions.push_back(specialDefeat.trigger.get());
  694. allOf.expressions.push_back(isHuman);
  695. specialDefeat.trigger = EventExpression(allOf);
  696. if(ui->standardLoseCheck->isChecked())
  697. {
  698. controller.map()->triggeredEvents.push_back(standardDefeat);
  699. }
  700. controller.map()->triggeredEvents.push_back(specialDefeat);
  701. }
  702. //Mod management
  703. auto widgetAction = [&](QTreeWidgetItem * item)
  704. {
  705. if(item->checkState(0) == Qt::Checked)
  706. {
  707. auto modName = item->data(0, Qt::UserRole).toString().toStdString();
  708. controller.map()->mods[modName] = VLC->modh->getModInfo(modName).version;
  709. }
  710. };
  711. controller.map()->mods.clear();
  712. for (int i = 0; i < ui->treeMods->topLevelItemCount(); ++i)
  713. {
  714. QTreeWidgetItem *item = ui->treeMods->topLevelItem(i);
  715. traverseNode(item, widgetAction);
  716. }
  717. close();
  718. }
  719. void MapSettings::on_victoryComboBox_currentIndexChanged(int index)
  720. {
  721. delete victoryTypeWidget;
  722. delete victoryValueWidget;
  723. delete victorySelectWidget;
  724. victoryTypeWidget = nullptr;
  725. victoryValueWidget = nullptr;
  726. victorySelectWidget = nullptr;
  727. if(index == 0)
  728. {
  729. ui->standardVictoryCheck->setChecked(true);
  730. ui->standardVictoryCheck->setEnabled(false);
  731. ui->onlyForHumansCheck->setChecked(false);
  732. ui->onlyForHumansCheck->setEnabled(false);
  733. return;
  734. }
  735. ui->onlyForHumansCheck->setEnabled(true);
  736. ui->standardVictoryCheck->setEnabled(true);
  737. int vicCondition = index - 1;
  738. switch(vicCondition)
  739. {
  740. case 0: { //EventCondition::HAVE_ARTIFACT
  741. victoryTypeWidget = new QComboBox;
  742. ui->victoryParamsLayout->addWidget(victoryTypeWidget);
  743. for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
  744. victoryTypeWidget->addItem(QString::fromStdString(VLC->arth->objects[i]->getNameTranslated()), QVariant::fromValue(i));
  745. break;
  746. }
  747. case 1: { //EventCondition::HAVE_CREATURES
  748. victoryTypeWidget = new QComboBox;
  749. ui->victoryParamsLayout->addWidget(victoryTypeWidget);
  750. for(int i = 0; i < VLC->creh->objects.size(); ++i)
  751. victoryTypeWidget->addItem(QString::fromStdString(VLC->creh->objects[i]->getNamePluralTranslated()), QVariant::fromValue(i));
  752. victoryValueWidget = new QLineEdit;
  753. ui->victoryParamsLayout->addWidget(victoryValueWidget);
  754. victoryValueWidget->setText("1");
  755. break;
  756. }
  757. case 2: { //EventCondition::HAVE_RESOURCES
  758. victoryTypeWidget = new QComboBox;
  759. ui->victoryParamsLayout->addWidget(victoryTypeWidget);
  760. {
  761. for(int resType = 0; resType < GameConstants::RESOURCE_QUANTITY; ++resType)
  762. {
  763. auto resName = QString::fromStdString(GameConstants::RESOURCE_NAMES[resType]);
  764. victoryTypeWidget->addItem(resName, QVariant::fromValue(resType));
  765. }
  766. }
  767. victoryValueWidget = new QLineEdit;
  768. ui->victoryParamsLayout->addWidget(victoryValueWidget);
  769. victoryValueWidget->setText("1");
  770. break;
  771. }
  772. case 3: { //EventCondition::HAVE_BUILDING
  773. victoryTypeWidget = new QComboBox;
  774. ui->victoryParamsLayout->addWidget(victoryTypeWidget);
  775. auto * ctown = VLC->townh->randomTown;
  776. for(int bId : ctown->getAllBuildings())
  777. victoryTypeWidget->addItem(QString::fromStdString(defaultBuildingIdConversion(BuildingID(bId))), QVariant::fromValue(bId));
  778. victorySelectWidget = new QComboBox;
  779. ui->victoryParamsLayout->addWidget(victorySelectWidget);
  780. victorySelectWidget->addItem("Any town", QVariant::fromValue(-1));
  781. for(int i : getObjectIndexes<CGTownInstance>())
  782. victorySelectWidget->addItem(getTownName(i).c_str(), QVariant::fromValue(i));
  783. break;
  784. }
  785. case 4: { //EventCondition::CONTROL (Obj::TOWN)
  786. victoryTypeWidget = new QComboBox;
  787. ui->victoryParamsLayout->addWidget(victoryTypeWidget);
  788. for(int i : getObjectIndexes<CGTownInstance>())
  789. victoryTypeWidget->addItem(tr(getTownName(i).c_str()), QVariant::fromValue(i));
  790. break;
  791. }
  792. case 5: { //EventCondition::DESTROY (Obj::HERO)
  793. victoryTypeWidget = new QComboBox;
  794. ui->victoryParamsLayout->addWidget(victoryTypeWidget);
  795. for(int i : getObjectIndexes<CGHeroInstance>())
  796. victoryTypeWidget->addItem(tr(getHeroName(i).c_str()), QVariant::fromValue(i));
  797. break;
  798. }
  799. case 6: { //EventCondition::TRANSPORT (Obj::ARTEFACT)
  800. victoryTypeWidget = new QComboBox;
  801. ui->victoryParamsLayout->addWidget(victoryTypeWidget);
  802. for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
  803. victoryTypeWidget->addItem(QString::fromStdString(VLC->arth->objects[i]->getNameTranslated()), QVariant::fromValue(i));
  804. victorySelectWidget = new QComboBox;
  805. ui->victoryParamsLayout->addWidget(victorySelectWidget);
  806. for(int i : getObjectIndexes<CGTownInstance>())
  807. victorySelectWidget->addItem(tr(getTownName(i).c_str()), QVariant::fromValue(i));
  808. break;
  809. }
  810. //TODO: support this vectory type
  811. // in order to do that, need to implement finding creature by position
  812. // selecting from map would be the best user experience
  813. /*case 7: { //EventCondition::DESTROY (Obj::MONSTER)
  814. victoryTypeWidget = new QComboBox;
  815. ui->loseParamsLayout->addWidget(victoryTypeWidget);
  816. for(int i : getObjectIndexes<CGCreature>())
  817. victoryTypeWidget->addItem(tr(getMonsterName(i).c_str()), QVariant::fromValue(i));
  818. break;
  819. }*/
  820. }
  821. }
  822. void MapSettings::on_loseComboBox_currentIndexChanged(int index)
  823. {
  824. delete loseTypeWidget;
  825. delete loseValueWidget;
  826. delete loseSelectWidget;
  827. loseTypeWidget = nullptr;
  828. loseValueWidget = nullptr;
  829. loseSelectWidget = nullptr;
  830. if(index == 0)
  831. {
  832. ui->standardLoseCheck->setChecked(true);
  833. ui->standardLoseCheck->setEnabled(false);
  834. return;
  835. }
  836. ui->standardLoseCheck->setEnabled(true);
  837. int loseCondition = index - 1;
  838. switch(loseCondition)
  839. {
  840. case 0: { //EventCondition::CONTROL (Obj::TOWN)
  841. loseTypeWidget = new QComboBox;
  842. ui->loseParamsLayout->addWidget(loseTypeWidget);
  843. for(int i : getObjectIndexes<CGTownInstance>())
  844. loseTypeWidget->addItem(tr(getTownName(i).c_str()), QVariant::fromValue(i));
  845. break;
  846. }
  847. case 1: { //EventCondition::CONTROL (Obj::HERO)
  848. loseTypeWidget = new QComboBox;
  849. ui->loseParamsLayout->addWidget(loseTypeWidget);
  850. for(int i : getObjectIndexes<CGHeroInstance>())
  851. loseTypeWidget->addItem(tr(getHeroName(i).c_str()), QVariant::fromValue(i));
  852. break;
  853. }
  854. case 2: { //EventCondition::DAYS_PASSED
  855. loseValueWidget = new QLineEdit;
  856. ui->loseParamsLayout->addWidget(loseValueWidget);
  857. loseValueWidget->setText("2m 1w 1d");
  858. break;
  859. }
  860. case 3: { //EventCondition::DAYS_WITHOUT_TOWN
  861. loseValueWidget = new QLineEdit;
  862. ui->loseParamsLayout->addWidget(loseValueWidget);
  863. loseValueWidget->setText("7");
  864. break;
  865. }
  866. }
  867. }
  868. void MapSettings::on_heroLevelLimitCheck_toggled(bool checked)
  869. {
  870. ui->heroLevelLimit->setEnabled(checked);
  871. }
  872. void MapSettings::on_modResolution_map_clicked()
  873. {
  874. updateModWidgetBasedOnMods(MapController::modAssessmentMap(*controller.map()));
  875. }
  876. void MapSettings::on_modResolution_full_clicked()
  877. {
  878. updateModWidgetBasedOnMods(MapController::modAssessmentAll());
  879. }
  880. void MapSettings::on_treeMods_itemChanged(QTreeWidgetItem *item, int column)
  881. {
  882. //set state for children
  883. for (int i = 0; i < item->childCount(); ++i)
  884. item->child(i)->setCheckState(0, item->checkState(0));
  885. //set state for parent
  886. ui->treeMods->blockSignals(true);
  887. if(item->checkState(0) == Qt::Checked)
  888. {
  889. while(item->parent())
  890. {
  891. item->parent()->setCheckState(0, Qt::Checked);
  892. item = item->parent();
  893. }
  894. }
  895. ui->treeMods->blockSignals(false);
  896. }