CArmedInstance.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /*
  2. * CArmedInstance.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 "CArmedInstance.h"
  12. #include "../CTownHandler.h"
  13. #include "../CCreatureHandler.h"
  14. #include "../CGeneralTextHandler.h"
  15. #include "../gameState/CGameState.h"
  16. #include "../CPlayerState.h"
  17. VCMI_LIB_NAMESPACE_BEGIN
  18. void CArmedInstance::randomizeArmy(FactionID type)
  19. {
  20. for (auto & elem : stacks)
  21. {
  22. if(elem.second->randomStack)
  23. {
  24. int level = elem.second->randomStack->level;
  25. int upgrade = elem.second->randomStack->upgrade;
  26. elem.second->setType((*VLC->townh)[type]->town->creatures[level][upgrade]);
  27. elem.second->randomStack = std::nullopt;
  28. }
  29. assert(elem.second->valid(false));
  30. assert(elem.second->armyObj == this);
  31. }
  32. }
  33. // Take Angelic Alliance troop-mixing freedom of non-evil units into account.
  34. CSelector CArmedInstance::nonEvilAlignmentMixSelector = Selector::type()(BonusType::NONEVIL_ALIGNMENT_MIX);
  35. CArmedInstance::CArmedInstance()
  36. :CArmedInstance(false)
  37. {
  38. }
  39. CArmedInstance::CArmedInstance(bool isHypotetic):
  40. CBonusSystemNode(isHypotetic),
  41. nonEvilAlignmentMix(this, nonEvilAlignmentMixSelector),
  42. battle(nullptr)
  43. {
  44. }
  45. void CArmedInstance::updateMoraleBonusFromArmy()
  46. {
  47. if(!validTypes(false)) //object not randomized, don't bother
  48. return;
  49. auto b = getExportedBonusList().getFirst(Selector::sourceType()(BonusSource::ARMY).And(Selector::type()(BonusType::MORALE)));
  50. if(!b)
  51. {
  52. b = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, 0, BonusSourceID());
  53. addNewBonus(b);
  54. }
  55. //number of alignments and presence of undead
  56. std::set<FactionID> factions;
  57. bool hasUndead = false;
  58. const std::string undeadCacheKey = "type_UNDEAD";
  59. static const CSelector undeadSelector = Selector::type()(BonusType::UNDEAD);
  60. for(const auto & slot : Slots())
  61. {
  62. const CStackInstance * inst = slot.second;
  63. const auto * creature = inst->getCreatureID().toEntity(VLC);
  64. factions.insert(creature->getFaction());
  65. // Check for undead flag instead of faction (undead mummies are neutral)
  66. if (!hasUndead)
  67. {
  68. //this is costly check, let's skip it at first undead
  69. hasUndead |= inst->hasBonus(undeadSelector, undeadCacheKey);
  70. }
  71. }
  72. size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account
  73. if (nonEvilAlignmentMix.getHasBonus())
  74. {
  75. size_t mixableFactions = 0;
  76. for(auto f : factions)
  77. {
  78. if (VLC->factions()->getById(f)->getAlignment() != EAlignment::EVIL)
  79. mixableFactions++;
  80. }
  81. if (mixableFactions > 0)
  82. factionsInArmy -= mixableFactions - 1;
  83. }
  84. std::string description;
  85. if(factionsInArmy == 1)
  86. {
  87. b->val = +1;
  88. description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
  89. description = description.substr(0, description.size()-3);//trim "+1"
  90. }
  91. else if (!factions.empty()) // no bonus from empty garrison
  92. {
  93. b->val = 2 - static_cast<si32>(factionsInArmy);
  94. description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d
  95. description = description.substr(0, description.size()-3);//trim value
  96. }
  97. boost::algorithm::trim(description);
  98. b->description = description;
  99. CBonusSystemNode::treeHasChanged();
  100. //-1 modifier for any Undead unit in army
  101. auto undeadModifier = getExportedBonusList().getFirst(Selector::source(BonusSource::ARMY, BonusCustomSource::undeadMoraleDebuff));
  102. if(hasUndead)
  103. {
  104. if(!undeadModifier)
  105. {
  106. undeadModifier = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, -1, BonusCustomSource::undeadMoraleDebuff, VLC->generaltexth->arraytxt[116]);
  107. undeadModifier->description = undeadModifier->description.substr(0, undeadModifier->description.size()-2);//trim value
  108. addNewBonus(undeadModifier);
  109. }
  110. }
  111. else if(undeadModifier)
  112. removeBonus(undeadModifier);
  113. }
  114. void CArmedInstance::armyChanged()
  115. {
  116. updateMoraleBonusFromArmy();
  117. }
  118. CBonusSystemNode & CArmedInstance::whereShouldBeAttached(CGameState * gs)
  119. {
  120. if(tempOwner.isValidPlayer())
  121. if(auto * where = gs->getPlayerState(tempOwner))
  122. return *where;
  123. return gs->globalEffects;
  124. }
  125. CBonusSystemNode & CArmedInstance::whatShouldBeAttached()
  126. {
  127. return *this;
  128. }
  129. const IBonusBearer* CArmedInstance::getBonusBearer() const
  130. {
  131. return this;
  132. }
  133. void CArmedInstance::serializeJsonOptions(JsonSerializeFormat & handler)
  134. {
  135. CGObjectInstance::serializeJsonOptions(handler);
  136. CCreatureSet::serializeJson(handler, "army", 7);
  137. }
  138. VCMI_LIB_NAMESPACE_END