CArmedInstance.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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 "../CCreatureHandler.h"
  13. #include "../CPlayerState.h"
  14. #include "../callback/IGameInfoCallback.h"
  15. #include "../entities/faction/CFaction.h"
  16. #include "../entities/faction/CTown.h"
  17. #include "../entities/faction/CTownHandler.h"
  18. #include "../GameLibrary.h"
  19. #include "../gameState/CGameState.h"
  20. #include "../mapping/CMapDefines.h"
  21. #include "../texts/CGeneralTextHandler.h"
  22. VCMI_LIB_NAMESPACE_BEGIN
  23. void CArmedInstance::randomizeArmy(FactionID type)
  24. {
  25. for (auto & elem : stacks)
  26. {
  27. if(elem.second->randomStack)
  28. {
  29. int level = elem.second->randomStack->level;
  30. int upgrade = elem.second->randomStack->upgrade;
  31. elem.second->setType((*LIBRARY->townh)[type]->town->creatures[level][upgrade]);
  32. elem.second->randomStack = std::nullopt;
  33. }
  34. assert(elem.second->valid(false));
  35. assert(elem.second->getArmy() == this);
  36. }
  37. }
  38. CArmedInstance::CArmedInstance(IGameInfoCallback *cb)
  39. :CArmedInstance(cb, false)
  40. {
  41. }
  42. CArmedInstance::CArmedInstance(IGameInfoCallback *cb, bool isHypothetic):
  43. CGObjectInstance(cb),
  44. CBonusSystemNode(isHypothetic),
  45. nonEvilAlignmentMix(this, Selector::type()(BonusType::NONEVIL_ALIGNMENT_MIX)), // Take Angelic Alliance troop-mixing freedom of non-evil units into account.
  46. battle(nullptr)
  47. {
  48. }
  49. void CArmedInstance::updateMoraleBonusFromArmy()
  50. {
  51. if(!validTypes(false)) //object not randomized, don't bother
  52. return;
  53. auto b = getExportedBonusList().getFirst(Selector::sourceType()(BonusSource::ARMY).And(Selector::type()(BonusType::MORALE)));
  54. if(!b)
  55. {
  56. b = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, 0, BonusSourceID());
  57. addNewBonus(b);
  58. }
  59. //number of alignments and presence of undead
  60. std::set<FactionID> factions;
  61. bool hasUndead = false;
  62. const std::string undeadCacheKey = "type_UNDEAD";
  63. static const CSelector undeadSelector = Selector::type()(BonusType::UNDEAD);
  64. for(const auto & slot : Slots())
  65. {
  66. const auto * creature = slot.second->getCreatureID().toEntity(LIBRARY);
  67. factions.insert(creature->getFactionID());
  68. // Check for undead flag instead of faction (undead mummies are neutral)
  69. if (!hasUndead)
  70. {
  71. //this is costly check, let's skip it at first undead
  72. hasUndead |= slot.second->hasBonus(undeadSelector, undeadCacheKey);
  73. }
  74. }
  75. size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account
  76. if (nonEvilAlignmentMix.hasBonus())
  77. {
  78. size_t mixableFactions = 0;
  79. for(auto f : factions)
  80. {
  81. if (LIBRARY->factions()->getById(f)->getAlignment() != EAlignment::EVIL)
  82. mixableFactions++;
  83. }
  84. if (mixableFactions > 0)
  85. factionsInArmy -= mixableFactions - 1;
  86. }
  87. MetaString bonusDescription;
  88. if(factionsInArmy == 1)
  89. {
  90. b->val = +1;
  91. bonusDescription.appendTextID("core.arraytxt.115"); //All troops of one alignment +1
  92. }
  93. else if (!factions.empty()) // no bonus from empty garrison
  94. {
  95. b->val = 2 - static_cast<si32>(factionsInArmy);
  96. bonusDescription.appendTextID("core.arraytxt.114"); //Troops of %d alignments %d
  97. bonusDescription.replaceNumber(factionsInArmy);
  98. }
  99. b->description = bonusDescription;
  100. nodeHasChanged();
  101. //-1 modifier for any Undead unit in army
  102. auto undeadModifier = getExportedBonusList().getFirst(Selector::source(BonusSource::ARMY, BonusCustomSource::undeadMoraleDebuff));
  103. if(hasUndead)
  104. {
  105. if(!undeadModifier)
  106. {
  107. undeadModifier = std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, -1, BonusCustomSource::undeadMoraleDebuff);
  108. undeadModifier->description.appendTextID("core.arraytxt.116");
  109. addNewBonus(undeadModifier);
  110. }
  111. }
  112. else if(undeadModifier)
  113. removeBonus(undeadModifier);
  114. }
  115. void CArmedInstance::armyChanged()
  116. {
  117. updateMoraleBonusFromArmy();
  118. }
  119. CBonusSystemNode & CArmedInstance::whereShouldBeAttached(CGameState & gs)
  120. {
  121. if(tempOwner.isValidPlayer())
  122. if(auto * where = gs.getPlayerState(tempOwner))
  123. return *where;
  124. return gs.globalEffects;
  125. }
  126. CBonusSystemNode & CArmedInstance::whatShouldBeAttached()
  127. {
  128. return *this;
  129. }
  130. void CArmedInstance::attachToBonusSystem(CGameState & gs)
  131. {
  132. whatShouldBeAttached().attachTo(whereShouldBeAttached(gs));
  133. }
  134. void CArmedInstance::restoreBonusSystem(CGameState & gs)
  135. {
  136. whatShouldBeAttached().attachTo(whereShouldBeAttached(gs));
  137. for(const auto & elem : stacks)
  138. elem.second->artDeserializationFix(gs, elem.second.get());
  139. }
  140. void CArmedInstance::detachFromBonusSystem(CGameState & gs)
  141. {
  142. whatShouldBeAttached().detachFrom(whereShouldBeAttached(gs));
  143. }
  144. void CArmedInstance::attachUnitsToArmy()
  145. {
  146. assert(getArmy() != nullptr);
  147. for(const auto & elem : stacks)
  148. elem.second->setArmy(getArmy());
  149. }
  150. const IBonusBearer* CArmedInstance::getBonusBearer() const
  151. {
  152. return this;
  153. }
  154. void CArmedInstance::serializeJsonOptions(JsonSerializeFormat & handler)
  155. {
  156. CGObjectInstance::serializeJsonOptions(handler);
  157. CCreatureSet::serializeJson(handler, "army", 7);
  158. }
  159. TerrainId CArmedInstance::getCurrentTerrain() const
  160. {
  161. if (anchorPos().isValid())
  162. return cb->getTile(visitablePos())->getTerrainID();
  163. else
  164. return TerrainId::NONE;
  165. }
  166. VCMI_LIB_NAMESPACE_END