startingbonus.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. * startingbonus.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 "startingbonus.h"
  12. #include "ui_startingbonus.h"
  13. #include "../../lib/mapObjects/CGHeroInstance.h"
  14. #include "../../lib/mapObjects/CGTownInstance.h"
  15. #include "../../lib/campaign/CampaignState.h"
  16. #include "../../lib/entities/artifact/CArtHandler.h"
  17. #include "../../lib/entities/building/CBuilding.h"
  18. #include "../../lib/entities/hero/CHero.h"
  19. #include "../../lib/mapping/CMap.h"
  20. #include "../../lib/CSkillHandler.h"
  21. #include "../../lib/CCreatureHandler.h"
  22. #include "../../lib/spells/CSpellHandler.h"
  23. #include "../../lib/texts/CGeneralTextHandler.h"
  24. StartingBonus::StartingBonus(PlayerColor color, std::shared_ptr<CMap> map, CampaignBonus bonus):
  25. ui(new Ui::StartingBonus),
  26. bonus(bonus),
  27. map(map),
  28. color(color)
  29. {
  30. ui->setupUi(this);
  31. setWindowTitle(tr("Edit Starting Bonus"));
  32. setWindowModality(Qt::ApplicationModal);
  33. ui->tabWidget->tabBar()->hide();
  34. ui->tabWidget->setStyleSheet("QTabWidget::pane { border: 0; }");
  35. initControls();
  36. loadBonus();
  37. show();
  38. }
  39. StartingBonus::~StartingBonus()
  40. {
  41. delete ui;
  42. }
  43. void StartingBonus::initControls()
  44. {
  45. std::map<int, std::string> heroSelection;
  46. for(ObjectInstanceID heroID : map->getHeroesOnMap())
  47. {
  48. const auto * hero = dynamic_cast<const CGHeroInstance*>(map->objects.at(heroID.getNum()).get());
  49. if(hero->getOwner() == color || color == PlayerColor::CANNOT_DETERMINE)
  50. heroSelection.emplace(hero->getHeroTypeID(), hero->getNameTranslated());
  51. }
  52. heroSelection.emplace(HeroTypeID::CAMP_STRONGEST, tr("Strongest").toStdString());
  53. heroSelection.emplace(HeroTypeID::CAMP_GENERATED, tr("Generated").toStdString());
  54. heroSelection.emplace(HeroTypeID::CAMP_RANDOM, tr("Random").toStdString());
  55. for(auto & hero : heroSelection)
  56. {
  57. ui->comboBoxSpellRecipient->addItem(QString::fromStdString(hero.second), QVariant(hero.first));
  58. ui->comboBoxCreatureRecipient->addItem(QString::fromStdString(hero.second), QVariant(hero.first));
  59. ui->comboBoxArtifactRecipient->addItem(QString::fromStdString(hero.second), QVariant(hero.first));
  60. ui->comboBoxSpellScrollRecipient->addItem(QString::fromStdString(hero.second), QVariant(hero.first));
  61. ui->comboBoxPrimarySkillRecipient->addItem(QString::fromStdString(hero.second), QVariant(hero.first));
  62. ui->comboBoxSecondarySkillRecipient->addItem(QString::fromStdString(hero.second), QVariant(hero.first));
  63. }
  64. for(auto const & objectPtr : LIBRARY->spellh->objects)
  65. {
  66. ui->comboBoxSpellSpell->addItem(QString::fromStdString(objectPtr->getNameTranslated()), QVariant(objectPtr->getId()));
  67. ui->comboBoxSpellScrollSpell->addItem(QString::fromStdString(objectPtr->getNameTranslated()), QVariant(objectPtr->getId()));
  68. }
  69. for(auto const & objectPtr : LIBRARY->creh->objects)
  70. ui->comboBoxCreatureCreatureType->addItem(QString::fromStdString(objectPtr->getNameSingularTranslated()), QVariant(objectPtr->getId()));
  71. if(map->players[color].hasMainTown)
  72. {
  73. for(ObjectInstanceID townID : map->getAllTowns())
  74. {
  75. const auto * town = dynamic_cast<const CGTownInstance*>(map->objects.at(townID.getNum()).get());
  76. if(town->pos == map->players[color].posOfMainTown)
  77. {
  78. if(town->getTown()->faction->getId() == FactionID::NEUTRAL)
  79. {
  80. ui->comboBoxBuildingBuilding->addItem(tr("Main town is of random faction"), QVariant(BuildingID::NONE));
  81. ui->comboBoxBuildingBuilding->setEnabled(false);
  82. break;
  83. }
  84. for(auto & building : town->getTown()->buildings)
  85. ui->comboBoxBuildingBuilding->addItem(QString::fromStdString(building.second->getNameTranslated()), QVariant(building.first.getNum()));
  86. break;
  87. }
  88. }
  89. }
  90. else
  91. {
  92. ui->comboBoxBuildingBuilding->addItem(tr("Player does not have a main town!"), QVariant(BuildingID::NONE));
  93. ui->comboBoxBuildingBuilding->setEnabled(false);
  94. }
  95. for(auto const & objectPtr : LIBRARY->arth->objects)
  96. if(!vstd::contains(std::vector<ArtifactID>({
  97. ArtifactID::SPELL_SCROLL,
  98. ArtifactID::SPELLBOOK,
  99. ArtifactID::GRAIL,
  100. ArtifactID::AMMO_CART,
  101. ArtifactID::BALLISTA,
  102. ArtifactID::CATAPULT,
  103. ArtifactID::FIRST_AID_TENT,
  104. }), objectPtr->getId()))
  105. ui->comboBoxArtifactArtifact->addItem(QString::fromStdString(objectPtr->getNameTranslated()), QVariant(objectPtr->getId()));
  106. for(auto const & objectPtr : LIBRARY->skillh->objects)
  107. ui->comboBoxSecondarySkillSecondarySkill->addItem(QString::fromStdString(objectPtr->getNameTranslated()), QVariant(objectPtr->getId()));
  108. for(int i = 0; i < 3; i++) // Basic, Advanced, Expert
  109. ui->comboBoxSecondarySkillMastery->addItem(QString::fromStdString(LIBRARY->generaltexth->translate("core.skilllev", i)), QVariant(i));
  110. for(auto & res : std::vector<EGameResID>({EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS, EGameResID::GOLD}))
  111. ui->comboBoxResourceResourceType->addItem(QString::fromStdString(MetaString::createFromName(res).toString()), QVariant(res));
  112. ui->comboBoxResourceResourceType->addItem(
  113. tr("Common (%1 and %2)")
  114. .arg(QString::fromStdString(MetaString::createFromName(EGameResID::WOOD).toString()))
  115. .arg(QString::fromStdString(MetaString::createFromName(EGameResID::ORE).toString()))
  116. , QVariant(EGameResID::COMMON));
  117. ui->comboBoxResourceResourceType->addItem(
  118. tr("Rare (%1, %2, %3, %4)")
  119. .arg(QString::fromStdString(MetaString::createFromName(EGameResID::MERCURY).toString()))
  120. .arg(QString::fromStdString(MetaString::createFromName(EGameResID::SULFUR).toString()))
  121. .arg(QString::fromStdString(MetaString::createFromName(EGameResID::CRYSTAL).toString()))
  122. .arg(QString::fromStdString(MetaString::createFromName(EGameResID::GEMS).toString()))
  123. , QVariant(EGameResID::RARE));
  124. }
  125. void StartingBonus::loadBonus()
  126. {
  127. auto setComboBoxValue = [](QComboBox * comboBox, QVariant data){
  128. int index = comboBox->findData(data);
  129. if(index != -1)
  130. comboBox->setCurrentIndex(index);
  131. };
  132. switch(bonus.getType())
  133. {
  134. case CampaignBonusType::SPELL:
  135. {
  136. const auto & bonusValue = bonus.getValue<CampaignBonusSpell>();
  137. ui->radioButtonSpell->setChecked(true);
  138. on_radioButtonSpell_toggled();
  139. setComboBoxValue(ui->comboBoxSpellRecipient, bonusValue.hero.getNum());
  140. setComboBoxValue(ui->comboBoxSpellSpell, bonusValue.spell.getNum());
  141. break;
  142. }
  143. case CampaignBonusType::MONSTER:
  144. {
  145. const auto & bonusValue = bonus.getValue<CampaignBonusCreatures>();
  146. ui->radioButtonCreature->setChecked(true);
  147. on_radioButtonCreature_toggled();
  148. setComboBoxValue(ui->comboBoxCreatureRecipient, bonusValue.hero.getNum());
  149. setComboBoxValue(ui->comboBoxCreatureCreatureType, bonusValue.creature.getNum());
  150. ui->spinBoxCreatureQuantity->setValue(bonusValue.amount);
  151. break;
  152. }
  153. case CampaignBonusType::BUILDING:
  154. {
  155. if(ui->comboBoxBuildingBuilding->currentData().toInt() == BuildingID::NONE)
  156. break;
  157. const auto & bonusValue = bonus.getValue<CampaignBonusBuilding>();
  158. ui->radioButtonBuilding->setChecked(true);
  159. on_radioButtonBuilding_toggled();
  160. setComboBoxValue(ui->comboBoxBuildingBuilding, bonusValue.buildingDecoded.getNum());
  161. break;
  162. }
  163. case CampaignBonusType::ARTIFACT:
  164. {
  165. const auto & bonusValue = bonus.getValue<CampaignBonusArtifact>();
  166. ui->radioButtonArtifact->setChecked(true);
  167. on_radioButtonArtifact_toggled();
  168. setComboBoxValue(ui->comboBoxArtifactRecipient, bonusValue.hero.getNum());
  169. setComboBoxValue(ui->comboBoxArtifactArtifact, bonusValue.artifact.getNum());
  170. break;
  171. }
  172. case CampaignBonusType::SPELL_SCROLL:
  173. {
  174. const auto & bonusValue = bonus.getValue<CampaignBonusSpellScroll>();
  175. ui->radioButtonSpellScroll->setChecked(true);
  176. on_radioButtonSpellScroll_toggled();
  177. setComboBoxValue(ui->comboBoxSpellScrollRecipient, bonusValue.hero.getNum());
  178. setComboBoxValue(ui->comboBoxSpellScrollSpell, bonusValue.spell.getNum());
  179. break;
  180. }
  181. case CampaignBonusType::PRIMARY_SKILL:
  182. {
  183. const auto & bonusValue = bonus.getValue<CampaignBonusPrimarySkill>();
  184. ui->radioButtonPrimarySkill->setChecked(true);
  185. on_radioButtonPrimarySkill_toggled();
  186. setComboBoxValue(ui->comboBoxPrimarySkillRecipient, bonusValue.hero.getNum());
  187. ui->spinBoxPrimarySkillAttack->setValue(bonusValue.amounts[0]);
  188. ui->spinBoxPrimarySkillDefense->setValue(bonusValue.amounts[0]);
  189. ui->spinBoxPrimarySkillSpell->setValue(bonusValue.amounts[0]);
  190. ui->spinBoxPrimarySkillKnowledge->setValue(bonusValue.amounts[0]);
  191. break;
  192. }
  193. case CampaignBonusType::SECONDARY_SKILL:
  194. {
  195. const auto & bonusValue = bonus.getValue<CampaignBonusSecondarySkill>();
  196. ui->radioButtonSecondarySkill->setChecked(true);
  197. on_radioButtonSecondarySkill_toggled();
  198. setComboBoxValue(ui->comboBoxSecondarySkillRecipient, bonusValue.hero.getNum());
  199. setComboBoxValue(ui->comboBoxSecondarySkillSecondarySkill, bonusValue.skill.getNum());
  200. setComboBoxValue(ui->comboBoxSecondarySkillMastery, bonusValue.mastery);
  201. break;
  202. }
  203. case CampaignBonusType::RESOURCE:
  204. {
  205. const auto & bonusValue = bonus.getValue<CampaignBonusStartingResources>();
  206. ui->radioButtonResource->setChecked(true);
  207. on_radioButtonResource_toggled();
  208. setComboBoxValue(ui->comboBoxResourceResourceType, bonusValue.resource.getNum());
  209. ui->spinBoxResourceQuantity->setValue(bonusValue.amount);
  210. break;
  211. }
  212. default:
  213. break;
  214. }
  215. }
  216. void StartingBonus::saveBonus()
  217. {
  218. if(ui->radioButtonSpell->isChecked())
  219. bonus = CampaignBonusSpell{
  220. HeroTypeID(ui->comboBoxSpellRecipient->currentData().toInt()),
  221. SpellID(ui->comboBoxSpellSpell->currentData().toInt())
  222. };
  223. else if(ui->radioButtonCreature->isChecked())
  224. bonus = CampaignBonusCreatures{
  225. HeroTypeID(ui->comboBoxCreatureRecipient->currentData().toInt()),
  226. CreatureID(ui->comboBoxCreatureCreatureType->currentData().toInt()),
  227. int32_t(ui->spinBoxCreatureQuantity->value())
  228. };
  229. else if(ui->radioButtonBuilding->isChecked())
  230. bonus = CampaignBonusBuilding{
  231. BuildingID{},
  232. BuildingID(ui->comboBoxBuildingBuilding->currentData().toInt())
  233. };
  234. else if(ui->radioButtonArtifact->isChecked())
  235. bonus = CampaignBonusArtifact{
  236. HeroTypeID(ui->comboBoxArtifactRecipient->currentData().toInt()),
  237. ArtifactID(ui->comboBoxArtifactArtifact->currentData().toInt())
  238. };
  239. else if(ui->radioButtonSpellScroll->isChecked())
  240. bonus = CampaignBonusSpellScroll{
  241. HeroTypeID(ui->comboBoxSpellScrollRecipient->currentData().toInt()),
  242. SpellID(ui->comboBoxSpellScrollSpell->currentData().toInt())
  243. };
  244. else if(ui->radioButtonPrimarySkill->isChecked())
  245. bonus = CampaignBonusPrimarySkill{
  246. HeroTypeID(ui->comboBoxPrimarySkillRecipient->currentData().toInt()),
  247. {
  248. uint8_t(ui->spinBoxPrimarySkillAttack->value()),
  249. uint8_t(ui->spinBoxPrimarySkillDefense->value()),
  250. uint8_t(ui->spinBoxPrimarySkillSpell->value()),
  251. uint8_t(ui->spinBoxPrimarySkillKnowledge->value()),
  252. }
  253. };
  254. else if(ui->radioButtonSecondarySkill->isChecked())
  255. bonus = CampaignBonusSecondarySkill{
  256. HeroTypeID(ui->comboBoxSecondarySkillRecipient->currentData().toInt()),
  257. SecondarySkill(ui->comboBoxSecondarySkillSecondarySkill->currentData().toInt()),
  258. int32_t(ui->comboBoxSecondarySkillMastery->currentData().toInt())
  259. };
  260. else if(ui->radioButtonResource->isChecked())
  261. bonus = CampaignBonusStartingResources{
  262. GameResID(ui->comboBoxResourceResourceType->currentData().toInt()),
  263. int32_t(ui->spinBoxResourceQuantity->value())
  264. };
  265. }
  266. void StartingBonus::on_buttonBox_clicked(QAbstractButton * button)
  267. {
  268. if(button == ui->buttonBox->button(QDialogButtonBox::Ok))
  269. {
  270. saveBonus();
  271. accept();
  272. }
  273. close();
  274. }
  275. bool StartingBonus::showStartingBonus(PlayerColor color, std::shared_ptr<CMap> map, CampaignBonus & bonus)
  276. {
  277. auto * dialog = new StartingBonus(color, map, bonus);
  278. dialog->setAttribute(Qt::WA_DeleteOnClose);
  279. auto result = dialog->exec();
  280. if(result == QDialog::Accepted)
  281. bonus = dialog->bonus;
  282. return result == QDialog::Accepted;
  283. }
  284. QString StartingBonus::getBonusListTitle(CampaignBonus bonus, std::shared_ptr<CMap> map)
  285. {
  286. auto getHeroName = [map](int id){
  287. MetaString tmp;
  288. if(id == HeroTypeID::CAMP_STRONGEST)
  289. tmp.appendRawString(tr("strongest hero").toStdString());
  290. else if(id == HeroTypeID::CAMP_GENERATED)
  291. tmp.appendRawString(tr("generated hero").toStdString());
  292. else if(id == HeroTypeID::CAMP_RANDOM)
  293. tmp.appendRawString(tr("random hero").toStdString());
  294. else
  295. {
  296. for(auto o : map->objects)
  297. if(auto * ins = dynamic_cast<CGHeroInstance *>(o.get()))
  298. if(ins->getHeroTypeID().getNum() == id)
  299. tmp.appendTextID(ins->getNameTextID());
  300. }
  301. return QString::fromStdString(tmp.toString());
  302. };
  303. auto getSpellName = [](SpellID id){
  304. MetaString tmp;
  305. tmp.appendName(id);
  306. return QString::fromStdString(tmp.toString());
  307. };
  308. auto getMonsterName = [](CreatureID id, int amount){
  309. MetaString tmp;
  310. tmp.appendName(id, amount);
  311. return QString::fromStdString(tmp.toString());
  312. };
  313. auto getArtifactName = [](ArtifactID id){
  314. MetaString tmp;
  315. tmp.appendName(id);
  316. return QString::fromStdString(tmp.toString());
  317. };
  318. switch(bonus.getType())
  319. {
  320. case CampaignBonusType::SPELL:
  321. {
  322. const auto & bonusValue = bonus.getValue<CampaignBonusSpell>();
  323. return tr("%1 spell for %2").arg(getSpellName(bonusValue.spell)).arg(getHeroName(bonusValue.hero));
  324. }
  325. case CampaignBonusType::MONSTER:
  326. {
  327. const auto & bonusValue = bonus.getValue<CampaignBonusCreatures>();
  328. return tr("%1 %2 for %3").arg(bonusValue.amount).arg(getMonsterName(bonusValue.creature, bonusValue.amount)).arg(getHeroName(bonusValue.hero));
  329. }
  330. case CampaignBonusType::BUILDING:
  331. {
  332. return tr("Building");
  333. }
  334. case CampaignBonusType::ARTIFACT:
  335. {
  336. const auto & bonusValue = bonus.getValue<CampaignBonusArtifact>();
  337. return tr("%1 artifact for %2").arg(getArtifactName(bonusValue.artifact)).arg(getHeroName(bonusValue.hero));
  338. }
  339. case CampaignBonusType::SPELL_SCROLL:
  340. {
  341. const auto & bonusValue = bonus.getValue<CampaignBonusSpellScroll>();
  342. return tr("%1 spell scroll for %2").arg(getSpellName(bonusValue.spell)).arg(getHeroName(bonusValue.hero));
  343. }
  344. case CampaignBonusType::PRIMARY_SKILL:
  345. {
  346. const auto & bonusValue = bonus.getValue<CampaignBonusPrimarySkill>();
  347. return tr("Primary skill (Attack: %1, Defense: %2, Spell: %3, Knowledge: %4) for %5")
  348. .arg(bonusValue.amounts[0])
  349. .arg(bonusValue.amounts[1])
  350. .arg(bonusValue.amounts[2])
  351. .arg(bonusValue.amounts[3])
  352. .arg(getHeroName(bonusValue.hero));
  353. }
  354. case CampaignBonusType::SECONDARY_SKILL:
  355. {
  356. return tr("Secondary skill");
  357. }
  358. case CampaignBonusType::RESOURCE:
  359. {
  360. return tr("Resource");
  361. }
  362. }
  363. return {};
  364. }
  365. void StartingBonus::radioButtonToggled(int index)
  366. {
  367. if(index == 2 && ui->comboBoxBuildingBuilding->currentData().toInt() == BuildingID::NONE)
  368. ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
  369. else
  370. ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
  371. ui->tabWidget->setCurrentIndex(index);
  372. }
  373. void StartingBonus::on_radioButtonSpell_toggled()
  374. {
  375. radioButtonToggled(0);
  376. }
  377. void StartingBonus::on_radioButtonCreature_toggled()
  378. {
  379. radioButtonToggled(1);
  380. }
  381. void StartingBonus::on_radioButtonBuilding_toggled()
  382. {
  383. radioButtonToggled(2);
  384. }
  385. void StartingBonus::on_radioButtonArtifact_toggled()
  386. {
  387. radioButtonToggled(3);
  388. }
  389. void StartingBonus::on_radioButtonSpellScroll_toggled()
  390. {
  391. radioButtonToggled(4);
  392. }
  393. void StartingBonus::on_radioButtonPrimarySkill_toggled()
  394. {
  395. radioButtonToggled(5);
  396. }
  397. void StartingBonus::on_radioButtonSecondarySkill_toggled()
  398. {
  399. radioButtonToggled(6);
  400. }
  401. void StartingBonus::on_radioButtonResource_toggled()
  402. {
  403. radioButtonToggled(7);
  404. }