CGameStateTest.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * CGameStateTest.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 "mock/mock_MapService.h"
  12. #include "mock/mock_IGameCallback.h"
  13. #include "../../lib/VCMIDirs.h"
  14. #include "../../lib/CGameState.h"
  15. #include "../../lib/NetPacks.h"
  16. #include "../../lib/StartInfo.h"
  17. #include "../../lib/battle/BattleInfo.h"
  18. #include "../../lib/CStack.h"
  19. #include "../../lib/filesystem/ResourceID.h"
  20. #include "../../lib/mapping/CMap.h"
  21. #include "../../lib/spells/CSpellHandler.h"
  22. #include "../../lib/spells/ISpellMechanics.h"
  23. #include "../../lib/spells/AbilityCaster.h"
  24. class CGameStateTest : public ::testing::Test, public SpellCastEnvironment, public MapListener
  25. {
  26. public:
  27. CGameStateTest()
  28. : gameCallback(new GameCallbackMock(this)),
  29. mapService("test/MiniTest/", this),
  30. map(nullptr)
  31. {
  32. IObjectInterface::cb = gameCallback.get();
  33. }
  34. virtual ~CGameStateTest()
  35. {
  36. IObjectInterface::cb = nullptr;
  37. }
  38. void sendAndApply(CPackForClient * pack) const override
  39. {
  40. gameState->apply(pack);
  41. }
  42. void complain(const std::string & problem) const
  43. {
  44. FAIL() << "Server-side assertion:" << problem;
  45. };
  46. CRandomGenerator & getRandomGenerator() const override
  47. {
  48. return gameState->getRandomGenerator();//todo: mock this
  49. }
  50. const CMap * getMap() const override
  51. {
  52. return map;
  53. }
  54. const CGameInfoCallback * getCb() const override
  55. {
  56. return gameState.get();
  57. }
  58. bool moveHero(ObjectInstanceID hid, int3 dst, bool teleporting) const override
  59. {
  60. return false;
  61. }
  62. void genericQuery(Query * request, PlayerColor color, std::function<void(const JsonNode &)> callback) const
  63. {
  64. //todo:
  65. }
  66. void mapLoaded(CMap * map) override
  67. {
  68. EXPECT_EQ(this->map, nullptr);
  69. this->map = map;
  70. }
  71. void startTestGame()
  72. {
  73. StartInfo si;
  74. si.mapname = "anything";//does not matter, map service mocked
  75. si.difficulty = 0;
  76. si.mapfileChecksum = 0;
  77. si.mode = StartInfo::NEW_GAME;
  78. si.seedToBeUsed = 42;
  79. std::unique_ptr<CMapHeader> header = mapService.loadMapHeader(ResourceID(si.mapname));
  80. ASSERT_NE(header.get(), nullptr);
  81. //FIXME: this has been copied from CPreGame, but should be part of StartInfo
  82. for(int i = 0; i < header->players.size(); i++)
  83. {
  84. const PlayerInfo & pinfo = header->players[i];
  85. //neither computer nor human can play - no player
  86. if (!(pinfo.canHumanPlay || pinfo.canComputerPlay))
  87. continue;
  88. PlayerSettings & pset = si.playerInfos[PlayerColor(i)];
  89. pset.color = PlayerColor(i);
  90. pset.connectedPlayerIDs.insert(i);
  91. pset.name = "Player";
  92. pset.castle = pinfo.defaultCastle();
  93. pset.hero = pinfo.defaultHero();
  94. if(pset.hero != PlayerSettings::RANDOM && pinfo.hasCustomMainHero())
  95. {
  96. pset.hero = pinfo.mainCustomHeroId;
  97. pset.heroName = pinfo.mainCustomHeroName;
  98. pset.heroPortrait = pinfo.mainCustomHeroPortrait;
  99. }
  100. pset.handicap = PlayerSettings::NO_HANDICAP;
  101. }
  102. gameState = std::make_shared<CGameState>();
  103. gameCallback->setGameState(gameState.get());
  104. gameState->init(&mapService, &si, false);
  105. ASSERT_NE(map, nullptr);
  106. ASSERT_EQ(map->heroesOnMap.size(), 2);
  107. }
  108. void startTestBattle(const CGHeroInstance * attacker, const CGHeroInstance * defender)
  109. {
  110. const CGHeroInstance * heroes[2] = {attacker, defender};
  111. const CArmedInstance * armedInstancies[2] = {attacker, defender};
  112. int3 tile(4,4,0);
  113. const auto t = gameCallback->getTile(tile);
  114. ETerrainType terrain = t->terType;
  115. BFieldType terType = BFieldType::GRASS_HILLS;
  116. //send info about battles
  117. BattleInfo * battle = BattleInfo::setupBattle(tile, terrain, terType, armedInstancies, heroes, false, nullptr);
  118. BattleStart bs;
  119. bs.info = battle;
  120. ASSERT_EQ(gameState->curB, nullptr);
  121. gameCallback->sendAndApply(&bs);
  122. ASSERT_EQ(gameState->curB, battle);
  123. }
  124. std::shared_ptr<CGameState> gameState;
  125. std::shared_ptr<GameCallbackMock> gameCallback;
  126. MapServiceMock mapService;
  127. CMap * map;
  128. };
  129. //Issue #2765, Ghost Dragons can cast Age on Catapults
  130. TEST_F(CGameStateTest, issue2765)
  131. {
  132. startTestGame();
  133. CGHeroInstance * attacker = map->heroesOnMap[0];
  134. CGHeroInstance * defender = map->heroesOnMap[1];
  135. ASSERT_NE(attacker->tempOwner, defender->tempOwner);
  136. {
  137. CArtifactInstance * a = new CArtifactInstance();
  138. a->artType = const_cast<CArtifact *>(ArtifactID(ArtifactID::BALLISTA).toArtifact());
  139. NewArtifact na;
  140. na.art = a;
  141. gameCallback->sendAndApply(&na);
  142. PutArtifact pack;
  143. pack.al = ArtifactLocation(defender, ArtifactPosition::MACH1);
  144. pack.art = a;
  145. gameCallback->sendAndApply(&pack);
  146. }
  147. startTestBattle(attacker, defender);
  148. {
  149. battle::UnitInfo info;
  150. info.id = gameState->curB->battleNextUnitId();
  151. info.count = 1;
  152. info.type = CreatureID(69);
  153. info.side = BattleSide::ATTACKER;
  154. info.position = gameState->curB->getAvaliableHex(info.type, info.side);
  155. info.summoned = false;
  156. BattleUnitsChanged pack;
  157. pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
  158. info.save(pack.changedStacks.back().data);
  159. gameCallback->sendAndApply(&pack);
  160. }
  161. const CStack * att = nullptr;
  162. const CStack * def = nullptr;
  163. for(const CStack * s : gameState->curB->stacks)
  164. {
  165. if(s->type->idNumber == CreatureID::BALLISTA)
  166. def = s;
  167. else if(s->type->idNumber == CreatureID(69))
  168. att = s;
  169. }
  170. ASSERT_NE(att, nullptr);
  171. ASSERT_NE(def, nullptr);
  172. ASSERT_EQ(att->getMyHero(), attacker);
  173. ASSERT_EQ(def->getMyHero(), defender);
  174. {
  175. const CSpell * age = SpellID(SpellID::AGE).toSpell();
  176. ASSERT_NE(age, nullptr);
  177. spells::AbilityCaster caster(att, 3);
  178. //here tested ballista, but this applied to all war machines
  179. spells::BattleCast cast(gameState->curB, &caster, spells::Mode::PASSIVE, age);
  180. cast.aimToUnit(def);
  181. EXPECT_FALSE(age->canBeCastAt(gameState->curB, spells::Mode::PASSIVE, &caster, def->getPosition()));
  182. EXPECT_TRUE(cast.castIfPossible(this));//should be possible, but with no effect (change to aimed cast check?)
  183. EXPECT_TRUE(def->activeSpells().empty());
  184. }
  185. }
  186. TEST_F(CGameStateTest, battleResurrection)
  187. {
  188. startTestGame();
  189. CGHeroInstance * attacker = map->heroesOnMap[0];
  190. CGHeroInstance * defender = map->heroesOnMap[1];
  191. ASSERT_NE(attacker->tempOwner, defender->tempOwner);
  192. attacker->setSecSkillLevel(SecondarySkill::EARTH_MAGIC, 3, true);
  193. attacker->spells.insert(SpellID::RESURRECTION);
  194. attacker->setPrimarySkill(PrimarySkill::SPELL_POWER, 100, true);
  195. attacker->setPrimarySkill(PrimarySkill::KNOWLEDGE, 20, true);
  196. attacker->mana = attacker->manaLimit();
  197. {
  198. CArtifactInstance * a = new CArtifactInstance();
  199. a->artType = const_cast<CArtifact *>(ArtifactID(ArtifactID::SPELLBOOK).toArtifact());
  200. NewArtifact na;
  201. na.art = a;
  202. gameCallback->sendAndApply(&na);
  203. PutArtifact pack;
  204. pack.al = ArtifactLocation(attacker, ArtifactPosition::SPELLBOOK);
  205. pack.art = a;
  206. gameCallback->sendAndApply(&pack);
  207. }
  208. startTestBattle(attacker, defender);
  209. uint32_t unitId = gameState->curB->battleNextUnitId();
  210. {
  211. battle::UnitInfo info;
  212. info.id = unitId;
  213. info.count = 10;
  214. info.type = CreatureID(13);
  215. info.side = BattleSide::ATTACKER;
  216. info.position = gameState->curB->getAvaliableHex(info.type, info.side);
  217. info.summoned = false;
  218. BattleUnitsChanged pack;
  219. pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
  220. info.save(pack.changedStacks.back().data);
  221. gameCallback->sendAndApply(&pack);
  222. }
  223. {
  224. battle::UnitInfo info;
  225. info.id = gameState->curB->battleNextUnitId();
  226. info.count = 10;
  227. info.type = CreatureID(13);
  228. info.side = BattleSide::DEFENDER;
  229. info.position = gameState->curB->getAvaliableHex(info.type, info.side);
  230. info.summoned = false;
  231. BattleUnitsChanged pack;
  232. pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
  233. info.save(pack.changedStacks.back().data);
  234. gameCallback->sendAndApply(&pack);
  235. }
  236. CStack * unit = gameState->curB->getStack(unitId);
  237. ASSERT_NE(unit, nullptr);
  238. int64_t damage = unit->MaxHealth() + 1;
  239. unit->damage(damage);
  240. EXPECT_EQ(unit->getCount(), 9);
  241. {
  242. const CSpell * spell = SpellID(SpellID::RESURRECTION).toSpell();
  243. ASSERT_NE(spell, nullptr);
  244. spells::BattleCast cast(gameState->curB, attacker, spells::Mode::HERO, spell);
  245. cast.aimToUnit(unit);
  246. EXPECT_TRUE(spell->canBeCast(gameState->curB, spells::Mode::HERO, attacker));
  247. EXPECT_TRUE(spell->canBeCastAt(gameState->curB, spells::Mode::HERO, attacker, unit->getPosition()));
  248. cast.cast(this);
  249. }
  250. EXPECT_EQ(unit->health.getCount(), 10);
  251. EXPECT_EQ(unit->health.getResurrected(), 0);
  252. }