CGameStateTest.cpp 10 KB

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