DimensionDoorMechanics.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. * DimensionDoorMechanics.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 "DimensionDoorMechanics.h"
  12. #include "../CSpellHandler.h"
  13. #include "../../IGameSettings.h"
  14. #include "../../callback/IGameInfoCallback.h"
  15. #include "../../mapObjects/CGHeroInstance.h"
  16. #include "../../mapping/TerrainTile.h"
  17. #include "../../networkPacks/PacksForClient.h"
  18. VCMI_LIB_NAMESPACE_BEGIN
  19. DimensionDoorMechanics::DimensionDoorMechanics(const CSpell * s)
  20. : AdventureSpellMechanics(s)
  21. {
  22. }
  23. bool DimensionDoorMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const
  24. {
  25. if(!caster->getHeroCaster())
  26. return false;
  27. if(caster->getHeroCaster()->movementPointsRemaining() <= 0) //unlike town portal non-zero MP is enough
  28. {
  29. problem.add(MetaString::createFromTextID("core.genrltxt.125"));
  30. return false;
  31. }
  32. const auto schoolLevel = caster->getSpellSchoolLevel(owner);
  33. std::stringstream cachingStr;
  34. cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << owner->id.num;
  35. int castsAlreadyPerformedThisTurn = caster->getHeroCaster()->getBonuses(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(owner->id)), cachingStr.str())->size();
  36. int castsLimit = owner->getLevelPower(schoolLevel);
  37. bool isTournamentRulesLimitEnabled = cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT);
  38. if(isTournamentRulesLimitEnabled)
  39. {
  40. int3 mapSize = cb->getMapSize();
  41. bool meetsTournamentRulesTwoCastsRequirements = mapSize.x * mapSize.y * mapSize.z >= GameConstants::TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD && schoolLevel == MasteryLevel::EXPERT;
  42. castsLimit = meetsTournamentRulesTwoCastsRequirements ? 2 : 1;
  43. }
  44. if(castsAlreadyPerformedThisTurn >= castsLimit) //limit casts per turn
  45. {
  46. MetaString message = MetaString::createFromTextID("core.genrltxt.338");
  47. caster->getCasterName(message);
  48. problem.add(std::move(message));
  49. return false;
  50. }
  51. return true;
  52. }
  53. bool DimensionDoorMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const
  54. {
  55. if(!cb->isInTheMap(pos))
  56. return false;
  57. if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES))
  58. {
  59. if(!cb->isVisibleFor(pos, caster->getCasterOwner()))
  60. return false;
  61. }
  62. int3 casterPosition = caster->getHeroCaster()->getSightCenter();
  63. const TerrainTile * dest = cb->getTileUnchecked(pos);
  64. const TerrainTile * curr = cb->getTileUnchecked(casterPosition);
  65. if(!dest)
  66. return false;
  67. if(!curr)
  68. return false;
  69. if(!isInScreenRange(casterPosition, pos))
  70. return false;
  71. if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE))
  72. {
  73. if(!dest->isClear(curr))
  74. return false;
  75. }
  76. else
  77. {
  78. if(dest->blocked())
  79. return false;
  80. }
  81. return true;
  82. }
  83. ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
  84. {
  85. const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
  86. const int baseCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE);
  87. const int movementCost = baseCost * ((schoolLevel >= 3) ? 2 : 3);
  88. int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter();
  89. const TerrainTile * dest = env->getCb()->getTile(parameters.pos);
  90. const TerrainTile * curr = env->getCb()->getTile(casterPosition);
  91. if(!dest->isClear(curr))
  92. {
  93. InfoWindow iw;
  94. iw.player = parameters.caster->getCasterOwner();
  95. // tile is either blocked or not possible to move (e.g. water <-> land)
  96. if(env->getCb()->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_FAILURE_SPENDS_POINTS))
  97. {
  98. // SOD: DD to such "wrong" terrain results in mana and move points spending, but fails to move hero
  99. iw.text = MetaString::createFromTextID("core.genrltxt.70"); // Dimension Door failed!
  100. env->apply(iw);
  101. // no return - resources will be spent
  102. }
  103. else
  104. {
  105. // HotA: game will show error message without taking mana or move points, even when DD into terra incognita
  106. iw.text = MetaString::createFromTextID("vcmi.dimensionDoor.seaToLandError");
  107. env->apply(iw);
  108. return ESpellCastResult::CANCEL;
  109. }
  110. }
  111. GiveBonus gb;
  112. gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
  113. gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id));
  114. env->apply(gb);
  115. SetMovePoints smp;
  116. smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
  117. if(movementCost < static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()))
  118. smp.val = parameters.caster->getHeroCaster()->movementPointsRemaining() - movementCost;
  119. else
  120. smp.val = 0;
  121. env->apply(smp);
  122. return ESpellCastResult::OK;
  123. }
  124. void DimensionDoorMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
  125. {
  126. int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter();
  127. const TerrainTile * dest = env->getCb()->getTile(parameters.pos);
  128. const TerrainTile * curr = env->getCb()->getTile(casterPosition);
  129. if(dest->isClear(curr))
  130. env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(parameters.pos), EMovementMode::DIMENSION_DOOR);
  131. }
  132. VCMI_LIB_NAMESPACE_END