TownPortalEffect.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /*
  2. * TownPortalEffect.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 "TownPortalEffect.h"
  12. #include "AdventureSpellMechanics.h"
  13. #include "../CSpellHandler.h"
  14. #include "../../CPlayerState.h"
  15. #include "../../IGameSettings.h"
  16. #include "../../callback/IGameInfoCallback.h"
  17. #include "../../mapObjects/CGHeroInstance.h"
  18. #include "../../mapObjects/CGTownInstance.h"
  19. #include "../../mapping/CMap.h"
  20. #include "../../networkPacks/PacksForClient.h"
  21. VCMI_LIB_NAMESPACE_BEGIN
  22. TownPortalEffect::TownPortalEffect(const CSpell * s, const JsonNode & config)
  23. : owner(s)
  24. , movementPointsRequired(config["movementPointsRequired"].Integer())
  25. , movementPointsTaken(config["movementPointsTaken"].Integer())
  26. , allowTownSelection(config["allowTownSelection"].Bool())
  27. , skipOccupiedTowns(config["skipOccupiedTowns"].Bool())
  28. {
  29. }
  30. ESpellCastResult TownPortalEffect::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
  31. {
  32. const CGTownInstance * destination = nullptr;
  33. if(!parameters.caster->getHeroCaster())
  34. {
  35. env->complain("Not a hero caster!");
  36. return ESpellCastResult::ERROR;
  37. }
  38. if(!allowTownSelection)
  39. {
  40. std::vector<const CGTownInstance *> pool = getPossibleTowns(env, parameters);
  41. destination = findNearestTown(env, parameters, pool);
  42. if(nullptr == destination)
  43. return ESpellCastResult::ERROR;
  44. if(static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()) < movementPointsRequired)
  45. return ESpellCastResult::ERROR;
  46. if(destination->getVisitingHero())
  47. {
  48. InfoWindow iw;
  49. iw.player = parameters.caster->getCasterOwner();
  50. iw.text.appendLocalString(EMetaText::GENERAL_TXT, 123);
  51. env->apply(iw);
  52. return ESpellCastResult::CANCEL;
  53. }
  54. }
  55. else if(env->getMap()->isInTheMap(parameters.pos))
  56. {
  57. const TerrainTile & tile = env->getMap()->getTile(parameters.pos);
  58. ObjectInstanceID topObjID = tile.topVisitableObj(false);
  59. const CGObjectInstance * topObj = env->getMap()->getObject(topObjID);
  60. if(!topObj)
  61. {
  62. env->complain("Destination tile is not visitable" + parameters.pos.toString());
  63. return ESpellCastResult::ERROR;
  64. }
  65. else if(topObj->ID == Obj::HERO)
  66. {
  67. env->complain("Can't teleport to occupied town at " + parameters.pos.toString());
  68. return ESpellCastResult::ERROR;
  69. }
  70. else if(topObj->ID != Obj::TOWN)
  71. {
  72. env->complain("No town at destination tile " + parameters.pos.toString());
  73. return ESpellCastResult::ERROR;
  74. }
  75. destination = dynamic_cast<const CGTownInstance *>(topObj);
  76. if(nullptr == destination)
  77. {
  78. env->complain("[Internal error] invalid town object at " + parameters.pos.toString());
  79. return ESpellCastResult::ERROR;
  80. }
  81. const auto relations = env->getCb()->getPlayerRelations(destination->tempOwner, parameters.caster->getCasterOwner());
  82. if(relations == PlayerRelations::ENEMIES)
  83. {
  84. env->complain("Can't teleport to enemy!");
  85. return ESpellCastResult::ERROR;
  86. }
  87. if(static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()) < movementPointsRequired)
  88. {
  89. env->complain("This hero has not enough movement points!");
  90. return ESpellCastResult::ERROR;
  91. }
  92. if(destination->getVisitingHero())
  93. {
  94. env->complain("[Internal error] Can't teleport to occupied town");
  95. return ESpellCastResult::ERROR;
  96. }
  97. }
  98. else
  99. {
  100. env->complain("Invalid destination tile");
  101. return ESpellCastResult::ERROR;
  102. }
  103. const TerrainTile & from = env->getMap()->getTile(parameters.caster->getHeroCaster()->visitablePos());
  104. const TerrainTile & dest = env->getMap()->getTile(destination->visitablePos());
  105. if(!dest.entrableTerrain(&from))
  106. {
  107. InfoWindow iw;
  108. iw.player = parameters.caster->getCasterOwner();
  109. iw.text.appendLocalString(EMetaText::GENERAL_TXT, 135);
  110. env->apply(iw);
  111. return ESpellCastResult::ERROR;
  112. }
  113. return ESpellCastResult::OK;
  114. }
  115. void TownPortalEffect::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
  116. {
  117. const CGTownInstance * destination = nullptr;
  118. if(parameters.caster->getSpellSchoolLevel(owner) < 2)
  119. {
  120. std::vector<const CGTownInstance *> pool = getPossibleTowns(env, parameters);
  121. destination = findNearestTown(env, parameters, pool);
  122. }
  123. else
  124. {
  125. const TerrainTile & tile = env->getMap()->getTile(parameters.pos);
  126. ObjectInstanceID topObjID = tile.topVisitableObj(false);
  127. const CGObjectInstance * topObj = env->getMap()->getObject(topObjID);
  128. destination = dynamic_cast<const CGTownInstance *>(topObj);
  129. }
  130. if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(destination->visitablePos()), EMovementMode::TOWN_PORTAL))
  131. {
  132. SetMovePoints smp;
  133. smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
  134. if(movementPointsTaken < static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()))
  135. smp.val = parameters.caster->getHeroCaster()->movementPointsRemaining() - movementPointsTaken;
  136. else
  137. smp.val = 0;
  138. env->apply(smp);
  139. }
  140. }
  141. ESpellCastResult TownPortalEffect::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const AdventureSpellMechanics & mechanics) const
  142. {
  143. std::vector<const CGTownInstance *> towns = getPossibleTowns(env, parameters);
  144. if(!parameters.caster->getHeroCaster())
  145. {
  146. env->complain("Not a hero caster!");
  147. return ESpellCastResult::ERROR;
  148. }
  149. if(towns.empty())
  150. {
  151. InfoWindow iw;
  152. iw.player = parameters.caster->getCasterOwner();
  153. iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124);
  154. env->apply(iw);
  155. return ESpellCastResult::CANCEL;
  156. }
  157. if(static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()) < movementPointsTaken)
  158. {
  159. InfoWindow iw;
  160. iw.player = parameters.caster->getCasterOwner();
  161. iw.text.appendLocalString(EMetaText::GENERAL_TXT, 125);
  162. env->apply(iw);
  163. return ESpellCastResult::CANCEL;
  164. }
  165. if(!parameters.pos.isValid() && parameters.caster->getSpellSchoolLevel(owner) >= 2)
  166. {
  167. auto queryCallback = [&mechanics, env, parameters](std::optional<int32_t> reply) -> void
  168. {
  169. if(reply.has_value())
  170. {
  171. ObjectInstanceID townId(*reply);
  172. const CGObjectInstance * o = env->getCb()->getObj(townId, true);
  173. if(o == nullptr)
  174. {
  175. env->complain("Invalid object instance selected");
  176. return;
  177. }
  178. if(!dynamic_cast<const CGTownInstance *>(o))
  179. {
  180. env->complain("Object instance is not town");
  181. return;
  182. }
  183. AdventureSpellCastParameters p;
  184. p.caster = parameters.caster;
  185. p.pos = o->visitablePos();
  186. mechanics.performCast(env, p);
  187. }
  188. };
  189. MapObjectSelectDialog request;
  190. for(const auto * t : towns)
  191. {
  192. if(t->getVisitingHero() == nullptr) //empty town
  193. request.objects.push_back(t->id);
  194. }
  195. if(request.objects.empty())
  196. {
  197. InfoWindow iw;
  198. iw.player = parameters.caster->getCasterOwner();
  199. iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124);
  200. env->apply(iw);
  201. return ESpellCastResult::CANCEL;
  202. }
  203. request.player = parameters.caster->getCasterOwner();
  204. request.title.appendLocalString(EMetaText::JK_TXT, 40);
  205. request.description.appendLocalString(EMetaText::JK_TXT, 41);
  206. request.icon = Component(ComponentType::SPELL, owner->id);
  207. env->genericQuery(&request, request.player, queryCallback);
  208. return ESpellCastResult::PENDING;
  209. }
  210. return ESpellCastResult::OK;
  211. }
  212. const CGTownInstance * TownPortalEffect::findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector <const CGTownInstance *> & pool) const
  213. {
  214. if(pool.empty())
  215. return nullptr;
  216. if(!parameters.caster->getHeroCaster())
  217. return nullptr;
  218. auto nearest = pool.cbegin(); //nearest town's iterator
  219. si32 dist = (*nearest)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos());
  220. for(auto i = nearest + 1; i != pool.cend(); ++i)
  221. {
  222. si32 curDist = (*i)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos());
  223. if(curDist < dist)
  224. {
  225. nearest = i;
  226. dist = curDist;
  227. }
  228. }
  229. return *nearest;
  230. }
  231. std::vector<const CGTownInstance *> TownPortalEffect::getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
  232. {
  233. std::vector<const CGTownInstance *> ret;
  234. const TeamState * team = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner());
  235. for(const auto & color : team->players)
  236. {
  237. for(auto currTown : env->getCb()->getPlayerState(color)->getTowns())
  238. {
  239. if (!skipOccupiedTowns || currTown->getVisitingHero() == nullptr)
  240. ret.push_back(currTown);
  241. }
  242. }
  243. return ret;
  244. }
  245. VCMI_LIB_NAMESPACE_END