NetPacksClient.cpp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. /*
  2. * NetPacksClient.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 "ClientNetPackVisitors.h"
  12. #include "Client.h"
  13. #include "CPlayerInterface.h"
  14. #include "CGameInfo.h"
  15. #include "windows/GUIClasses.h"
  16. #include "mapView/mapHandler.h"
  17. #include "adventureMap/AdventureMapInterface.h"
  18. #include "adventureMap/CInGameConsole.h"
  19. #include "battle/BattleInterface.h"
  20. #include "battle/BattleWindow.h"
  21. #include "gui/CGuiHandler.h"
  22. #include "gui/WindowHandler.h"
  23. #include "widgets/MiscWidgets.h"
  24. #include "CMT.h"
  25. #include "GameChatHandler.h"
  26. #include "CServerHandler.h"
  27. #include "PlayerLocalState.h"
  28. #include "../CCallback.h"
  29. #include "../lib/filesystem/Filesystem.h"
  30. #include "../lib/filesystem/FileInfo.h"
  31. #include "../lib/serializer/Connection.h"
  32. #include "../lib/texts/CGeneralTextHandler.h"
  33. #include "../lib/CHeroHandler.h"
  34. #include "../lib/VCMI_Lib.h"
  35. #include "../lib/mapping/CMap.h"
  36. #include "../lib/VCMIDirs.h"
  37. #include "../lib/spells/CSpellHandler.h"
  38. #include "../lib/CSoundBase.h"
  39. #include "../lib/StartInfo.h"
  40. #include "../lib/CConfigHandler.h"
  41. #include "../lib/mapObjects/CGMarket.h"
  42. #include "../lib/mapObjects/CGTownInstance.h"
  43. #include "../lib/gameState/CGameState.h"
  44. #include "../lib/CStack.h"
  45. #include "../lib/battle/BattleInfo.h"
  46. #include "../lib/GameConstants.h"
  47. #include "../lib/CPlayerState.h"
  48. // TODO: as Tow suggested these template should all be part of CClient
  49. // This will require rework spectator interface properly though
  50. template<typename T, typename ... Args, typename ... Args2>
  51. bool callOnlyThatInterface(CClient & cl, PlayerColor player, void (T::*ptr)(Args...), Args2 && ...args)
  52. {
  53. if(vstd::contains(cl.playerint, player))
  54. {
  55. ((*cl.playerint[player]).*ptr)(std::forward<Args2>(args)...);
  56. return true;
  57. }
  58. return false;
  59. }
  60. template<typename T, typename ... Args, typename ... Args2>
  61. bool callInterfaceIfPresent(CClient & cl, PlayerColor player, void (T::*ptr)(Args...), Args2 && ...args)
  62. {
  63. bool called = callOnlyThatInterface(cl, player, ptr, std::forward<Args2>(args)...);
  64. return called;
  65. }
  66. template<typename T, typename ... Args, typename ... Args2>
  67. void callOnlyThatBattleInterface(CClient & cl, PlayerColor player, void (T::*ptr)(Args...), Args2 && ...args)
  68. {
  69. if(vstd::contains(cl.battleints,player))
  70. ((*cl.battleints[player]).*ptr)(std::forward<Args2>(args)...);
  71. if(cl.additionalBattleInts.count(player))
  72. {
  73. for(auto bInt : cl.additionalBattleInts[player])
  74. ((*bInt).*ptr)(std::forward<Args2>(args)...);
  75. }
  76. }
  77. template<typename T, typename ... Args, typename ... Args2>
  78. void callBattleInterfaceIfPresent(CClient & cl, PlayerColor player, void (T::*ptr)(Args...), Args2 && ...args)
  79. {
  80. callOnlyThatInterface(cl, player, ptr, std::forward<Args2>(args)...);
  81. }
  82. //calls all normal interfaces and privileged ones, playerints may be updated when iterating over it, so we need a copy
  83. template<typename T, typename ... Args, typename ... Args2>
  84. void callAllInterfaces(CClient & cl, void (T::*ptr)(Args...), Args2 && ...args)
  85. {
  86. for(auto pInt : cl.playerint)
  87. ((*pInt.second).*ptr)(std::forward<Args2>(args)...);
  88. }
  89. //calls all normal interfaces and privileged ones, playerints may be updated when iterating over it, so we need a copy
  90. template<typename T, typename ... Args, typename ... Args2>
  91. void callBattleInterfaceIfPresentForBothSides(CClient & cl, const BattleID & battleID, void (T::*ptr)(Args...), Args2 && ...args)
  92. {
  93. assert(cl.gameState()->getBattle(battleID));
  94. if(!cl.gameState()->getBattle(battleID))
  95. {
  96. logGlobal->error("Attempt to call battle interface without ongoing battle!");
  97. return;
  98. }
  99. callOnlyThatBattleInterface(cl, cl.gameState()->getBattle(battleID)->getSide(BattleSide::ATTACKER).color, ptr, std::forward<Args2>(args)...);
  100. callOnlyThatBattleInterface(cl, cl.gameState()->getBattle(battleID)->getSide(BattleSide::DEFENDER).color, ptr, std::forward<Args2>(args)...);
  101. if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool() && LOCPLINT->battleInt)
  102. {
  103. callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, ptr, std::forward<Args2>(args)...);
  104. }
  105. }
  106. void ApplyClientNetPackVisitor::visitSetResources(SetResources & pack)
  107. {
  108. //todo: inform on actual resource set transferred
  109. callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::receivedResource);
  110. }
  111. void ApplyClientNetPackVisitor::visitSetPrimSkill(SetPrimSkill & pack)
  112. {
  113. const CGHeroInstance * h = cl.getHero(pack.id);
  114. if(!h)
  115. {
  116. logNetwork->error("Cannot find hero with pack.id %d", pack.id.getNum());
  117. return;
  118. }
  119. callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroPrimarySkillChanged, h, pack.which, pack.val);
  120. }
  121. void ApplyClientNetPackVisitor::visitSetSecSkill(SetSecSkill & pack)
  122. {
  123. const CGHeroInstance *h = cl.getHero(pack.id);
  124. if(!h)
  125. {
  126. logNetwork->error("Cannot find hero with pack.id %d", pack.id.getNum());
  127. return;
  128. }
  129. callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroSecondarySkillChanged, h, pack.which, pack.val);
  130. }
  131. void ApplyClientNetPackVisitor::visitHeroVisitCastle(HeroVisitCastle & pack)
  132. {
  133. const CGHeroInstance *h = cl.getHero(pack.hid);
  134. if(pack.start())
  135. {
  136. callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroVisitsTown, h, gs.getTown(pack.tid));
  137. }
  138. }
  139. void ApplyClientNetPackVisitor::visitSetMana(SetMana & pack)
  140. {
  141. const CGHeroInstance *h = cl.getHero(pack.hid);
  142. callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroManaPointsChanged, h);
  143. if(settings["session"]["headless"].Bool())
  144. return;
  145. for(auto window : GH.windows().findWindows<BattleWindow>())
  146. window->heroManaPointsChanged(h);
  147. }
  148. void ApplyClientNetPackVisitor::visitSetMovePoints(SetMovePoints & pack)
  149. {
  150. const CGHeroInstance *h = cl.getHero(pack.hid);
  151. cl.invalidatePaths();
  152. callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroMovePointsChanged, h);
  153. }
  154. void ApplyClientNetPackVisitor::visitFoWChange(FoWChange & pack)
  155. {
  156. for(auto &i : cl.playerint)
  157. {
  158. if(cl.getPlayerRelations(i.first, pack.player) == PlayerRelations::SAME_PLAYER && pack.waitForDialogs && LOCPLINT == i.second.get())
  159. {
  160. LOCPLINT->waitWhileDialog();
  161. }
  162. if(cl.getPlayerRelations(i.first, pack.player) != PlayerRelations::ENEMIES)
  163. {
  164. if(pack.mode == ETileVisibility::REVEALED)
  165. i.second->tileRevealed(pack.tiles);
  166. else
  167. i.second->tileHidden(pack.tiles);
  168. }
  169. }
  170. cl.invalidatePaths();
  171. }
  172. static void dispatchGarrisonChange(CClient & cl, ObjectInstanceID army1, ObjectInstanceID army2)
  173. {
  174. auto obj1 = cl.getObj(army1);
  175. if(!obj1)
  176. {
  177. logNetwork->error("Cannot find army with pack.id %d", army1.getNum());
  178. return;
  179. }
  180. callInterfaceIfPresent(cl, obj1->tempOwner, &IGameEventsReceiver::garrisonsChanged, army1, army2);
  181. if(army2 != ObjectInstanceID() && army2 != army1)
  182. {
  183. auto obj2 = cl.getObj(army2);
  184. if(!obj2)
  185. {
  186. logNetwork->error("Cannot find army with pack.id %d", army2.getNum());
  187. return;
  188. }
  189. if(obj1->tempOwner != obj2->tempOwner)
  190. callInterfaceIfPresent(cl, obj2->tempOwner, &IGameEventsReceiver::garrisonsChanged, army1, army2);
  191. }
  192. }
  193. void ApplyClientNetPackVisitor::visitChangeStackCount(ChangeStackCount & pack)
  194. {
  195. dispatchGarrisonChange(cl, pack.army, ObjectInstanceID());
  196. }
  197. void ApplyClientNetPackVisitor::visitSetStackType(SetStackType & pack)
  198. {
  199. dispatchGarrisonChange(cl, pack.army, ObjectInstanceID());
  200. }
  201. void ApplyClientNetPackVisitor::visitEraseStack(EraseStack & pack)
  202. {
  203. dispatchGarrisonChange(cl, pack.army, ObjectInstanceID());
  204. cl.invalidatePaths(); //it is possible to remove last non-native unit for current terrain and lose movement penalty
  205. if(LOCPLINT)
  206. {
  207. for(const auto hero : LOCPLINT->localState->getWanderingHeroes())
  208. {
  209. if(hero->id == pack.army)
  210. LOCPLINT->localState->verifyPath(hero);
  211. }
  212. }
  213. }
  214. void ApplyClientNetPackVisitor::visitSwapStacks(SwapStacks & pack)
  215. {
  216. dispatchGarrisonChange(cl, pack.srcArmy, pack.dstArmy);
  217. if(pack.srcArmy != pack.dstArmy)
  218. {
  219. cl.invalidatePaths(); // adding/removing units may change terrain type penalty based on creature native terrains
  220. if(LOCPLINT)
  221. {
  222. for(const auto hero : LOCPLINT->localState->getWanderingHeroes())
  223. {
  224. if(hero->id == pack.dstArmy)
  225. LOCPLINT->localState->verifyPath(hero);
  226. }
  227. }
  228. }
  229. }
  230. void ApplyClientNetPackVisitor::visitInsertNewStack(InsertNewStack & pack)
  231. {
  232. dispatchGarrisonChange(cl, pack.army, ObjectInstanceID());
  233. auto hero = gs.getHero(pack.army);
  234. if(hero)
  235. {
  236. cl.invalidatePaths(); // adding/removing units may change terrain type penalty based on creature native terrains
  237. if(LOCPLINT)
  238. {
  239. LOCPLINT->localState->verifyPath(hero);
  240. }
  241. }
  242. }
  243. void ApplyClientNetPackVisitor::visitRebalanceStacks(RebalanceStacks & pack)
  244. {
  245. dispatchGarrisonChange(cl, pack.srcArmy, pack.dstArmy);
  246. if(pack.srcArmy != pack.dstArmy)
  247. {
  248. cl.invalidatePaths(); // adding/removing units may change terrain type penalty based on creature native terrains
  249. if(LOCPLINT)
  250. {
  251. for(const auto hero : LOCPLINT->localState->getWanderingHeroes())
  252. {
  253. if(hero->id == pack.dstArmy || hero->id == pack.srcArmy)
  254. LOCPLINT->localState->verifyPath(hero);
  255. }
  256. }
  257. }
  258. }
  259. void ApplyClientNetPackVisitor::visitBulkRebalanceStacks(BulkRebalanceStacks & pack)
  260. {
  261. if(!pack.moves.empty())
  262. {
  263. auto destArmy = pack.moves[0].srcArmy == pack.moves[0].dstArmy
  264. ? ObjectInstanceID()
  265. : pack.moves[0].dstArmy;
  266. dispatchGarrisonChange(cl, pack.moves[0].srcArmy, destArmy);
  267. if(pack.moves[0].srcArmy != destArmy)
  268. {
  269. cl.invalidatePaths(); // adding/removing units may change terrain type penalty based on creature native terrains
  270. if(LOCPLINT)
  271. {
  272. // update all paths
  273. for(const auto hero : LOCPLINT->localState->getWanderingHeroes())
  274. {
  275. if(hero->id == destArmy || hero->id == pack.moves[0].srcArmy)
  276. LOCPLINT->localState->verifyPath(hero);
  277. }
  278. }
  279. }
  280. }
  281. }
  282. void ApplyClientNetPackVisitor::visitBulkSmartRebalanceStacks(BulkSmartRebalanceStacks & pack)
  283. {
  284. if(!pack.moves.empty())
  285. {
  286. assert(pack.moves[0].srcArmy == pack.moves[0].dstArmy);
  287. dispatchGarrisonChange(cl, pack.moves[0].srcArmy, ObjectInstanceID());
  288. }
  289. else if(!pack.changes.empty())
  290. {
  291. dispatchGarrisonChange(cl, pack.changes[0].army, ObjectInstanceID());
  292. }
  293. }
  294. void ApplyClientNetPackVisitor::visitPutArtifact(PutArtifact & pack)
  295. {
  296. callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactPut, pack.al);
  297. if(pack.askAssemble)
  298. callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::askToAssembleArtifact, pack.al);
  299. }
  300. void ApplyClientNetPackVisitor::visitEraseArtifact(BulkEraseArtifacts & pack)
  301. {
  302. for(const auto & slotErase : pack.posPack)
  303. callInterfaceIfPresent(cl, cl.getOwner(pack.artHolder), &IGameEventsReceiver::artifactRemoved, ArtifactLocation(pack.artHolder, slotErase));
  304. }
  305. void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack)
  306. {
  307. const auto dstOwner = cl.getOwner(pack.dstArtHolder);
  308. const auto applyMove = [this, &pack, dstOwner](std::vector<BulkMoveArtifacts::LinkedSlots> & artsPack)
  309. {
  310. for(const auto & slotToMove : artsPack)
  311. {
  312. const auto srcLoc = ArtifactLocation(pack.srcArtHolder, slotToMove.srcPos);
  313. const auto dstLoc = ArtifactLocation(pack.dstArtHolder, slotToMove.dstPos);
  314. callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc);
  315. if(slotToMove.askAssemble)
  316. callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::askToAssembleArtifact, dstLoc);
  317. if(pack.interfaceOwner != dstOwner)
  318. callInterfaceIfPresent(cl, dstOwner, &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc);
  319. cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings
  320. if(LOCPLINT)
  321. {
  322. for(const auto hero : LOCPLINT->localState->getWanderingHeroes())
  323. {
  324. if(hero->id == pack.srcArtHolder || hero->id == pack.dstArtHolder)
  325. LOCPLINT->localState->verifyPath(hero);
  326. }
  327. }
  328. }
  329. };
  330. size_t possibleAssemblyNumOfArts = 0;
  331. const auto calcPossibleAssemblyNumOfArts = [&possibleAssemblyNumOfArts](const auto & slotToMove)
  332. {
  333. if(slotToMove.askAssemble)
  334. possibleAssemblyNumOfArts++;
  335. };
  336. std::for_each(pack.artsPack0.cbegin(), pack.artsPack0.cend(), calcPossibleAssemblyNumOfArts);
  337. std::for_each(pack.artsPack1.cbegin(), pack.artsPack1.cend(), calcPossibleAssemblyNumOfArts);
  338. // Begin a session of bulk movement of arts. It is not necessary but useful for the client optimization.
  339. callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::bulkArtMovementStart,
  340. pack.artsPack0.size() + pack.artsPack1.size(), possibleAssemblyNumOfArts);
  341. if(pack.interfaceOwner != dstOwner)
  342. callInterfaceIfPresent(cl, dstOwner, &IGameEventsReceiver::bulkArtMovementStart,
  343. pack.artsPack0.size() + pack.artsPack1.size(), possibleAssemblyNumOfArts);
  344. applyMove(pack.artsPack0);
  345. if(!pack.artsPack1.empty())
  346. applyMove(pack.artsPack1);
  347. }
  348. void ApplyClientNetPackVisitor::visitAssembledArtifact(AssembledArtifact & pack)
  349. {
  350. callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactAssembled, pack.al);
  351. cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings
  352. if(LOCPLINT)
  353. {
  354. for(const auto hero : LOCPLINT->localState->getWanderingHeroes())
  355. {
  356. if(hero->id == pack.al.artHolder)
  357. LOCPLINT->localState->verifyPath(hero);
  358. }
  359. }
  360. }
  361. void ApplyClientNetPackVisitor::visitDisassembledArtifact(DisassembledArtifact & pack)
  362. {
  363. callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactDisassembled, pack.al);
  364. cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings
  365. if(LOCPLINT)
  366. {
  367. for(const auto hero : LOCPLINT->localState->getWanderingHeroes())
  368. {
  369. if(hero->id == pack.al.artHolder)
  370. LOCPLINT->localState->verifyPath(hero);
  371. }
  372. }
  373. }
  374. void ApplyClientNetPackVisitor::visitHeroVisit(HeroVisit & pack)
  375. {
  376. auto hero = cl.getHero(pack.heroId);
  377. auto obj = cl.getObj(pack.objId, false);
  378. callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::heroVisit, hero, obj, pack.starting);
  379. }
  380. void ApplyClientNetPackVisitor::visitNewTurn(NewTurn & pack)
  381. {
  382. cl.invalidatePaths();
  383. if(pack.newWeekNotification)
  384. {
  385. const auto & newWeek = *pack.newWeekNotification;
  386. std::string str = newWeek.text.toString();
  387. callAllInterfaces(cl, &CGameInterface::showInfoDialog, newWeek.type, str, newWeek.components,(soundBase::soundID)newWeek.soundID);
  388. }
  389. }
  390. void ApplyClientNetPackVisitor::visitGiveBonus(GiveBonus & pack)
  391. {
  392. cl.invalidatePaths();
  393. switch(pack.who)
  394. {
  395. case GiveBonus::ETarget::OBJECT:
  396. {
  397. const CGHeroInstance *h = gs.getHero(pack.id.as<ObjectInstanceID>());
  398. if(h)
  399. callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, pack.bonus, true);
  400. }
  401. break;
  402. case GiveBonus::ETarget::PLAYER:
  403. {
  404. callInterfaceIfPresent(cl, pack.id.as<PlayerColor>(), &IGameEventsReceiver::playerBonusChanged, pack.bonus, true);
  405. }
  406. break;
  407. }
  408. }
  409. void ApplyFirstClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack)
  410. {
  411. CGObjectInstance *obj = gs.getObjInstance(pack.objid);
  412. if(CGI && CGI->mh)
  413. {
  414. CGI->mh->onObjectFadeOut(obj, pack.initiator);
  415. CGI->mh->waitForOngoingAnimations();
  416. }
  417. }
  418. void ApplyClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack)
  419. {
  420. CGObjectInstance *obj = gs.getObjInstance(pack.objid);
  421. if(CGI && CGI->mh)
  422. {
  423. CGI->mh->onObjectFadeIn(obj, pack.initiator);
  424. CGI->mh->waitForOngoingAnimations();
  425. }
  426. cl.invalidatePaths();
  427. }
  428. void ApplyClientNetPackVisitor::visitPlayerEndsGame(PlayerEndsGame & pack)
  429. {
  430. callAllInterfaces(cl, &IGameEventsReceiver::gameOver, pack.player, pack.victoryLossCheckResult);
  431. bool lastHumanEndsGame = CSH->howManyPlayerInterfaces() == 1 && vstd::contains(cl.playerint, pack.player) && cl.getPlayerState(pack.player)->human && !settings["session"]["spectate"].Bool();
  432. if(lastHumanEndsGame)
  433. {
  434. assert(adventureInt);
  435. if(adventureInt)
  436. {
  437. GH.windows().popWindows(GH.windows().count());
  438. adventureInt.reset();
  439. }
  440. CSH->showHighScoresAndEndGameplay(pack.player, pack.victoryLossCheckResult.victory(), pack.statistic);
  441. }
  442. // In auto testing pack.mode we always close client if red pack.player won or lose
  443. if(!settings["session"]["testmap"].isNull() && pack.player == PlayerColor(0))
  444. {
  445. logAi->info("Red player %s. Ending game.", pack.victoryLossCheckResult.victory() ? "won" : "lost");
  446. handleQuit(settings["session"]["spectate"].Bool()); // if spectator is active ask to close client or not
  447. }
  448. }
  449. void ApplyClientNetPackVisitor::visitPlayerReinitInterface(PlayerReinitInterface & pack)
  450. {
  451. auto initInterfaces = [this]()
  452. {
  453. cl.initPlayerInterfaces();
  454. for(PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
  455. {
  456. if(cl.gameState()->isPlayerMakingTurn(player))
  457. {
  458. callAllInterfaces(cl, &IGameEventsReceiver::playerStartsTurn, player);
  459. callOnlyThatInterface(cl, player, &CGameInterface::yourTurn, QueryID::NONE);
  460. }
  461. }
  462. };
  463. for(auto player : pack.players)
  464. {
  465. auto & plSettings = CSH->si->getIthPlayersSettings(player);
  466. if(pack.playerConnectionId == PlayerSettings::PLAYER_AI)
  467. {
  468. plSettings.connectedPlayerIDs.clear();
  469. cl.initPlayerEnvironments();
  470. initInterfaces();
  471. }
  472. else if(pack.playerConnectionId == CSH->logicConnection->connectionID)
  473. {
  474. plSettings.connectedPlayerIDs.insert(pack.playerConnectionId);
  475. cl.playerint.clear();
  476. initInterfaces();
  477. }
  478. }
  479. }
  480. void ApplyClientNetPackVisitor::visitRemoveBonus(RemoveBonus & pack)
  481. {
  482. cl.invalidatePaths();
  483. switch(pack.who)
  484. {
  485. case GiveBonus::ETarget::OBJECT:
  486. {
  487. const CGHeroInstance *h = gs.getHero(pack.whoID.as<ObjectInstanceID>());
  488. if(h)
  489. callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroBonusChanged, h, pack.bonus, false);
  490. }
  491. break;
  492. case GiveBonus::ETarget::PLAYER:
  493. {
  494. //const PlayerState *p = gs.getPlayerState(pack.id);
  495. callInterfaceIfPresent(cl, pack.whoID.as<PlayerColor>(), &IGameEventsReceiver::playerBonusChanged, pack.bonus, false);
  496. }
  497. break;
  498. }
  499. }
  500. void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
  501. {
  502. const CGObjectInstance *o = cl.getObj(pack.objectID);
  503. if(CGI->mh)
  504. CGI->mh->onObjectFadeOut(o, pack.initiator);
  505. //notify interfaces about removal
  506. for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
  507. {
  508. //below line contains little cheat for AI so it will be aware of deletion of enemy heroes that moved or got re-covered by FoW
  509. //TODO: loose requirements as next AI related crashes appear, for example another pack.player collects object that got re-covered by FoW, unsure if AI code workarounds this
  510. if(gs.isVisible(o, i->first) || (!cl.getPlayerState(i->first)->human && o->ID == Obj::HERO && o->tempOwner != i->first))
  511. i->second->objectRemoved(o, pack.initiator);
  512. }
  513. if(CGI->mh)
  514. CGI->mh->waitForOngoingAnimations();
  515. }
  516. void ApplyClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
  517. {
  518. cl.invalidatePaths();
  519. for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
  520. i->second->objectRemovedAfter();
  521. }
  522. void ApplyFirstClientNetPackVisitor::visitTryMoveHero(TryMoveHero & pack)
  523. {
  524. CGHeroInstance *h = gs.getHero(pack.id);
  525. if(CGI->mh)
  526. {
  527. switch (pack.result)
  528. {
  529. case TryMoveHero::EMBARK:
  530. CGI->mh->onBeforeHeroEmbark(h, pack.start, pack.end);
  531. break;
  532. case TryMoveHero::TELEPORTATION:
  533. CGI->mh->onBeforeHeroTeleported(h, pack.start, pack.end);
  534. break;
  535. case TryMoveHero::DISEMBARK:
  536. CGI->mh->onBeforeHeroDisembark(h, pack.start, pack.end);
  537. break;
  538. }
  539. CGI->mh->waitForOngoingAnimations();
  540. }
  541. }
  542. void ApplyClientNetPackVisitor::visitTryMoveHero(TryMoveHero & pack)
  543. {
  544. const CGHeroInstance *h = cl.getHero(pack.id);
  545. cl.invalidatePaths();
  546. if(CGI->mh)
  547. {
  548. switch(pack.result)
  549. {
  550. case TryMoveHero::SUCCESS:
  551. CGI->mh->onHeroMoved(h, pack.start, pack.end);
  552. break;
  553. case TryMoveHero::EMBARK:
  554. CGI->mh->onAfterHeroEmbark(h, pack.start, pack.end);
  555. break;
  556. case TryMoveHero::TELEPORTATION:
  557. CGI->mh->onAfterHeroTeleported(h, pack.start, pack.end);
  558. break;
  559. case TryMoveHero::DISEMBARK:
  560. CGI->mh->onAfterHeroDisembark(h, pack.start, pack.end);
  561. break;
  562. }
  563. }
  564. PlayerColor player = h->tempOwner;
  565. for(auto &i : cl.playerint)
  566. if(cl.getPlayerRelations(i.first, player) != PlayerRelations::ENEMIES)
  567. i.second->tileRevealed(pack.fowRevealed);
  568. for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
  569. {
  570. if(i->first != PlayerColor::SPECTATOR && gs.checkForStandardLoss(i->first)) // Do not notify vanquished pack.player's interface
  571. continue;
  572. if(gs.isVisible(h->convertToVisitablePos(pack.start), i->first)
  573. || gs.isVisible(h->convertToVisitablePos(pack.end), i->first))
  574. {
  575. // pack.src and pack.dst of enemy hero move may be not visible => 'verbose' should be false
  576. const bool verbose = cl.getPlayerRelations(i->first, player) != PlayerRelations::ENEMIES;
  577. i->second->heroMoved(pack, verbose);
  578. }
  579. }
  580. }
  581. void ApplyClientNetPackVisitor::visitNewStructures(NewStructures & pack)
  582. {
  583. CGTownInstance *town = gs.getTown(pack.tid);
  584. for(const auto & id : pack.bid)
  585. {
  586. callInterfaceIfPresent(cl, town->getOwner(), &IGameEventsReceiver::buildChanged, town, id, 1);
  587. }
  588. // invalidate section of map view with our object and force an update
  589. if(CGI->mh)
  590. {
  591. CGI->mh->onObjectInstantRemove(town, town->getOwner());
  592. CGI->mh->onObjectInstantAdd(town, town->getOwner());
  593. }
  594. }
  595. void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack)
  596. {
  597. CGTownInstance * town = gs.getTown(pack.tid);
  598. for(const auto & id : pack.bid)
  599. {
  600. callInterfaceIfPresent(cl, town->getOwner(), &IGameEventsReceiver::buildChanged, town, id, 2);
  601. }
  602. // invalidate section of map view with our object and force an update
  603. if(CGI->mh)
  604. {
  605. CGI->mh->onObjectInstantRemove(town, town->getOwner());
  606. CGI->mh->onObjectInstantAdd(town, town->getOwner());
  607. }
  608. }
  609. void ApplyClientNetPackVisitor::visitSetAvailableCreatures(SetAvailableCreatures & pack)
  610. {
  611. const CGDwelling * dw = static_cast<const CGDwelling*>(cl.getObj(pack.tid));
  612. PlayerColor p;
  613. if(dw->ID == Obj::WAR_MACHINE_FACTORY) //War Machines Factory is not flaggable, it's "owned" by visitor
  614. p = cl.getTile(dw->visitablePos())->visitableObjects.back()->tempOwner;
  615. else
  616. p = dw->tempOwner;
  617. callInterfaceIfPresent(cl, p, &IGameEventsReceiver::availableCreaturesChanged, dw);
  618. }
  619. void ApplyClientNetPackVisitor::visitSetHeroesInTown(SetHeroesInTown & pack)
  620. {
  621. CGTownInstance * t = gs.getTown(pack.tid);
  622. CGHeroInstance * hGarr = gs.getHero(pack.garrison);
  623. CGHeroInstance * hVisit = gs.getHero(pack.visiting);
  624. //inform all players that see this object
  625. for(auto i = cl.playerint.cbegin(); i != cl.playerint.cend(); ++i)
  626. {
  627. if(!i->first.isValidPlayer())
  628. continue;
  629. if(gs.isVisible(t, i->first) ||
  630. (hGarr && gs.isVisible(hGarr, i->first)) ||
  631. (hVisit && gs.isVisible(hVisit, i->first)))
  632. {
  633. cl.playerint[i->first]->heroInGarrisonChange(t);
  634. }
  635. }
  636. }
  637. void ApplyClientNetPackVisitor::visitHeroRecruited(HeroRecruited & pack)
  638. {
  639. CGHeroInstance *h = gs.map->heroesOnMap.back();
  640. if(h->getHeroType() != pack.hid)
  641. {
  642. logNetwork->error("Something wrong with hero recruited!");
  643. }
  644. if(callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroCreated, h))
  645. {
  646. if(const CGTownInstance *t = gs.getTown(pack.tid))
  647. callInterfaceIfPresent(cl, h->getOwner(), &IGameEventsReceiver::heroInGarrisonChange, t);
  648. }
  649. if(CGI->mh)
  650. CGI->mh->onObjectInstantAdd(h, h->getOwner());
  651. }
  652. void ApplyClientNetPackVisitor::visitGiveHero(GiveHero & pack)
  653. {
  654. CGHeroInstance *h = gs.getHero(pack.id);
  655. if(CGI->mh)
  656. CGI->mh->onObjectInstantAdd(h, h->getOwner());
  657. callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroCreated, h);
  658. }
  659. void ApplyFirstClientNetPackVisitor::visitGiveHero(GiveHero & pack)
  660. {
  661. }
  662. void ApplyClientNetPackVisitor::visitInfoWindow(InfoWindow & pack)
  663. {
  664. std::string str = pack.text.toString();
  665. if(!callInterfaceIfPresent(cl, pack.player, &CGameInterface::showInfoDialog, pack.type, str, pack.components,(soundBase::soundID)pack.soundID))
  666. logNetwork->warn("We received InfoWindow for not our player...");
  667. }
  668. void ApplyFirstClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack)
  669. {
  670. //inform all players that see this object
  671. for(auto it = cl.playerint.cbegin(); it != cl.playerint.cend(); ++it)
  672. {
  673. if(gs.isVisible(gs.getObjInstance(pack.id), it->first))
  674. callInterfaceIfPresent(cl, it->first, &IGameEventsReceiver::beforeObjectPropertyChanged, &pack);
  675. }
  676. // invalidate section of map view with our object and force an update with new flag color
  677. if(pack.what == ObjProperty::OWNER && CGI->mh)
  678. {
  679. auto object = gs.getObjInstance(pack.id);
  680. CGI->mh->onObjectInstantRemove(object, object->getOwner());
  681. }
  682. }
  683. void ApplyClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack)
  684. {
  685. //inform all players that see this object
  686. for(auto it = cl.playerint.cbegin(); it != cl.playerint.cend(); ++it)
  687. {
  688. if(gs.isVisible(gs.getObjInstance(pack.id), it->first))
  689. callInterfaceIfPresent(cl, it->first, &IGameEventsReceiver::objectPropertyChanged, &pack);
  690. }
  691. // invalidate section of map view with our object and force an update with new flag color
  692. if(pack.what == ObjProperty::OWNER && CGI->mh)
  693. {
  694. auto object = gs.getObjInstance(pack.id);
  695. CGI->mh->onObjectInstantAdd(object, object->getOwner());
  696. }
  697. }
  698. void ApplyClientNetPackVisitor::visitHeroLevelUp(HeroLevelUp & pack)
  699. {
  700. const CGHeroInstance * hero = cl.getHero(pack.heroId);
  701. assert(hero);
  702. callOnlyThatInterface(cl, pack.player, &CGameInterface::heroGotLevel, hero, pack.primskill, pack.skills, pack.queryID);
  703. }
  704. void ApplyClientNetPackVisitor::visitCommanderLevelUp(CommanderLevelUp & pack)
  705. {
  706. const CGHeroInstance * hero = cl.getHero(pack.heroId);
  707. assert(hero);
  708. const CCommanderInstance * commander = hero->commander;
  709. assert(commander);
  710. assert(commander->armyObj); //is it possible for Commander to exist beyond armed instance?
  711. callOnlyThatInterface(cl, pack.player, &CGameInterface::commanderGotLevel, commander, pack.skills, pack.queryID);
  712. }
  713. void ApplyClientNetPackVisitor::visitBlockingDialog(BlockingDialog & pack)
  714. {
  715. std::string str = pack.text.toString();
  716. if(!callOnlyThatInterface(cl, pack.player, &CGameInterface::showBlockingDialog, str, pack.components, pack.queryID, (soundBase::soundID)pack.soundID, pack.selection(), pack.cancel(), pack.safeToAutoaccept()))
  717. logNetwork->warn("We received YesNoDialog for not our player...");
  718. }
  719. void ApplyClientNetPackVisitor::visitGarrisonDialog(GarrisonDialog & pack)
  720. {
  721. const CGHeroInstance *h = cl.getHero(pack.hid);
  722. const CArmedInstance *obj = static_cast<const CArmedInstance*>(cl.getObj(pack.objid));
  723. callOnlyThatInterface(cl, h->getOwner(), &CGameInterface::showGarrisonDialog, obj, h, pack.removableUnits, pack.queryID);
  724. }
  725. void ApplyClientNetPackVisitor::visitExchangeDialog(ExchangeDialog & pack)
  726. {
  727. callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::heroExchangeStarted, pack.hero1, pack.hero2, pack.queryID);
  728. }
  729. void ApplyClientNetPackVisitor::visitTeleportDialog(TeleportDialog & pack)
  730. {
  731. const CGHeroInstance *h = cl.getHero(pack.hero);
  732. callOnlyThatInterface(cl, h->getOwner(), &CGameInterface::showTeleportDialog, h, pack.channel, pack.exits, pack.impassable, pack.queryID);
  733. }
  734. void ApplyClientNetPackVisitor::visitMapObjectSelectDialog(MapObjectSelectDialog & pack)
  735. {
  736. callOnlyThatInterface(cl, pack.player, &CGameInterface::showMapObjectSelectDialog, pack.queryID, pack.icon, pack.title, pack.description, pack.objects);
  737. }
  738. void ApplyFirstClientNetPackVisitor::visitBattleStart(BattleStart & pack)
  739. {
  740. // Cannot use the usual code because curB is not set yet
  741. callOnlyThatBattleInterface(cl, pack.info->getSide(BattleSide::ATTACKER).color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSide(BattleSide::ATTACKER).armyObject, pack.info->getSide(BattleSide::DEFENDER).armyObject,
  742. pack.info->tile, pack.info->getSide(BattleSide::ATTACKER).hero, pack.info->getSide(BattleSide::DEFENDER).hero);
  743. callOnlyThatBattleInterface(cl, pack.info->getSide(BattleSide::DEFENDER).color, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSide(BattleSide::ATTACKER).armyObject, pack.info->getSide(BattleSide::DEFENDER).armyObject,
  744. pack.info->tile, pack.info->getSide(BattleSide::ATTACKER).hero, pack.info->getSide(BattleSide::DEFENDER).hero);
  745. callOnlyThatBattleInterface(cl, PlayerColor::SPECTATOR, &IBattleEventsReceiver::battleStartBefore, pack.battleID, pack.info->getSide(BattleSide::ATTACKER).armyObject, pack.info->getSide(BattleSide::DEFENDER).armyObject,
  746. pack.info->tile, pack.info->getSide(BattleSide::ATTACKER).hero, pack.info->getSide(BattleSide::DEFENDER).hero);
  747. }
  748. void ApplyClientNetPackVisitor::visitBattleStart(BattleStart & pack)
  749. {
  750. cl.battleStarted(pack.info);
  751. }
  752. void ApplyFirstClientNetPackVisitor::visitBattleNextRound(BattleNextRound & pack)
  753. {
  754. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleNewRoundFirst, pack.battleID);
  755. }
  756. void ApplyClientNetPackVisitor::visitBattleNextRound(BattleNextRound & pack)
  757. {
  758. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleNewRound, pack.battleID);
  759. }
  760. void ApplyClientNetPackVisitor::visitBattleSetActiveStack(BattleSetActiveStack & pack)
  761. {
  762. if(!pack.askPlayerInterface)
  763. return;
  764. const CStack *activated = gs.getBattle(pack.battleID)->battleGetStackByID(pack.stack);
  765. PlayerColor playerToCall; //pack.player that will move activated stack
  766. if(activated->hasBonusOfType(BonusType::HYPNOTIZED))
  767. {
  768. playerToCall = gs.getBattle(pack.battleID)->getSide(BattleSide::ATTACKER).color == activated->unitOwner()
  769. ? gs.getBattle(pack.battleID)->getSide(BattleSide::DEFENDER).color
  770. : gs.getBattle(pack.battleID)->getSide(BattleSide::ATTACKER).color;
  771. }
  772. else
  773. {
  774. playerToCall = activated->unitOwner();
  775. }
  776. cl.startPlayerBattleAction(pack.battleID, playerToCall);
  777. }
  778. void ApplyClientNetPackVisitor::visitBattleLogMessage(BattleLogMessage & pack)
  779. {
  780. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleLogMessage, pack.battleID, pack.lines);
  781. }
  782. void ApplyClientNetPackVisitor::visitBattleTriggerEffect(BattleTriggerEffect & pack)
  783. {
  784. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleTriggerEffect, pack.battleID, pack);
  785. }
  786. void ApplyFirstClientNetPackVisitor::visitBattleUpdateGateState(BattleUpdateGateState & pack)
  787. {
  788. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleGateStateChanged, pack.battleID, pack.state);
  789. }
  790. void ApplyFirstClientNetPackVisitor::visitBattleResult(BattleResult & pack)
  791. {
  792. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleEnd, pack.battleID, &pack, pack.queryID);
  793. cl.battleFinished(pack.battleID);
  794. }
  795. void ApplyFirstClientNetPackVisitor::visitBattleStackMoved(BattleStackMoved & pack)
  796. {
  797. const CStack * movedStack = gs.getBattle(pack.battleID)->battleGetStackByID(pack.stack);
  798. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleStackMoved, pack.battleID, movedStack, pack.tilesToMove, pack.distance, pack.teleporting);
  799. }
  800. void ApplyFirstClientNetPackVisitor::visitBattleAttack(BattleAttack & pack)
  801. {
  802. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleAttack, pack.battleID, &pack);
  803. // battleStacksAttacked should be executed before BattleAttack.applyGs() to play animation before damaging unit
  804. // so this has to be here instead of ApplyClientNetPackVisitor::visitBattleAttack()
  805. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleStacksAttacked, pack.battleID, pack.bsa, pack.shot());
  806. }
  807. void ApplyClientNetPackVisitor::visitBattleAttack(BattleAttack & pack)
  808. {
  809. }
  810. void ApplyFirstClientNetPackVisitor::visitStartAction(StartAction & pack)
  811. {
  812. cl.currentBattleAction = std::make_unique<BattleAction>(pack.ba);
  813. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::actionStarted, pack.battleID, pack.ba);
  814. }
  815. void ApplyClientNetPackVisitor::visitBattleSpellCast(BattleSpellCast & pack)
  816. {
  817. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleSpellCast, pack.battleID, &pack);
  818. }
  819. void ApplyClientNetPackVisitor::visitSetStackEffect(SetStackEffect & pack)
  820. {
  821. //informing about effects
  822. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleStacksEffectsSet, pack.battleID, pack);
  823. }
  824. void ApplyClientNetPackVisitor::visitStacksInjured(StacksInjured & pack)
  825. {
  826. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleStacksAttacked, pack.battleID, pack.stacks, false);
  827. }
  828. void ApplyClientNetPackVisitor::visitBattleResultsApplied(BattleResultsApplied & pack)
  829. {
  830. callInterfaceIfPresent(cl, pack.player1, &IGameEventsReceiver::battleResultsApplied);
  831. callInterfaceIfPresent(cl, pack.player2, &IGameEventsReceiver::battleResultsApplied);
  832. callInterfaceIfPresent(cl, PlayerColor::SPECTATOR, &IGameEventsReceiver::battleResultsApplied);
  833. }
  834. void ApplyClientNetPackVisitor::visitBattleUnitsChanged(BattleUnitsChanged & pack)
  835. {
  836. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleUnitsChanged, pack.battleID, pack.changedStacks);
  837. }
  838. void ApplyClientNetPackVisitor::visitBattleObstaclesChanged(BattleObstaclesChanged & pack)
  839. {
  840. //inform interfaces about removed obstacles
  841. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleObstaclesChanged, pack.battleID, pack.changes);
  842. }
  843. void ApplyClientNetPackVisitor::visitCatapultAttack(CatapultAttack & pack)
  844. {
  845. //inform interfaces about catapult attack
  846. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::battleCatapultAttacked, pack.battleID, pack);
  847. }
  848. void ApplyClientNetPackVisitor::visitEndAction(EndAction & pack)
  849. {
  850. callBattleInterfaceIfPresentForBothSides(cl, pack.battleID, &IBattleEventsReceiver::actionFinished, pack.battleID, *cl.currentBattleAction);
  851. cl.currentBattleAction.reset();
  852. }
  853. void ApplyClientNetPackVisitor::visitPackageApplied(PackageApplied & pack)
  854. {
  855. callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::requestRealized, &pack);
  856. if(!CClient::waitingRequest.tryRemovingElement(pack.requestID))
  857. logNetwork->warn("Surprising server message! PackageApplied for unknown requestID!");
  858. }
  859. void ApplyClientNetPackVisitor::visitSystemMessage(SystemMessage & pack)
  860. {
  861. // usually used to receive error messages from server
  862. logNetwork->error("System message: %s", pack.text.toString());
  863. CSH->getGameChat().onNewSystemMessageReceived(pack.text.toString());
  864. }
  865. void ApplyClientNetPackVisitor::visitPlayerBlocked(PlayerBlocked & pack)
  866. {
  867. callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::playerBlocked, pack.reason, pack.startOrEnd == PlayerBlocked::BLOCKADE_STARTED);
  868. }
  869. void ApplyClientNetPackVisitor::visitPlayerStartsTurn(PlayerStartsTurn & pack)
  870. {
  871. logNetwork->debug("Server gives turn to %s", pack.player.toString());
  872. callAllInterfaces(cl, &IGameEventsReceiver::playerStartsTurn, pack.player);
  873. callOnlyThatInterface(cl, pack.player, &CGameInterface::yourTurn, pack.queryID);
  874. }
  875. void ApplyClientNetPackVisitor::visitPlayerEndsTurn(PlayerEndsTurn & pack)
  876. {
  877. logNetwork->debug("Server ends turn of %s", pack.player.toString());
  878. callAllInterfaces(cl, &IGameEventsReceiver::playerEndsTurn, pack.player);
  879. }
  880. void ApplyClientNetPackVisitor::visitTurnTimeUpdate(TurnTimeUpdate & pack)
  881. {
  882. logNetwork->debug("Server sets turn timer {turn: %d, base: %d, battle: %d, creature: %d} for %s", pack.turnTimer.turnTimer, pack.turnTimer.baseTimer, pack.turnTimer.battleTimer, pack.turnTimer.unitTimer, pack.player.toString());
  883. }
  884. void ApplyClientNetPackVisitor::visitPlayerMessageClient(PlayerMessageClient & pack)
  885. {
  886. logNetwork->debug("pack.player %s sends a message: %s", pack.player.toString(), pack.text);
  887. CSH->getGameChat().onNewGameMessageReceived(pack.player, pack.text);
  888. }
  889. void ApplyClientNetPackVisitor::visitAdvmapSpellCast(AdvmapSpellCast & pack)
  890. {
  891. cl.invalidatePaths();
  892. auto caster = cl.getHero(pack.casterID);
  893. if(caster)
  894. //consider notifying other interfaces that see hero?
  895. callInterfaceIfPresent(cl, caster->getOwner(), &IGameEventsReceiver::advmapSpellCast, caster, pack.spellID);
  896. else
  897. logNetwork->error("Invalid hero instance");
  898. }
  899. void ApplyClientNetPackVisitor::visitShowWorldViewEx(ShowWorldViewEx & pack)
  900. {
  901. callOnlyThatInterface(cl, pack.player, &CGameInterface::showWorldViewEx, pack.objectPositions, pack.showTerrain);
  902. }
  903. void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
  904. {
  905. switch(pack.window)
  906. {
  907. case EOpenWindowMode::RECRUITMENT_FIRST:
  908. case EOpenWindowMode::RECRUITMENT_ALL:
  909. {
  910. const CGDwelling *dw = dynamic_cast<const CGDwelling*>(cl.getObj(ObjectInstanceID(pack.object)));
  911. const CArmedInstance *dst = dynamic_cast<const CArmedInstance*>(cl.getObj(ObjectInstanceID(pack.visitor)));
  912. callInterfaceIfPresent(cl, dst->tempOwner, &IGameEventsReceiver::showRecruitmentDialog, dw, dst, pack.window == EOpenWindowMode::RECRUITMENT_FIRST ? 0 : -1, pack.queryID);
  913. }
  914. break;
  915. case EOpenWindowMode::SHIPYARD_WINDOW:
  916. {
  917. assert(pack.queryID == QueryID::NONE);
  918. const auto * sy = dynamic_cast<const IShipyard *>(cl.getObj(ObjectInstanceID(pack.object)));
  919. callInterfaceIfPresent(cl, sy->getObject()->getOwner(), &IGameEventsReceiver::showShipyardDialog, sy);
  920. }
  921. break;
  922. case EOpenWindowMode::THIEVES_GUILD:
  923. {
  924. assert(pack.queryID == QueryID::NONE);
  925. //displays Thieves' Guild window (when hero enters Den of Thieves)
  926. const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
  927. const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
  928. callInterfaceIfPresent(cl, hero->getOwner(), &IGameEventsReceiver::showThievesGuildWindow, obj);
  929. }
  930. break;
  931. case EOpenWindowMode::UNIVERSITY_WINDOW:
  932. {
  933. //displays University window (when hero enters University on adventure map)
  934. const auto * market = cl.getMarket(ObjectInstanceID(pack.object));
  935. const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
  936. callInterfaceIfPresent(cl, hero->tempOwner, &IGameEventsReceiver::showUniversityWindow, market, hero, pack.queryID);
  937. }
  938. break;
  939. case EOpenWindowMode::MARKET_WINDOW:
  940. {
  941. //displays Thieves' Guild window (when hero enters Den of Thieves)
  942. const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
  943. const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
  944. const auto market = cl.getMarket(pack.object);
  945. callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, hero, pack.queryID);
  946. }
  947. break;
  948. case EOpenWindowMode::HILL_FORT_WINDOW:
  949. {
  950. assert(pack.queryID == QueryID::NONE);
  951. //displays Hill fort window
  952. const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
  953. const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
  954. callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showHillFortWindow, obj, hero);
  955. }
  956. break;
  957. case EOpenWindowMode::PUZZLE_MAP:
  958. {
  959. assert(pack.queryID == QueryID::NONE);
  960. const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
  961. callInterfaceIfPresent(cl, hero->getOwner(), &IGameEventsReceiver::showPuzzleMap);
  962. }
  963. break;
  964. case EOpenWindowMode::TAVERN_WINDOW:
  965. {
  966. const CGObjectInstance *obj1 = cl.getObj(ObjectInstanceID(pack.object));
  967. const CGHeroInstance * hero = cl.getHero(ObjectInstanceID(pack.visitor));
  968. callInterfaceIfPresent(cl, hero->tempOwner, &IGameEventsReceiver::showTavernWindow, obj1, hero, pack.queryID);
  969. }
  970. break;
  971. }
  972. }
  973. void ApplyClientNetPackVisitor::visitCenterView(CenterView & pack)
  974. {
  975. callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::centerView, pack.pos, pack.focusTime);
  976. }
  977. void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack)
  978. {
  979. cl.invalidatePaths();
  980. const CGObjectInstance *obj = pack.newObject;
  981. if(CGI->mh)
  982. CGI->mh->onObjectFadeIn(obj, pack.initiator);
  983. for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
  984. {
  985. if(gs.isVisible(obj, i->first))
  986. i->second->newObject(obj);
  987. }
  988. if(CGI->mh)
  989. CGI->mh->waitForOngoingAnimations();
  990. }
  991. void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts & pack)
  992. {
  993. if(pack.id < 0) //artifact merchants globally
  994. {
  995. callAllInterfaces(cl, &IGameEventsReceiver::availableArtifactsChanged, nullptr);
  996. }
  997. else
  998. {
  999. const CGBlackMarket *bm = dynamic_cast<const CGBlackMarket *>(cl.getObj(ObjectInstanceID(pack.id)));
  1000. assert(bm);
  1001. callInterfaceIfPresent(cl, cl.getTile(bm->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::availableArtifactsChanged, bm);
  1002. }
  1003. }
  1004. void ApplyClientNetPackVisitor::visitEntitiesChanged(EntitiesChanged & pack)
  1005. {
  1006. cl.invalidatePaths();
  1007. }