CArmedInstance.cpp 5.0 KB

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