CGameStateTest.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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_Services.h"
  12. #include "mock/mock_MapService.h"
  13. #include "mock/mock_IGameCallback.h"
  14. #include "mock/mock_spells_Problem.h"
  15. #include "../../lib/VCMIDirs.h"
  16. #include "../../lib/json/JsonUtils.h"
  17. #include "../../lib/gameState/CGameState.h"
  18. #include "../../lib/networkPacks/PacksForClient.h"
  19. #include "../../lib/networkPacks/PacksForClientBattle.h"
  20. #include "../../lib/networkPacks/SetStackEffect.h"
  21. #include "../../lib/CRandomGenerator.h"
  22. #include "../../lib/StartInfo.h"
  23. #include "../../lib/TerrainHandler.h"
  24. #include "../../lib/battle/BattleInfo.h"
  25. #include "../../lib/battle/BattleLayout.h"
  26. #include "../../lib/callback/GameRandomizer.h"
  27. #include "../../lib/CStack.h"
  28. #include "../../lib/filesystem/ResourcePath.h"
  29. #include "../../lib/mapping/CMap.h"
  30. #include "../../lib/spells/CSpellHandler.h"
  31. #include "../../lib/spells/ISpellMechanics.h"
  32. #include "../../lib/spells/AbilityCaster.h"
  33. class CGameStateTest : public ::testing::Test, public SpellCastEnvironment, public MapListener
  34. {
  35. public:
  36. CGameStateTest()
  37. : gameCallback(new GameCallbackMock(this)),
  38. mapService("test/MiniTest/", this),
  39. map(nullptr)
  40. {
  41. }
  42. void SetUp() override
  43. {
  44. gameState = std::make_shared<CGameState>(gameCallback.get());
  45. gameCallback->setGameState(gameState);
  46. gameState->preInit(&services);
  47. }
  48. void TearDown() override
  49. {
  50. gameState.reset();
  51. }
  52. bool describeChanges() const override
  53. {
  54. return true;
  55. }
  56. void apply(CPackForClient & pack) override
  57. {
  58. gameState->apply(pack);
  59. }
  60. void apply(BattleLogMessage & pack) override
  61. {
  62. gameState->apply(pack);
  63. }
  64. void apply(BattleStackMoved & pack) override
  65. {
  66. gameState->apply(pack);
  67. }
  68. void apply(BattleUnitsChanged & pack) override
  69. {
  70. gameState->apply(pack);
  71. }
  72. void apply(SetStackEffect & pack) override
  73. {
  74. gameState->apply(pack);
  75. }
  76. void apply(StacksInjured & pack) override
  77. {
  78. gameState->apply(pack);
  79. }
  80. void apply(BattleObstaclesChanged & pack) override
  81. {
  82. gameState->apply(pack);
  83. }
  84. void apply(CatapultAttack & pack) override
  85. {
  86. gameState->apply(pack);
  87. }
  88. void complain(const std::string & problem) override
  89. {
  90. FAIL() << "Server-side assertion: " << problem;
  91. };
  92. vstd::RNG * getRNG() override
  93. {
  94. return &randomGenerator;//todo: mock this
  95. }
  96. const CMap * getMap() const override
  97. {
  98. return map;
  99. }
  100. const IGameInfoCallback * getCb() const override
  101. {
  102. return gameState.get();
  103. }
  104. void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) override
  105. {
  106. }
  107. bool moveHero(ObjectInstanceID hid, int3 dst, EMovementMode movementMode) override
  108. {
  109. return false;
  110. }
  111. void genericQuery(Query * request, PlayerColor color, std::function<void(std::optional<int32_t>)> callback) override
  112. {
  113. //todo:
  114. }
  115. void mapLoaded(CMap * map) override
  116. {
  117. EXPECT_EQ(this->map, nullptr);
  118. this->map = map;
  119. }
  120. void startTestGame()
  121. {
  122. StartInfo si;
  123. si.mapname = "anything";//does not matter, map service mocked
  124. si.difficulty = 0;
  125. si.mode = EStartMode::NEW_GAME;
  126. std::unique_ptr<CMapHeader> header = mapService.loadMapHeader(ResourcePath(si.mapname));
  127. ASSERT_NE(header.get(), nullptr);
  128. //FIXME: this has been copied from CPreGame, but should be part of StartInfo
  129. for(int i = 0; i < header->players.size(); i++)
  130. {
  131. const PlayerInfo & pinfo = header->players[i];
  132. //neither computer nor human can play - no player
  133. if (!(pinfo.canHumanPlay || pinfo.canComputerPlay))
  134. continue;
  135. PlayerSettings & pset = si.playerInfos[PlayerColor(i)];
  136. pset.color = PlayerColor(i);
  137. pset.connectedPlayerIDs.insert(i);
  138. pset.name = "Player";
  139. pset.castle = pinfo.defaultCastle();
  140. pset.hero = pinfo.defaultHero();
  141. if(pset.hero != HeroTypeID::RANDOM && pinfo.hasCustomMainHero())
  142. {
  143. pset.hero = pinfo.mainCustomHeroId;
  144. pset.heroNameTextId = pinfo.mainCustomHeroNameTextId;
  145. pset.heroPortrait = HeroTypeID(pinfo.mainCustomHeroPortrait);
  146. }
  147. }
  148. GameRandomizer randomizer(*gameState);
  149. Load::ProgressAccumulator progressTracker;
  150. gameState->init(&mapService, &si, randomizer, progressTracker, false);
  151. ASSERT_NE(map, nullptr);
  152. ASSERT_EQ(map->getHeroesOnMap().size(), 2);
  153. }
  154. void startTestBattle(const CGHeroInstance * attacker, const CGHeroInstance * defender)
  155. {
  156. BattleSideArray<const CGHeroInstance *> heroes = {attacker, defender};
  157. BattleSideArray<const CArmedInstance *> armedInstancies = {attacker, defender};
  158. int3 tile(4,4,0);
  159. const auto & t = *gameCallback->getTile(tile);
  160. auto terrain = t.getTerrainID();
  161. BattleField terType(0);
  162. BattleLayout layout = BattleLayout::createDefaultLayout(gameState->cb, attacker, defender);
  163. //send info about battles
  164. auto battle = BattleInfo::setupBattle(gameState->cb, tile, terrain, terType, armedInstancies, heroes, layout, nullptr);
  165. BattleStart bs;
  166. bs.info = std::move(battle);
  167. ASSERT_EQ(gameState->currentBattles.size(), 0);
  168. gameCallback->sendAndApply(bs);
  169. ASSERT_EQ(gameState->currentBattles.size(), 1);
  170. }
  171. std::shared_ptr<CGameState> gameState;
  172. std::shared_ptr<GameCallbackMock> gameCallback;
  173. MapServiceMock mapService;
  174. ServicesMock services;
  175. CMap * map;
  176. CRandomGenerator randomGenerator;
  177. };
  178. //Issue #2765, Ghost Dragons can cast Age on Catapults
  179. TEST_F(CGameStateTest, DISABLED_issue2765)
  180. {
  181. startTestGame();
  182. auto attackerID = map->getHeroesOnMap()[0];
  183. auto defenderID = map->getHeroesOnMap()[1];
  184. auto attacker = dynamic_cast<CGHeroInstance *>(map->getObject(attackerID));
  185. auto defender = dynamic_cast<CGHeroInstance *>(map->getObject(defenderID));
  186. ASSERT_NE(attacker->tempOwner, defender->tempOwner);
  187. {
  188. NewArtifact na;
  189. na.artHolder = defender->id;
  190. na.artId = ArtifactID::BALLISTA;
  191. na.pos = ArtifactPosition::MACH1;
  192. gameCallback->sendAndApply(na);
  193. }
  194. startTestBattle(attacker, defender);
  195. {
  196. battle::UnitInfo info;
  197. info.id = gameState->currentBattles.front()->battleNextUnitId();
  198. info.count = 1;
  199. info.type = CreatureID(69);
  200. info.side = BattleSide::ATTACKER;
  201. info.position = gameState->currentBattles.front()->getAvailableHex(info.type, info.side);
  202. info.summoned = false;
  203. BattleUnitsChanged pack;
  204. pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
  205. info.save(pack.changedStacks.back().data);
  206. gameCallback->sendAndApply(pack);
  207. }
  208. const CStack * att = nullptr;
  209. const CStack * def = nullptr;
  210. for(const auto & s : gameState->currentBattles.front()->stacks)
  211. {
  212. if(s->unitType()->getId() == CreatureID::BALLISTA && s->unitSide() == BattleSide::DEFENDER)
  213. def = s.get();
  214. else if(s->unitType()->getId() == CreatureID(69) && s->unitSide() == BattleSide::ATTACKER)
  215. att = s.get();
  216. }
  217. ASSERT_NE(att, nullptr);
  218. ASSERT_NE(def, nullptr);
  219. ASSERT_NE(att, def);
  220. EXPECT_NE(att->getMyHero(), defender);
  221. EXPECT_NE(def->getMyHero(), attacker);
  222. EXPECT_EQ(att->getMyHero(), attacker) << att->nodeName();
  223. EXPECT_EQ(def->getMyHero(), defender) << def->nodeName();
  224. {
  225. using namespace ::testing;
  226. spells::ProblemMock problemMock;
  227. // EXPECT_CALL(problemMock, add(_));
  228. const CSpell * age = SpellID(SpellID::AGE).toSpell();
  229. ASSERT_NE(age, nullptr);
  230. spells::AbilityCaster caster(att, 3);
  231. //here tested ballista, but this applied to all war machines
  232. spells::BattleCast cast(gameState->currentBattles.front().get(), &caster, spells::Mode::PASSIVE, age);
  233. spells::Target target;
  234. target.emplace_back(def);
  235. auto m = age->battleMechanics(&cast);
  236. EXPECT_FALSE(m->canBeCastAt(target, problemMock));
  237. EXPECT_TRUE(cast.castIfPossible(this, target));//should be possible, but with no effect (change to aimed cast check?)
  238. EXPECT_TRUE(def->activeSpells().empty());
  239. }
  240. }
  241. TEST_F(CGameStateTest, DISABLED_battleResurrection)
  242. {
  243. startTestGame();
  244. auto attackerID = map->getHeroesOnMap()[0];
  245. auto defenderID = map->getHeroesOnMap()[1];
  246. auto attacker = dynamic_cast<CGHeroInstance *>(map->getObject(attackerID));
  247. auto defender = dynamic_cast<CGHeroInstance *>(map->getObject(defenderID));
  248. ASSERT_NE(attacker->tempOwner, defender->tempOwner);
  249. attacker->setSecSkillLevel(SecondarySkill::EARTH_MAGIC, 3, ChangeValueMode::ABSOLUTE);
  250. attacker->addSpellToSpellbook(SpellID::RESURRECTION);
  251. attacker->setPrimarySkill(PrimarySkill::SPELL_POWER, 100, ChangeValueMode::ABSOLUTE);
  252. attacker->setPrimarySkill(PrimarySkill::KNOWLEDGE, 20, ChangeValueMode::ABSOLUTE);
  253. attacker->mana = attacker->manaLimit();
  254. {
  255. NewArtifact na;
  256. na.artHolder = attacker->id;
  257. na.artId = ArtifactID::SPELLBOOK;
  258. na.pos = ArtifactPosition::SPELLBOOK;
  259. gameCallback->sendAndApply(na);
  260. }
  261. startTestBattle(attacker, defender);
  262. uint32_t unitId = gameState->currentBattles.front()->battleNextUnitId();
  263. {
  264. battle::UnitInfo info;
  265. info.id = unitId;
  266. info.count = 10;
  267. info.type = CreatureID(13);
  268. info.side = BattleSide::ATTACKER;
  269. info.position = gameState->currentBattles.front()->getAvailableHex(info.type, info.side);
  270. info.summoned = false;
  271. BattleUnitsChanged pack;
  272. pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
  273. info.save(pack.changedStacks.back().data);
  274. gameCallback->sendAndApply(pack);
  275. }
  276. {
  277. battle::UnitInfo info;
  278. info.id = gameState->currentBattles.front()->battleNextUnitId();
  279. info.count = 10;
  280. info.type = CreatureID(13);
  281. info.side = BattleSide::DEFENDER;
  282. info.position = gameState->currentBattles.front()->getAvailableHex(info.type, info.side);
  283. info.summoned = false;
  284. BattleUnitsChanged pack;
  285. pack.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
  286. info.save(pack.changedStacks.back().data);
  287. gameCallback->sendAndApply(pack);
  288. }
  289. CStack * unit = gameState->currentBattles.front()->getStack(unitId);
  290. ASSERT_NE(unit, nullptr);
  291. int64_t damage = unit->getMaxHealth() + 1;
  292. unit->damage(damage);
  293. EXPECT_EQ(unit->getCount(), 9);
  294. {
  295. using namespace ::testing;
  296. spells::ProblemMock problemMock;
  297. EXPECT_CALL(problemMock, add(_)).Times(AnyNumber()); //todo: do smth with problems of optional effects
  298. const CSpell * spell = SpellID(SpellID::RESURRECTION).toSpell();
  299. ASSERT_NE(spell, nullptr);
  300. spells::BattleCast cast(gameState->currentBattles.front().get(), attacker, spells::Mode::HERO, spell);
  301. spells::Target target;
  302. target.emplace_back(unit);
  303. auto m = spell->battleMechanics(&cast);
  304. EXPECT_TRUE(m->canBeCast(problemMock));
  305. EXPECT_TRUE(m->canBeCastAt(target, problemMock));
  306. cast.cast(this, target);
  307. //
  308. // std::vector<std::string> expLog;
  309. //
  310. // EXPECT_THAT(problemMock.log, ContainerEq(expLog));
  311. }
  312. EXPECT_EQ(unit->health.getCount(), 10);
  313. EXPECT_EQ(unit->health.getResurrected(), 0);
  314. }