CGameStateTest.cpp 10 KB

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