CGameInfoCallback.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. /*
  2. * CGameInfoCallback.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 "CGameInfoCallback.h"
  12. #include "gameState/CGameState.h"
  13. #include "gameState/InfoAboutArmy.h"
  14. #include "gameState/SThievesGuildInfo.h"
  15. #include "gameState/TavernHeroesPool.h"
  16. #include "gameState/QuestInfo.h"
  17. #include "mapObjects/CGHeroInstance.h"
  18. #include "mapObjects/CGTownInstance.h"
  19. #include "mapObjects/MiscObjects.h"
  20. #include "networkPacks/ArtifactLocation.h"
  21. #include "CGeneralTextHandler.h"
  22. #include "StartInfo.h" // for StartInfo
  23. #include "battle/BattleInfo.h" // for BattleInfo
  24. #include "GameSettings.h"
  25. #include "TerrainHandler.h"
  26. #include "spells/CSpellHandler.h"
  27. #include "mapping/CMap.h"
  28. #include "CPlayerState.h"
  29. VCMI_LIB_NAMESPACE_BEGIN
  30. //TODO make clean
  31. #define ERROR_VERBOSE_OR_NOT_RET_VAL_IF(cond, verbose, txt, retVal) do {if(cond){if(verbose)logGlobal->error("%s: %s",BOOST_CURRENT_FUNCTION, txt); return retVal;}} while(0)
  32. #define ERROR_RET_IF(cond, txt) do {if(cond){logGlobal->error("%s: %s", BOOST_CURRENT_FUNCTION, txt); return;}} while(0)
  33. #define ERROR_RET_VAL_IF(cond, txt, retVal) do {if(cond){logGlobal->error("%s: %s", BOOST_CURRENT_FUNCTION, txt); return retVal;}} while(0)
  34. PlayerColor CGameInfoCallback::getOwner(ObjectInstanceID heroID) const
  35. {
  36. const CGObjectInstance *obj = getObj(heroID);
  37. ERROR_RET_VAL_IF(!obj, "No such object!", PlayerColor::CANNOT_DETERMINE);
  38. return obj->tempOwner;
  39. }
  40. int CGameInfoCallback::getResource(PlayerColor Player, GameResID which) const
  41. {
  42. const PlayerState *p = getPlayerState(Player);
  43. ERROR_RET_VAL_IF(!p, "No player info!", -1);
  44. ERROR_RET_VAL_IF(p->resources.size() <= which.getNum() || which.getNum() < 0, "No such resource!", -1);
  45. return p->resources[which];
  46. }
  47. const PlayerSettings * CGameInfoCallback::getPlayerSettings(PlayerColor color) const
  48. {
  49. return &gs->scenarioOps->getIthPlayersSettings(color);
  50. }
  51. bool CGameInfoCallback::isAllowed(SpellID id) const
  52. {
  53. return gs->map->allowedSpells.count(id) != 0;
  54. }
  55. bool CGameInfoCallback::isAllowed(ArtifactID id) const
  56. {
  57. return gs->map->allowedArtifact.count(id) != 0;
  58. }
  59. bool CGameInfoCallback::isAllowed(SecondarySkill id) const
  60. {
  61. return gs->map->allowedAbilities.count(id) != 0;
  62. }
  63. std::optional<PlayerColor> CGameInfoCallback::getPlayerID() const
  64. {
  65. return std::nullopt;
  66. }
  67. const Player * CGameInfoCallback::getPlayer(PlayerColor color) const
  68. {
  69. return getPlayerState(color, false);
  70. }
  71. const PlayerState * CGameInfoCallback::getPlayerState(PlayerColor color, bool verbose) const
  72. {
  73. //funtion written from scratch since it's accessed A LOT by AI
  74. if(!color.isValidPlayer())
  75. {
  76. return nullptr;
  77. }
  78. auto player = gs->players.find(color);
  79. if (player != gs->players.end())
  80. {
  81. if (hasAccess(color))
  82. return &player->second;
  83. else
  84. {
  85. if (verbose)
  86. logGlobal->error("Cannot access player %d info!", color);
  87. return nullptr;
  88. }
  89. }
  90. else
  91. {
  92. if (verbose)
  93. logGlobal->error("Cannot find player %d info!", color);
  94. return nullptr;
  95. }
  96. }
  97. TurnTimerInfo CGameInfoCallback::getPlayerTurnTime(PlayerColor color) const
  98. {
  99. if(!color.isValidPlayer())
  100. {
  101. return TurnTimerInfo{};
  102. }
  103. auto player = gs->players.find(color);
  104. if(player != gs->players.end())
  105. {
  106. return player->second.turnTimer;
  107. }
  108. return TurnTimerInfo{};
  109. }
  110. /************************************************************************/
  111. /* */
  112. /************************************************************************/
  113. const CGObjectInstance* CGameInfoCallback::getObj(ObjectInstanceID objid, bool verbose) const
  114. {
  115. si32 oid = objid.num;
  116. if(oid < 0 || oid >= gs->map->objects.size())
  117. {
  118. if(verbose)
  119. logGlobal->error("Cannot get object with id %d", oid);
  120. return nullptr;
  121. }
  122. const CGObjectInstance *ret = gs->map->objects[oid];
  123. if(!ret)
  124. {
  125. if(verbose)
  126. logGlobal->error("Cannot get object with id %d. Object was removed", oid);
  127. return nullptr;
  128. }
  129. if(!isVisible(ret, getPlayerID()) && ret->tempOwner != getPlayerID())
  130. {
  131. if(verbose)
  132. logGlobal->error("Cannot get object with id %d. Object is not visible.", oid);
  133. return nullptr;
  134. }
  135. return ret;
  136. }
  137. const CGHeroInstance* CGameInfoCallback::getHero(ObjectInstanceID objid) const
  138. {
  139. const CGObjectInstance *obj = getObj(objid, false);
  140. if(obj)
  141. return dynamic_cast<const CGHeroInstance*>(obj);
  142. else
  143. return nullptr;
  144. }
  145. const CGTownInstance* CGameInfoCallback::getTown(ObjectInstanceID objid) const
  146. {
  147. const CGObjectInstance *obj = getObj(objid, false);
  148. if(obj)
  149. return dynamic_cast<const CGTownInstance*>(obj);
  150. else
  151. return nullptr;
  152. }
  153. void CGameInfoCallback::fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const
  154. {
  155. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  156. ERROR_RET_IF(!canGetFullInfo(obj), "Cannot get info about not owned object!");
  157. ERROR_RET_IF(!obj->hasStackAtSlot(stackPos), "There is no such stack!");
  158. gs->fillUpgradeInfo(obj, stackPos, out);
  159. //return gs->fillUpgradeInfo(obj->getStack(stackPos));
  160. }
  161. const StartInfo * CGameInfoCallback::getStartInfo(bool beforeRandomization) const
  162. {
  163. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  164. if(beforeRandomization)
  165. return gs->initialOpts;
  166. else
  167. return gs->scenarioOps;
  168. }
  169. int32_t CGameInfoCallback::getSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const
  170. {
  171. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  172. ERROR_RET_VAL_IF(!canGetFullInfo(caster), "Cannot get info about caster!", -1);
  173. //if there is a battle
  174. auto casterBattle = gs->getBattle(caster->getOwner());
  175. if(casterBattle)
  176. return casterBattle->battleGetSpellCost(sp, caster);
  177. //if there is no battle
  178. return caster->getSpellCost(sp);
  179. }
  180. int64_t CGameInfoCallback::estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const
  181. {
  182. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  183. ERROR_RET_VAL_IF(hero && !canGetFullInfo(hero), "Cannot get info about caster!", -1);
  184. if(hero) //we see hero's spellbook
  185. return sp->calculateDamage(hero);
  186. else
  187. return 0; //mage guild
  188. }
  189. void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj)
  190. {
  191. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  192. ERROR_RET_IF(!obj, "No guild object!");
  193. ERROR_RET_IF(obj->ID == Obj::TOWN && !canGetFullInfo(obj), "Cannot get info about town guild object!");
  194. //TODO: advmap object -> check if they're visited by our hero
  195. if(obj->ID == Obj::TOWN || obj->ID == Obj::TAVERN)
  196. {
  197. int taverns = 0;
  198. for(auto town : gs->players[*getPlayerID()].towns)
  199. {
  200. if(town->hasBuilt(BuildingID::TAVERN))
  201. taverns++;
  202. }
  203. gs->obtainPlayersStats(thi, taverns);
  204. }
  205. else if(obj->ID == Obj::DEN_OF_THIEVES)
  206. {
  207. gs->obtainPlayersStats(thi, 20);
  208. }
  209. }
  210. int CGameInfoCallback::howManyTowns(PlayerColor Player) const
  211. {
  212. ERROR_RET_VAL_IF(!hasAccess(Player), "Access forbidden!", -1);
  213. return static_cast<int>(gs->players[Player].towns.size());
  214. }
  215. bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject) const
  216. {
  217. ERROR_RET_VAL_IF(!isVisible(town, getPlayerID()), "Town is not visible!", false); //it's not a town or it's not visible for layer
  218. bool detailed = hasAccess(town->tempOwner);
  219. if(town->ID == Obj::TOWN)
  220. {
  221. if(!detailed && nullptr != selectedObject)
  222. {
  223. const auto * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
  224. if(nullptr != selectedHero)
  225. detailed = selectedHero->hasVisions(town, BonusCustomSubtype::visionsTowns);
  226. }
  227. dest.initFromTown(dynamic_cast<const CGTownInstance *>(town), detailed);
  228. }
  229. else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2)
  230. dest.initFromArmy(dynamic_cast<const CArmedInstance *>(town), detailed);
  231. else
  232. return false;
  233. return true;
  234. }
  235. int3 CGameInfoCallback::guardingCreaturePosition (int3 pos) const //FIXME: redundant?
  236. {
  237. ERROR_RET_VAL_IF(!isVisible(pos), "Tile is not visible!", int3(-1,-1,-1));
  238. return gs->guardingCreaturePosition(pos);
  239. }
  240. std::vector<const CGObjectInstance*> CGameInfoCallback::getGuardingCreatures (int3 pos) const
  241. {
  242. ERROR_RET_VAL_IF(!isVisible(pos), "Tile is not visible!", std::vector<const CGObjectInstance*>());
  243. std::vector<const CGObjectInstance*> ret;
  244. for(auto * cr : gs->guardingCreatures(pos))
  245. {
  246. ret.push_back(cr);
  247. }
  248. return ret;
  249. }
  250. bool CGameInfoCallback::isTileGuardedAfterDimensionDoorUse(int3 tile, const CGHeroInstance * castingHero) const
  251. {
  252. //for known tiles this is just potential convenience info, for tiles behind fog of war this info matches HotA but not H3 so make it accessible only with proper setting on
  253. bool canAccessInfo = false;
  254. if(isVisible(tile))
  255. canAccessInfo = true;
  256. else if(VLC->settings()->getBoolean(EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS) //TODO: check if available casts > 0
  257. && isInScreenRange(castingHero->getSightCenter(), tile)
  258. && castingHero->canCastThisSpell(static_cast<SpellID>(SpellID::DIMENSION_DOOR).toSpell()))
  259. canAccessInfo = true;
  260. if(canAccessInfo)
  261. return !gs->guardingCreatures(tile).empty();
  262. return false;
  263. }
  264. bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject) const
  265. {
  266. const auto * h = dynamic_cast<const CGHeroInstance *>(hero);
  267. ERROR_RET_VAL_IF(!h, "That's not a hero!", false);
  268. InfoAboutHero::EInfoLevel infoLevel = InfoAboutHero::EInfoLevel::BASIC;
  269. if(hasAccess(h->tempOwner))
  270. infoLevel = InfoAboutHero::EInfoLevel::DETAILED;
  271. if (infoLevel == InfoAboutHero::EInfoLevel::BASIC)
  272. {
  273. auto ourBattle = gs->getBattle(*getPlayerID());
  274. if(ourBattle && ourBattle->playerHasAccessToHeroInfo(*getPlayerID(), h)) //if it's battle we can get enemy hero full data
  275. infoLevel = InfoAboutHero::EInfoLevel::INBATTLE;
  276. else
  277. ERROR_RET_VAL_IF(!isVisible(h->visitablePos()), "That hero is not visible!", false);
  278. }
  279. if( (infoLevel == InfoAboutHero::EInfoLevel::BASIC) && nullptr != selectedObject)
  280. {
  281. const auto * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
  282. if(nullptr != selectedHero)
  283. if(selectedHero->hasVisions(hero, BonusCustomSubtype::visionsHeroes))
  284. infoLevel = InfoAboutHero::EInfoLevel::DETAILED;
  285. }
  286. dest.initFromHero(h, infoLevel);
  287. //DISGUISED bonus implementation
  288. if(getPlayerRelations(*getPlayerID(), hero->tempOwner) == PlayerRelations::ENEMIES)
  289. {
  290. //todo: bonus cashing
  291. int disguiseLevel = h->valOfBonuses(BonusType::DISGUISED);
  292. auto doBasicDisguise = [](InfoAboutHero & info)
  293. {
  294. int maxAIValue = 0;
  295. const CCreature * mostStrong = nullptr;
  296. for(auto & elem : info.army)
  297. {
  298. if(static_cast<int>(elem.second.type->getAIValue()) > maxAIValue)
  299. {
  300. maxAIValue = elem.second.type->getAIValue();
  301. mostStrong = elem.second.type;
  302. }
  303. }
  304. if(nullptr == mostStrong)//just in case
  305. logGlobal->error("CGameInfoCallback::getHeroInfo: Unable to select most strong stack");
  306. else
  307. for(auto & elem : info.army)
  308. {
  309. elem.second.type = mostStrong;
  310. }
  311. };
  312. auto doAdvancedDisguise = [&doBasicDisguise](InfoAboutHero & info)
  313. {
  314. doBasicDisguise(info);
  315. for(auto & elem : info.army)
  316. elem.second.count = 0;
  317. };
  318. auto doExpertDisguise = [this,h](InfoAboutHero & info)
  319. {
  320. for(auto & elem : info.army)
  321. elem.second.count = 0;
  322. const auto factionIndex = getStartInfo(false)->playerInfos.at(h->tempOwner).castle;
  323. int maxAIValue = 0;
  324. const CCreature * mostStrong = nullptr;
  325. for(auto creature : VLC->creh->objects)
  326. {
  327. if(creature->getFaction() == factionIndex && static_cast<int>(creature->getAIValue()) > maxAIValue)
  328. {
  329. maxAIValue = creature->getAIValue();
  330. mostStrong = creature.get();
  331. }
  332. }
  333. if(nullptr != mostStrong) //possible, faction may have no creatures at all
  334. for(auto & elem : info.army)
  335. elem.second.type = mostStrong;
  336. };
  337. switch (disguiseLevel)
  338. {
  339. case 0:
  340. //no bonus at all - do nothing
  341. break;
  342. case 1:
  343. doBasicDisguise(dest);
  344. break;
  345. case 2:
  346. doAdvancedDisguise(dest);
  347. break;
  348. case 3:
  349. doExpertDisguise(dest);
  350. break;
  351. default:
  352. //invalid value
  353. logGlobal->error("CGameInfoCallback::getHeroInfo: Invalid DISGUISED bonus value %d", disguiseLevel);
  354. break;
  355. }
  356. }
  357. return true;
  358. }
  359. int CGameInfoCallback::getDate(Date mode) const
  360. {
  361. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  362. return gs->getDate(mode);
  363. }
  364. bool CGameInfoCallback::isVisible(int3 pos, const std::optional<PlayerColor> & Player) const
  365. {
  366. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  367. return gs->isVisible(pos, Player);
  368. }
  369. bool CGameInfoCallback::isVisible(int3 pos) const
  370. {
  371. return isVisible(pos, getPlayerID());
  372. }
  373. bool CGameInfoCallback::isVisible(const CGObjectInstance * obj, const std::optional<PlayerColor> & Player) const
  374. {
  375. return gs->isVisible(obj, Player);
  376. }
  377. bool CGameInfoCallback::isVisible(const CGObjectInstance *obj) const
  378. {
  379. return isVisible(obj, getPlayerID());
  380. }
  381. // const CCreatureSet* CInfoCallback::getGarrison(const CGObjectInstance *obj) const
  382. // {
  383. // //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  384. // if()
  385. // const CArmedInstance *armi = dynamic_cast<const CArmedInstance*>(obj);
  386. // if(!armi)
  387. // return nullptr;
  388. // else
  389. // return armi;
  390. // }
  391. std::vector <const CGObjectInstance *> CGameInfoCallback::getBlockingObjs( int3 pos ) const
  392. {
  393. std::vector<const CGObjectInstance *> ret;
  394. const TerrainTile *t = getTile(pos);
  395. ERROR_RET_VAL_IF(!t, "Not a valid tile requested!", ret);
  396. for(const CGObjectInstance * obj : t->blockingObjects)
  397. ret.push_back(obj);
  398. return ret;
  399. }
  400. std::vector <const CGObjectInstance *> CGameInfoCallback::getVisitableObjs(int3 pos, bool verbose) const
  401. {
  402. std::vector<const CGObjectInstance *> ret;
  403. const TerrainTile *t = getTile(pos, verbose);
  404. ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!t, verbose, pos.toString() + " is not visible!", ret);
  405. for(const CGObjectInstance * obj : t->visitableObjects)
  406. {
  407. if(getPlayerID() || obj->ID != Obj::EVENT) //hide events from players
  408. ret.push_back(obj);
  409. }
  410. return ret;
  411. }
  412. const CGObjectInstance * CGameInfoCallback::getTopObj (int3 pos) const
  413. {
  414. return vstd::backOrNull(getVisitableObjs(pos));
  415. }
  416. std::vector <const CGObjectInstance *> CGameInfoCallback::getFlaggableObjects(int3 pos) const
  417. {
  418. std::vector<const CGObjectInstance *> ret;
  419. const TerrainTile *t = getTile(pos);
  420. ERROR_RET_VAL_IF(!t, "Not a valid tile requested!", ret);
  421. for(const CGObjectInstance *obj : t->blockingObjects)
  422. if(obj->tempOwner != PlayerColor::UNFLAGGABLE)
  423. ret.push_back(obj);
  424. return ret;
  425. }
  426. int3 CGameInfoCallback::getMapSize() const
  427. {
  428. return int3(gs->map->width, gs->map->height, gs->map->twoLevel ? 2 : 1);
  429. }
  430. std::vector<const CGHeroInstance *> CGameInfoCallback::getAvailableHeroes(const CGObjectInstance * townOrTavern) const
  431. {
  432. ASSERT_IF_CALLED_WITH_PLAYER
  433. std::vector<const CGHeroInstance *> ret;
  434. //ERROR_RET_VAL_IF(!isOwnedOrVisited(townOrTavern), "Town or tavern must be owned or visited!", ret);
  435. //TODO: town needs to be owned, advmap tavern needs to be visited; to be reimplemented when visit tracking is done
  436. const CGTownInstance * town = getTown(townOrTavern->id);
  437. if(townOrTavern->ID == Obj::TAVERN || (town && town->hasBuilt(BuildingID::TAVERN)))
  438. return gs->heroesPool->getHeroesFor(*getPlayerID());
  439. return ret;
  440. }
  441. const TerrainTile * CGameInfoCallback::getTile(int3 tile, bool verbose) const
  442. {
  443. if(isVisible(tile))
  444. return &gs->map->getTile(tile);
  445. if(verbose)
  446. logGlobal->error("\r\n%s: %s\r\n", BOOST_CURRENT_FUNCTION, tile.toString() + " is not visible!");
  447. return nullptr;
  448. }
  449. const TerrainTile * CGameInfoCallback::getTileForDimensionDoor(int3 tile, const CGHeroInstance * castingHero) const
  450. {
  451. auto outputTile = getTile(tile, false);
  452. if(outputTile != nullptr)
  453. return outputTile;
  454. bool allowOnlyToUncoveredTiles = VLC->settings()->getBoolean(EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES);
  455. if(!allowOnlyToUncoveredTiles)
  456. {
  457. if(castingHero->canCastThisSpell(static_cast<SpellID>(SpellID::DIMENSION_DOOR).toSpell())
  458. && isInScreenRange(castingHero->getSightCenter(), tile)) //TODO: check if > 0 casts left
  459. {
  460. //we are allowed to get basic blocked/water invisible nearby tile date when casting DD spell
  461. TerrainTile targetTile = gs->map->getTile(tile);
  462. auto obfuscatedTile = std::make_shared<TerrainTile>();
  463. obfuscatedTile->visitable = false;
  464. obfuscatedTile->blocked = targetTile.blocked || targetTile.visitable;
  465. if(targetTile.blocked || targetTile.visitable)
  466. obfuscatedTile->terType = VLC->terrainTypeHandler->getById(TerrainId::ROCK);
  467. else if(!VLC->settings()->getBoolean(EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE))
  468. obfuscatedTile->terType = gs->map->getTile(castingHero->getSightCenter()).terType;
  469. else
  470. obfuscatedTile->terType = targetTile.isWater()
  471. ? VLC->terrainTypeHandler->getById(TerrainId::WATER)
  472. : VLC->terrainTypeHandler->getById(TerrainId::GRASS);
  473. outputTile = obfuscatedTile.get();
  474. }
  475. }
  476. return outputTile;
  477. }
  478. EDiggingStatus CGameInfoCallback::getTileDigStatus(int3 tile, bool verbose) const
  479. {
  480. if(!isVisible(tile))
  481. return EDiggingStatus::UNKNOWN;
  482. for(const auto & object : gs->map->objects)
  483. {
  484. if(object && object->ID == Obj::HOLE && object->pos == tile)
  485. return EDiggingStatus::TILE_OCCUPIED;
  486. }
  487. return getTile(tile)->getDiggingStatus();
  488. }
  489. //TODO: typedef?
  490. std::shared_ptr<const boost::multi_array<TerrainTile*, 3>> CGameInfoCallback::getAllVisibleTiles() const
  491. {
  492. assert(getPlayerID().has_value());
  493. const auto * team = getPlayerTeam(getPlayerID().value());
  494. size_t width = gs->map->width;
  495. size_t height = gs->map->height;
  496. size_t levels = gs->map->levels();
  497. auto * ptr = new boost::multi_array<TerrainTile *, 3>(boost::extents[levels][width][height]);
  498. int3 tile;
  499. for(tile.z = 0; tile.z < levels; tile.z++)
  500. for(tile.x = 0; tile.x < width; tile.x++)
  501. for(tile.y = 0; tile.y < height; tile.y++)
  502. {
  503. if ((*team->fogOfWarMap)[tile.z][tile.x][tile.y])
  504. (*ptr)[tile.z][tile.x][tile.y] = &gs->map->getTile(tile);
  505. else
  506. (*ptr)[tile.z][tile.x][tile.y] = nullptr;
  507. }
  508. return std::shared_ptr<const boost::multi_array<TerrainTile*, 3>>(ptr);
  509. }
  510. EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, BuildingID ID )
  511. {
  512. ERROR_RET_VAL_IF(!canGetFullInfo(t), "Town is not owned!", EBuildingState::TOWN_NOT_OWNED);
  513. if(!t->town->buildings.count(ID))
  514. return EBuildingState::BUILDING_ERROR;
  515. const CBuilding * building = t->town->buildings.at(ID);
  516. if(t->hasBuilt(ID)) //already built
  517. return EBuildingState::ALREADY_PRESENT;
  518. //can we build it?
  519. if(vstd::contains(t->forbiddenBuildings, ID))
  520. return EBuildingState::FORBIDDEN; //forbidden
  521. auto possiblyNotBuiltTest = [&](const BuildingID & id) -> bool
  522. {
  523. return ((id == BuildingID::CAPITOL) ? true : !t->hasBuilt(id));
  524. };
  525. std::function<bool(BuildingID id)> allowedTest = [&](const BuildingID & id) -> bool
  526. {
  527. return !vstd::contains(t->forbiddenBuildings, id);
  528. };
  529. if (!t->genBuildingRequirements(ID, true).satisfiable(allowedTest, possiblyNotBuiltTest))
  530. return EBuildingState::FORBIDDEN;
  531. if(ID == BuildingID::CAPITOL)
  532. {
  533. const PlayerState *ps = getPlayerState(t->tempOwner, false);
  534. if(ps)
  535. {
  536. for(const CGTownInstance *town : ps->towns)
  537. {
  538. if(town->hasBuilt(BuildingID::CAPITOL))
  539. {
  540. return EBuildingState::HAVE_CAPITAL; //no more than one capitol
  541. }
  542. }
  543. }
  544. }
  545. else if(ID == BuildingID::SHIPYARD)
  546. {
  547. const TerrainTile *tile = getTile(t->bestLocation(), false);
  548. if(!tile || !tile->terType->isWater())
  549. return EBuildingState::NO_WATER; //lack of water
  550. }
  551. auto buildTest = [&](const BuildingID & id) -> bool
  552. {
  553. return t->hasBuilt(id);
  554. };
  555. if (!t->genBuildingRequirements(ID).test(buildTest))
  556. return EBuildingState::PREREQUIRES;
  557. if(t->builded >= VLC->settings()->getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP))
  558. return EBuildingState::CANT_BUILD_TODAY; //building limit
  559. //checking resources
  560. if(!building->resources.canBeAfforded(getPlayerState(t->tempOwner)->resources))
  561. return EBuildingState::NO_RESOURCES; //lack of res
  562. return EBuildingState::ALLOWED;
  563. }
  564. const CMapHeader * CGameInfoCallback::getMapHeader() const
  565. {
  566. return gs->map;
  567. }
  568. bool CGameInfoCallback::hasAccess(std::optional<PlayerColor> playerId) const
  569. {
  570. return !getPlayerID() || getPlayerID()->isSpectator() || gs->getPlayerRelations(*playerId, *getPlayerID()) != PlayerRelations::ENEMIES;
  571. }
  572. EPlayerStatus CGameInfoCallback::getPlayerStatus(PlayerColor player, bool verbose) const
  573. {
  574. const PlayerState *ps = gs->getPlayerState(player, verbose);
  575. ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!ps, verbose, "No such player!", EPlayerStatus::WRONG);
  576. return ps->status;
  577. }
  578. std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTavern) const
  579. {
  580. MetaString text;
  581. text.appendLocalString(EMetaText::GENERAL_TXT, 216);
  582. std::string extraText;
  583. if(gs->rumor.type == RumorState::TYPE_NONE)
  584. return text.toString();
  585. auto rumor = gs->rumor.last[gs->rumor.type];
  586. switch(gs->rumor.type)
  587. {
  588. case RumorState::TYPE_SPECIAL:
  589. text.replaceLocalString(EMetaText::GENERAL_TXT, rumor.first);
  590. if(rumor.first == RumorState::RUMOR_GRAIL)
  591. text.replaceTextID(TextIdentifier("core", "arraytxt", 158 + rumor.second).get());
  592. else
  593. text.replaceTextID(TextIdentifier("core", "plcolors", rumor.second).get());
  594. break;
  595. case RumorState::TYPE_MAP:
  596. text.replaceRawString(gs->map->rumors[rumor.first].text.toString());
  597. break;
  598. case RumorState::TYPE_RAND:
  599. text.replaceTextID(TextIdentifier("core", "randtvrn", rumor.first).get());
  600. break;
  601. }
  602. return text.toString();
  603. }
  604. PlayerRelations CGameInfoCallback::getPlayerRelations( PlayerColor color1, PlayerColor color2 ) const
  605. {
  606. return gs->getPlayerRelations(color1, color2);
  607. }
  608. bool CGameInfoCallback::canGetFullInfo(const CGObjectInstance *obj) const
  609. {
  610. return !obj || hasAccess(obj->tempOwner);
  611. }
  612. int CGameInfoCallback::getHeroCount( PlayerColor player, bool includeGarrisoned ) const
  613. {
  614. int ret = 0;
  615. const PlayerState *p = gs->getPlayerState(player);
  616. ERROR_RET_VAL_IF(!p, "No such player!", -1);
  617. if(includeGarrisoned)
  618. return static_cast<int>(p->heroes.size());
  619. else
  620. for(const auto & elem : p->heroes)
  621. if(!elem->inTownGarrison)
  622. ret++;
  623. return ret;
  624. }
  625. bool CGameInfoCallback::isOwnedOrVisited(const CGObjectInstance *obj) const
  626. {
  627. if(canGetFullInfo(obj))
  628. return true;
  629. const TerrainTile *t = getTile(obj->visitablePos()); //get entrance tile
  630. const CGObjectInstance *visitor = t->visitableObjects.back(); //visitong hero if present or the obejct itself at last
  631. return visitor->ID == Obj::HERO && canGetFullInfo(visitor); //owned or allied hero is a visitor
  632. }
  633. bool CGameInfoCallback::isPlayerMakingTurn(PlayerColor player) const
  634. {
  635. return gs->actingPlayers.count(player);
  636. }
  637. CGameInfoCallback::CGameInfoCallback():
  638. gs(nullptr)
  639. {
  640. }
  641. CGameInfoCallback::CGameInfoCallback(CGameState * GS):
  642. gs(GS)
  643. {
  644. }
  645. int CPlayerSpecificInfoCallback::howManyTowns() const
  646. {
  647. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  648. ERROR_RET_VAL_IF(!getPlayerID(), "Applicable only for player callbacks", -1);
  649. return CGameInfoCallback::howManyTowns(*getPlayerID());
  650. }
  651. std::vector < const CGTownInstance *> CPlayerSpecificInfoCallback::getTownsInfo(bool onlyOur) const
  652. {
  653. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  654. auto ret = std::vector < const CGTownInstance *>();
  655. for(const auto & i : gs->players)
  656. {
  657. for(const auto & town : i.second.towns)
  658. {
  659. if(i.first == getPlayerID() || (!onlyOur && isVisible(town, getPlayerID())))
  660. {
  661. ret.push_back(town);
  662. }
  663. }
  664. } // for ( std::map<int, PlayerState>::iterator i=gs->players.begin() ; i!=gs->players.end();i++)
  665. return ret;
  666. }
  667. std::vector < const CGHeroInstance *> CPlayerSpecificInfoCallback::getHeroesInfo(bool onlyOur) const
  668. {
  669. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  670. std::vector < const CGHeroInstance *> ret;
  671. for(auto hero : gs->map->heroesOnMap)
  672. {
  673. // !player || // - why would we even get access to hero not owned by any player?
  674. if((hero->tempOwner == *getPlayerID()) ||
  675. (isVisible(hero->visitablePos(), getPlayerID()) && !onlyOur) )
  676. {
  677. ret.push_back(hero);
  678. }
  679. }
  680. return ret;
  681. }
  682. int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool includeGarrisoned) const
  683. {
  684. if (hero->inTownGarrison && !includeGarrisoned)
  685. return -1;
  686. size_t index = 0;
  687. auto & heroes = gs->players[*getPlayerID()].heroes;
  688. for (auto & heroe : heroes)
  689. {
  690. if (includeGarrisoned || !(heroe)->inTownGarrison)
  691. index++;
  692. if (heroe == hero)
  693. return static_cast<int>(index);
  694. }
  695. return -1;
  696. }
  697. int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio )
  698. {
  699. if (!getPlayerID() || gs->map->obeliskCount == 0)
  700. {
  701. *outKnownRatio = 0.0;
  702. }
  703. else
  704. {
  705. TeamID t = gs->getPlayerTeam(*getPlayerID())->id;
  706. double visited = 0.0;
  707. if(gs->map->obelisksVisited.count(t))
  708. visited = static_cast<double>(gs->map->obelisksVisited[t]);
  709. *outKnownRatio = visited / gs->map->obeliskCount;
  710. }
  711. return gs->map->grailPos;
  712. }
  713. std::vector < const CGObjectInstance * > CPlayerSpecificInfoCallback::getMyObjects() const
  714. {
  715. std::vector < const CGObjectInstance * > ret;
  716. for(const CGObjectInstance * obj : gs->map->objects)
  717. {
  718. if(obj && obj->tempOwner == getPlayerID())
  719. ret.push_back(obj);
  720. }
  721. return ret;
  722. }
  723. std::vector < const CGDwelling * > CPlayerSpecificInfoCallback::getMyDwellings() const
  724. {
  725. ASSERT_IF_CALLED_WITH_PLAYER
  726. std::vector < const CGDwelling * > ret;
  727. for(CGDwelling * dw : gs->getPlayerState(*getPlayerID())->dwellings)
  728. {
  729. ret.push_back(dw);
  730. }
  731. return ret;
  732. }
  733. std::vector <QuestInfo> CPlayerSpecificInfoCallback::getMyQuests() const
  734. {
  735. std::vector <QuestInfo> ret;
  736. for(const auto & quest : gs->getPlayerState(*getPlayerID())->quests)
  737. {
  738. ret.push_back (quest);
  739. }
  740. return ret;
  741. }
  742. int CPlayerSpecificInfoCallback::howManyHeroes(bool includeGarrisoned) const
  743. {
  744. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  745. ERROR_RET_VAL_IF(!getPlayerID(), "Applicable only for player callbacks", -1);
  746. return getHeroCount(*getPlayerID(), includeGarrisoned);
  747. }
  748. const CGHeroInstance* CPlayerSpecificInfoCallback::getHeroBySerial(int serialId, bool includeGarrisoned) const
  749. {
  750. ASSERT_IF_CALLED_WITH_PLAYER
  751. const PlayerState *p = getPlayerState(*getPlayerID());
  752. ERROR_RET_VAL_IF(!p, "No player info", nullptr);
  753. if (!includeGarrisoned)
  754. {
  755. for(ui32 i = 0; i < p->heroes.size() && static_cast<int>(i) <= serialId; i++)
  756. if(p->heroes[i]->inTownGarrison)
  757. serialId++;
  758. }
  759. ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->heroes.size(), "No player info", nullptr);
  760. return p->heroes[serialId];
  761. }
  762. const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId) const
  763. {
  764. ASSERT_IF_CALLED_WITH_PLAYER
  765. const PlayerState *p = getPlayerState(*getPlayerID());
  766. ERROR_RET_VAL_IF(!p, "No player info", nullptr);
  767. ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->towns.size(), "No player info", nullptr);
  768. return p->towns[serialId];
  769. }
  770. int CPlayerSpecificInfoCallback::getResourceAmount(GameResID type) const
  771. {
  772. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  773. ERROR_RET_VAL_IF(!getPlayerID(), "Applicable only for player callbacks", -1);
  774. return getResource(*getPlayerID(), type);
  775. }
  776. TResources CPlayerSpecificInfoCallback::getResourceAmount() const
  777. {
  778. //boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
  779. ERROR_RET_VAL_IF(!getPlayerID(), "Applicable only for player callbacks", TResources());
  780. return gs->players[*getPlayerID()].resources;
  781. }
  782. const TeamState * CGameInfoCallback::getTeam( TeamID teamID ) const
  783. {
  784. //rewritten by hand, AI calls this function a lot
  785. auto team = gs->teams.find(teamID);
  786. if (team != gs->teams.end())
  787. {
  788. const TeamState *ret = &team->second;
  789. if(!getPlayerID().has_value()) //neutral (or invalid) player
  790. return ret;
  791. else
  792. {
  793. if (vstd::contains(ret->players, *getPlayerID())) //specific player
  794. return ret;
  795. else
  796. {
  797. logGlobal->error("Illegal attempt to access team data!");
  798. return nullptr;
  799. }
  800. }
  801. }
  802. else
  803. {
  804. logGlobal->error("Cannot find info for team %d", teamID);
  805. return nullptr;
  806. }
  807. }
  808. const TeamState * CGameInfoCallback::getPlayerTeam( PlayerColor color ) const
  809. {
  810. auto player = gs->players.find(color);
  811. if (player != gs->players.end())
  812. {
  813. return getTeam (player->second.team);
  814. }
  815. else
  816. {
  817. return nullptr;
  818. }
  819. }
  820. const CGHeroInstance * CGameInfoCallback::getHeroWithSubid( int subid ) const
  821. {
  822. if(subid<0)
  823. return nullptr;
  824. if(subid>= gs->map->allHeroes.size())
  825. return nullptr;
  826. return gs->map->allHeroes.at(subid).get();
  827. }
  828. bool CGameInfoCallback::isInTheMap(const int3 &pos) const
  829. {
  830. return gs->map->isInTheMap(pos);
  831. }
  832. void CGameInfoCallback::getVisibleTilesInRange(std::unordered_set<int3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula) const
  833. {
  834. gs->getTilesInRange(tiles, pos, radious, ETileVisibility::REVEALED, *getPlayerID(), distanceFormula);
  835. }
  836. void CGameInfoCallback::calculatePaths(const std::shared_ptr<PathfinderConfig> & config)
  837. {
  838. gs->calculatePaths(config);
  839. }
  840. void CGameInfoCallback::calculatePaths( const CGHeroInstance *hero, CPathsInfo &out)
  841. {
  842. gs->calculatePaths(hero, out);
  843. }
  844. const CArtifactInstance * CGameInfoCallback::getArtInstance( ArtifactInstanceID aid ) const
  845. {
  846. return gs->map->artInstances[aid.num];
  847. }
  848. const CGObjectInstance * CGameInfoCallback::getObjInstance( ObjectInstanceID oid ) const
  849. {
  850. return gs->map->objects[oid.num];
  851. }
  852. const CArtifactSet * CGameInfoCallback::getArtSet(const ArtifactLocation & loc) const
  853. {
  854. return gs->getArtSet(loc);
  855. }
  856. std::vector<ObjectInstanceID> CGameInfoCallback::getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player) const
  857. {
  858. vstd::erase_if(ids, [&](const ObjectInstanceID & id) -> bool
  859. {
  860. const auto * obj = getObj(id, false);
  861. return player != PlayerColor::UNFLAGGABLE && (!obj || !isVisible(obj->pos, player));
  862. });
  863. return ids;
  864. }
  865. std::vector<ObjectInstanceID> CGameInfoCallback::getTeleportChannelEntraces(TeleportChannelID id, PlayerColor player) const
  866. {
  867. return getVisibleTeleportObjects(gs->map->teleportChannels[id]->entrances, player);
  868. }
  869. std::vector<ObjectInstanceID> CGameInfoCallback::getTeleportChannelExits(TeleportChannelID id, PlayerColor player) const
  870. {
  871. return getVisibleTeleportObjects(gs->map->teleportChannels[id]->exits, player);
  872. }
  873. ETeleportChannelType CGameInfoCallback::getTeleportChannelType(TeleportChannelID id, PlayerColor player) const
  874. {
  875. std::vector<ObjectInstanceID> entrances = getTeleportChannelEntraces(id, player);
  876. std::vector<ObjectInstanceID> exits = getTeleportChannelExits(id, player);
  877. if((entrances.empty() || exits.empty()) // impassable if exits or entrances list are empty
  878. || (entrances.size() == 1 && entrances == exits)) // impassable if only entrance and only exit is same object. e.g bidirectional monolith
  879. {
  880. return ETeleportChannelType::IMPASSABLE;
  881. }
  882. auto intersection = vstd::intersection(entrances, exits);
  883. if(intersection.size() == entrances.size() && intersection.size() == exits.size())
  884. return ETeleportChannelType::BIDIRECTIONAL;
  885. else if(intersection.empty())
  886. return ETeleportChannelType::UNIDIRECTIONAL;
  887. else
  888. return ETeleportChannelType::MIXED;
  889. }
  890. bool CGameInfoCallback::isTeleportChannelImpassable(TeleportChannelID id, PlayerColor player) const
  891. {
  892. return ETeleportChannelType::IMPASSABLE == getTeleportChannelType(id, player);
  893. }
  894. bool CGameInfoCallback::isTeleportChannelBidirectional(TeleportChannelID id, PlayerColor player) const
  895. {
  896. return ETeleportChannelType::BIDIRECTIONAL == getTeleportChannelType(id, player);
  897. }
  898. bool CGameInfoCallback::isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player) const
  899. {
  900. return ETeleportChannelType::UNIDIRECTIONAL == getTeleportChannelType(id, player);
  901. }
  902. bool CGameInfoCallback::isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const
  903. {
  904. return obj && obj->isEntrance() && !isTeleportChannelImpassable(obj->channel, player);
  905. }
  906. VCMI_LIB_NAMESPACE_END