2023-10-19 16:19:09 +02:00
/*
* NetPacksClient.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "ClientNetPackVisitors.h"
#include "Client.h"
#include "CPlayerInterface.h"
#include "CGameInfo.h"
#include "windows/GUIClasses.h"
#include "mapView/mapHandler.h"
2024-04-07 14:19:57 +03:00
#include "adventureMap/AdventureMapInterface.h"
2023-10-19 16:19:09 +02:00
#include "adventureMap/CInGameConsole.h"
#include "battle/BattleInterface.h"
#include "battle/BattleWindow.h"
#include "gui/CGuiHandler.h"
#include "gui/WindowHandler.h"
#include "widgets/MiscWidgets.h"
#include "CMT.h"
2024-03-18 19:52:53 +02:00
#include "GameChatHandler.h"
2023-10-19 16:19:09 +02:00
#include "CServerHandler.h"
#include "../CCallback.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/filesystem/FileInfo.h"
#include "../lib/serializer/BinarySerializer.h"
2023-11-18 16:34:18 +02:00
#include "../lib/serializer/Connection.h"
2023-10-19 16:19:09 +02:00
#include "../lib/CGeneralTextHandler.h"
#include "../lib/CHeroHandler.h"
#include "../lib/VCMI_Lib.h"
#include "../lib/mapping/CMap.h"
#include "../lib/VCMIDirs.h"
#include "../lib/spells/CSpellHandler.h"
#include "../lib/CSoundBase.h"
#include "../lib/StartInfo.h"
#include "../lib/CConfigHandler.h"
#include "../lib/mapObjects/CGMarket.h"
2024-01-09 16:43:36 +02:00
#include "../lib/mapObjects/CGTownInstance.h"
2023-10-19 16:19:09 +02:00
#include "../lib/gameState/CGameState.h"
#include "../lib/CStack.h"
#include "../lib/battle/BattleInfo.h"
#include "../lib/GameConstants.h"
#include "../lib/CPlayerState.h"
// TODO: as Tow suggested these template should all be part of CClient
// This will require rework spectator interface properly though
template < typename T , typename ... Args , typename ... Args2 >
bool callOnlyThatInterface ( CClient & cl , PlayerColor player , void ( T ::* ptr )( Args ...), Args2 && ... args )
{
if ( vstd :: contains ( cl . playerint , player ))
{
(( * cl . playerint [ player ]). * ptr )( std :: forward < Args2 > ( args )...);
return true ;
}
return false ;
}
template < typename T , typename ... Args , typename ... Args2 >
bool callInterfaceIfPresent ( CClient & cl , PlayerColor player , void ( T ::* ptr )( Args ...), Args2 && ... args )
{
bool called = callOnlyThatInterface ( cl , player , ptr , std :: forward < Args2 > ( args )...);
return called ;
}
template < typename T , typename ... Args , typename ... Args2 >
void callOnlyThatBattleInterface ( CClient & cl , PlayerColor player , void ( T ::* ptr )( Args ...), Args2 && ... args )
{
if ( vstd :: contains ( cl . battleints , player ))
(( * cl . battleints [ player ]). * ptr )( std :: forward < Args2 > ( args )...);
if ( cl . additionalBattleInts . count ( player ))
{
for ( auto bInt : cl . additionalBattleInts [ player ])
(( * bInt ). * ptr )( std :: forward < Args2 > ( args )...);
}
}
template < typename T , typename ... Args , typename ... Args2 >
void callBattleInterfaceIfPresent ( CClient & cl , PlayerColor player , void ( T ::* ptr )( Args ...), Args2 && ... args )
{
callOnlyThatInterface ( cl , player , ptr , std :: forward < Args2 > ( args )...);
}
//calls all normal interfaces and privileged ones, playerints may be updated when iterating over it, so we need a copy
template < typename T , typename ... Args , typename ... Args2 >
void callAllInterfaces ( CClient & cl , void ( T ::* ptr )( Args ...), Args2 && ... args )
{
for ( auto pInt : cl . playerint )
(( * pInt . second ). * ptr )( std :: forward < Args2 > ( args )...);
}
//calls all normal interfaces and privileged ones, playerints may be updated when iterating over it, so we need a copy
template < typename T , typename ... Args , typename ... Args2 >
void callBattleInterfaceIfPresentForBothSides ( CClient & cl , const BattleID & battleID , void ( T ::* ptr )( Args ...), Args2 && ... args )
{
assert ( cl . gameState () -> getBattle ( battleID ));
if ( ! cl . gameState () -> getBattle ( battleID ))
{
logGlobal -> error ( "Attempt to call battle interface without ongoing battle!" );
return ;
}
callOnlyThatBattleInterface ( cl , cl . gameState () -> getBattle ( battleID ) -> sides [ 0 ]. color , ptr , std :: forward < Args2 > ( args )...);
callOnlyThatBattleInterface ( cl , cl . gameState () -> getBattle ( battleID ) -> sides [ 1 ]. color , ptr , std :: forward < Args2 > ( args )...);
if ( settings [ "session" ][ "spectate" ]. Bool () && ! settings [ "session" ][ "spectate-skip-battle" ]. Bool () && LOCPLINT -> battleInt )
{
callOnlyThatBattleInterface ( cl , PlayerColor :: SPECTATOR , ptr , std :: forward < Args2 > ( args )...);
}
}
void ApplyClientNetPackVisitor :: visitSetResources ( SetResources & pack )
{
//todo: inform on actual resource set transfered
callInterfaceIfPresent ( cl , pack . player , & IGameEventsReceiver :: receivedResource );
}
void ApplyClientNetPackVisitor :: visitSetPrimSkill ( SetPrimSkill & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . id );
if ( ! h )
{
logNetwork -> error ( "Cannot find hero with pack.id %d" , pack . id . getNum ());
return ;
}
callInterfaceIfPresent ( cl , h -> tempOwner , & IGameEventsReceiver :: heroPrimarySkillChanged , h , pack . which , pack . val );
}
void ApplyClientNetPackVisitor :: visitSetSecSkill ( SetSecSkill & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . id );
if ( ! h )
{
logNetwork -> error ( "Cannot find hero with pack.id %d" , pack . id . getNum ());
return ;
}
callInterfaceIfPresent ( cl , h -> tempOwner , & IGameEventsReceiver :: heroSecondarySkillChanged , h , pack . which , pack . val );
}
void ApplyClientNetPackVisitor :: visitHeroVisitCastle ( HeroVisitCastle & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . hid );
if ( pack . start ())
{
callInterfaceIfPresent ( cl , h -> tempOwner , & IGameEventsReceiver :: heroVisitsTown , h , gs . getTown ( pack . tid ));
}
}
void ApplyClientNetPackVisitor :: visitSetMana ( SetMana & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . hid );
callInterfaceIfPresent ( cl , h -> tempOwner , & IGameEventsReceiver :: heroManaPointsChanged , h );
2023-12-03 18:39:25 +02:00
if ( settings [ "session" ][ "headless" ]. Bool ())
return ;
2023-10-19 16:19:09 +02:00
for ( auto window : GH . windows (). findWindows < BattleWindow > ())
window -> heroManaPointsChanged ( h );
}
void ApplyClientNetPackVisitor :: visitSetMovePoints ( SetMovePoints & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . hid );
cl . invalidatePaths ();
callInterfaceIfPresent ( cl , h -> tempOwner , & IGameEventsReceiver :: heroMovePointsChanged , h );
}
void ApplyClientNetPackVisitor :: visitFoWChange ( FoWChange & pack )
{
for ( auto & i : cl . playerint )
{
if ( cl . getPlayerRelations ( i . first , pack . player ) == PlayerRelations :: SAME_PLAYER && pack . waitForDialogs && LOCPLINT == i . second . get ())
{
LOCPLINT -> waitWhileDialog ();
}
if ( cl . getPlayerRelations ( i . first , pack . player ) != PlayerRelations :: ENEMIES )
{
2023-10-22 18:36:41 +03:00
if ( pack . mode == ETileVisibility :: REVEALED )
2023-10-19 16:19:09 +02:00
i . second -> tileRevealed ( pack . tiles );
else
i . second -> tileHidden ( pack . tiles );
}
}
cl . invalidatePaths ();
}
static void dispatchGarrisonChange ( CClient & cl , ObjectInstanceID army1 , ObjectInstanceID army2 )
{
auto obj1 = cl . getObj ( army1 );
if ( ! obj1 )
{
logNetwork -> error ( "Cannot find army with pack.id %d" , army1 . getNum ());
return ;
}
callInterfaceIfPresent ( cl , obj1 -> tempOwner , & IGameEventsReceiver :: garrisonsChanged , army1 , army2 );
if ( army2 != ObjectInstanceID () && army2 != army1 )
{
auto obj2 = cl . getObj ( army2 );
if ( ! obj2 )
{
logNetwork -> error ( "Cannot find army with pack.id %d" , army2 . getNum ());
return ;
}
if ( obj1 -> tempOwner != obj2 -> tempOwner )
callInterfaceIfPresent ( cl , obj2 -> tempOwner , & IGameEventsReceiver :: garrisonsChanged , army1 , army2 );
}
}
void ApplyClientNetPackVisitor :: visitChangeStackCount ( ChangeStackCount & pack )
{
dispatchGarrisonChange ( cl , pack . army , ObjectInstanceID ());
}
void ApplyClientNetPackVisitor :: visitSetStackType ( SetStackType & pack )
{
dispatchGarrisonChange ( cl , pack . army , ObjectInstanceID ());
}
void ApplyClientNetPackVisitor :: visitEraseStack ( EraseStack & pack )
{
dispatchGarrisonChange ( cl , pack . army , ObjectInstanceID ());
2024-03-27 21:17:21 +01:00
cl . invalidatePaths (); //it is possible to remove last non-native unit for current terrain and lose movement penalty
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitSwapStacks ( SwapStacks & pack )
{
dispatchGarrisonChange ( cl , pack . srcArmy , pack . dstArmy );
2024-03-27 21:17:21 +01:00
if ( pack . srcArmy != pack . dstArmy )
cl . invalidatePaths (); // adding/removing units may change terrain type penalty based on creature native terrains
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitInsertNewStack ( InsertNewStack & pack )
{
dispatchGarrisonChange ( cl , pack . army , ObjectInstanceID ());
2024-03-27 21:17:21 +01:00
if ( gs . getHero ( pack . army ))
cl . invalidatePaths (); // adding/removing units may change terrain type penalty based on creature native terrains
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitRebalanceStacks ( RebalanceStacks & pack )
{
dispatchGarrisonChange ( cl , pack . srcArmy , pack . dstArmy );
2024-03-24 20:19:14 +01:00
2024-03-27 21:17:21 +01:00
if ( pack . srcArmy != pack . dstArmy )
cl . invalidatePaths (); // adding/removing units may change terrain type penalty based on creature native terrains
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitBulkRebalanceStacks ( BulkRebalanceStacks & pack )
{
if ( ! pack . moves . empty ())
{
auto destArmy = pack . moves [ 0 ]. srcArmy == pack . moves [ 0 ]. dstArmy
? ObjectInstanceID ()
: pack . moves [ 0 ]. dstArmy ;
dispatchGarrisonChange ( cl , pack . moves [ 0 ]. srcArmy , destArmy );
2024-03-24 20:19:14 +01:00
2024-03-27 21:17:21 +01:00
if ( pack . moves [ 0 ]. srcArmy != destArmy )
cl . invalidatePaths (); // adding/removing units may change terrain type penalty based on creature native terrains
2023-10-19 16:19:09 +02:00
}
}
void ApplyClientNetPackVisitor :: visitBulkSmartRebalanceStacks ( BulkSmartRebalanceStacks & pack )
{
if ( ! pack . moves . empty ())
{
assert ( pack . moves [ 0 ]. srcArmy == pack . moves [ 0 ]. dstArmy );
dispatchGarrisonChange ( cl , pack . moves [ 0 ]. srcArmy , ObjectInstanceID ());
}
else if ( ! pack . changes . empty ())
{
dispatchGarrisonChange ( cl , pack . changes [ 0 ]. army , ObjectInstanceID ());
}
}
void ApplyClientNetPackVisitor :: visitPutArtifact ( PutArtifact & pack )
{
2023-10-14 22:00:39 +03:00
callInterfaceIfPresent ( cl , cl . getOwner ( pack . al . artHolder ), & IGameEventsReceiver :: artifactPut , pack . al );
2023-10-19 16:19:09 +02:00
if ( pack . askAssemble )
2023-10-14 22:00:39 +03:00
callInterfaceIfPresent ( cl , cl . getOwner ( pack . al . artHolder ), & IGameEventsReceiver :: askToAssembleArtifact , pack . al );
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitEraseArtifact ( EraseArtifact & pack )
{
2023-10-14 22:00:39 +03:00
callInterfaceIfPresent ( cl , cl . getOwner ( pack . al . artHolder ), & IGameEventsReceiver :: artifactRemoved , pack . al );
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitMoveArtifact ( MoveArtifact & pack )
{
auto moveArtifact = [ this , & pack ]( PlayerColor player ) -> void
{
callInterfaceIfPresent ( cl , player , & IGameEventsReceiver :: artifactMoved , pack . src , pack . dst );
if ( pack . askAssemble )
callInterfaceIfPresent ( cl , player , & IGameEventsReceiver :: askToAssembleArtifact , pack . dst );
};
2024-03-07 16:52:50 +02:00
moveArtifact ( pack . interfaceOwner );
if ( pack . interfaceOwner != cl . getOwner ( pack . dst . artHolder ))
2023-10-14 22:00:39 +03:00
moveArtifact ( cl . getOwner ( pack . dst . artHolder ));
2023-10-19 16:19:09 +02:00
cl . invalidatePaths (); // hero might have equipped/unequipped Angel Wings
}
void ApplyClientNetPackVisitor :: visitBulkMoveArtifacts ( BulkMoveArtifacts & pack )
{
auto applyMove = [ this , & pack ]( std :: vector < BulkMoveArtifacts :: LinkedSlots > & artsPack ) -> void
{
for ( auto & slotToMove : artsPack )
{
auto srcLoc = ArtifactLocation ( pack . srcArtHolder , slotToMove . srcPos );
auto dstLoc = ArtifactLocation ( pack . dstArtHolder , slotToMove . dstPos );
2024-03-07 16:52:50 +02:00
MoveArtifact ma ( pack . interfaceOwner , srcLoc , dstLoc , pack . askAssemble );
2023-10-19 16:19:09 +02:00
visitMoveArtifact ( ma );
}
};
2023-10-23 19:39:12 +03:00
auto srcOwner = cl . getOwner ( pack . srcArtHolder );
auto dstOwner = cl . getOwner ( pack . dstArtHolder );
2023-10-19 16:19:09 +02:00
// Begin a session of bulk movement of arts. It is not necessary but useful for the client optimization.
callInterfaceIfPresent ( cl , srcOwner , & IGameEventsReceiver :: bulkArtMovementStart , pack . artsPack0 . size () + pack . artsPack1 . size ());
if ( srcOwner != dstOwner )
callInterfaceIfPresent ( cl , dstOwner , & IGameEventsReceiver :: bulkArtMovementStart , pack . artsPack0 . size () + pack . artsPack1 . size ());
applyMove ( pack . artsPack0 );
if ( pack . swap )
applyMove ( pack . artsPack1 );
}
void ApplyClientNetPackVisitor :: visitAssembledArtifact ( AssembledArtifact & pack )
{
2023-10-14 22:00:39 +03:00
callInterfaceIfPresent ( cl , cl . getOwner ( pack . al . artHolder ), & IGameEventsReceiver :: artifactAssembled , pack . al );
2023-10-19 16:19:09 +02:00
cl . invalidatePaths (); // hero might have equipped/unequipped Angel Wings
}
void ApplyClientNetPackVisitor :: visitDisassembledArtifact ( DisassembledArtifact & pack )
{
2023-10-14 22:00:39 +03:00
callInterfaceIfPresent ( cl , cl . getOwner ( pack . al . artHolder ), & IGameEventsReceiver :: artifactDisassembled , pack . al );
2023-10-19 16:19:09 +02:00
cl . invalidatePaths (); // hero might have equipped/unequipped Angel Wings
}
void ApplyClientNetPackVisitor :: visitHeroVisit ( HeroVisit & pack )
{
auto hero = cl . getHero ( pack . heroId );
auto obj = cl . getObj ( pack . objId , false );
callInterfaceIfPresent ( cl , pack . player , & IGameEventsReceiver :: heroVisit , hero , obj , pack . starting );
}
void ApplyClientNetPackVisitor :: visitNewTurn ( NewTurn & pack )
{
cl . invalidatePaths ();
}
void ApplyClientNetPackVisitor :: visitGiveBonus ( GiveBonus & pack )
{
cl . invalidatePaths ();
switch ( pack . who )
{
2023-11-06 18:27:16 +02:00
case GiveBonus :: ETarget :: OBJECT :
2023-10-19 16:19:09 +02:00
{
2023-11-06 18:27:16 +02:00
const CGHeroInstance * h = gs . getHero ( pack . id . as < ObjectInstanceID > ());
if ( h )
callInterfaceIfPresent ( cl , h -> tempOwner , & IGameEventsReceiver :: heroBonusChanged , h , pack . bonus , true );
2023-10-19 16:19:09 +02:00
}
break ;
case GiveBonus :: ETarget :: PLAYER :
{
2023-11-06 18:27:16 +02:00
callInterfaceIfPresent ( cl , pack . id . as < PlayerColor > (), & IGameEventsReceiver :: playerBonusChanged , pack . bonus , true );
2023-10-19 16:19:09 +02:00
}
break ;
}
}
void ApplyFirstClientNetPackVisitor :: visitChangeObjPos ( ChangeObjPos & pack )
{
CGObjectInstance * obj = gs . getObjInstance ( pack . objid );
2023-10-28 00:20:59 +02:00
if ( CGI && CGI -> mh )
2023-10-27 20:56:31 +00:00
{
2023-10-28 00:20:59 +02:00
CGI -> mh -> onObjectFadeOut ( obj , pack . initiator );
CGI -> mh -> waitForOngoingAnimations ();
2023-10-27 20:56:31 +00:00
}
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitChangeObjPos ( ChangeObjPos & pack )
{
CGObjectInstance * obj = gs . getObjInstance ( pack . objid );
2023-10-28 00:21:16 +02:00
if ( CGI && CGI -> mh )
2023-10-27 20:56:31 +00:00
{
2023-10-28 00:21:16 +02:00
CGI -> mh -> onObjectFadeIn ( obj , pack . initiator );
CGI -> mh -> waitForOngoingAnimations ();
2023-10-27 20:56:31 +00:00
}
2023-10-19 16:19:09 +02:00
cl . invalidatePaths ();
}
void ApplyClientNetPackVisitor :: visitPlayerEndsGame ( PlayerEndsGame & pack )
{
callAllInterfaces ( cl , & IGameEventsReceiver :: gameOver , pack . player , pack . victoryLossCheckResult );
2024-04-07 14:19:57 +03:00
bool lastHumanEndsGame = CSH -> howManyPlayerInterfaces () == 1 && vstd :: contains ( cl . playerint , pack . player ) && cl . getPlayerState ( pack . player ) -> human && ! settings [ "session" ][ "spectate" ]. Bool ();
if ( lastHumanEndsGame )
{
assert ( adventureInt );
if ( adventureInt )
{
GH . windows (). popWindows ( GH . windows (). count ());
adventureInt . reset ();
}
CSH -> showHighScoresAndEndGameplay ( pack . player , pack . victoryLossCheckResult . victory ());
}
2023-10-19 16:19:09 +02:00
// In auto testing pack.mode we always close client if red pack.player won or lose
if ( ! settings [ "session" ][ "testmap" ]. isNull () && pack . player == PlayerColor ( 0 ))
2024-03-28 13:39:15 +02:00
{
logAi -> info ( "Red player %s. Ending game." , pack . victoryLossCheckResult . victory () ? "won" : "lost" );
2023-10-19 16:19:09 +02:00
handleQuit ( settings [ "session" ][ "spectate" ]. Bool ()); // if spectator is active ask to close client or not
2024-03-28 13:39:15 +02:00
}
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitPlayerReinitInterface ( PlayerReinitInterface & pack )
{
auto initInterfaces = [ this ]()
{
cl . initPlayerInterfaces ();
for ( PlayerColor player ( 0 ); player < PlayerColor :: PLAYER_LIMIT ; ++ player )
{
if ( cl . gameState () -> isPlayerMakingTurn ( player ))
{
callAllInterfaces ( cl , & IGameEventsReceiver :: playerStartsTurn , player );
callOnlyThatInterface ( cl , player , & CGameInterface :: yourTurn , QueryID :: NONE );
}
}
};
for ( auto player : pack . players )
{
auto & plSettings = CSH -> si -> getIthPlayersSettings ( player );
if ( pack . playerConnectionId == PlayerSettings :: PLAYER_AI )
{
plSettings . connectedPlayerIDs . clear ();
cl . initPlayerEnvironments ();
initInterfaces ();
}
2024-02-11 16:26:27 +02:00
else if ( pack . playerConnectionId == CSH -> logicConnection -> connectionID )
2023-10-19 16:19:09 +02:00
{
plSettings . connectedPlayerIDs . insert ( pack . playerConnectionId );
cl . playerint . clear ();
initInterfaces ();
}
}
}
void ApplyClientNetPackVisitor :: visitRemoveBonus ( RemoveBonus & pack )
{
cl . invalidatePaths ();
switch ( pack . who )
{
2023-11-06 18:27:16 +02:00
case GiveBonus :: ETarget :: OBJECT :
2023-10-19 16:19:09 +02:00
{
2023-11-06 18:27:16 +02:00
const CGHeroInstance * h = gs . getHero ( pack . whoID . as < ObjectInstanceID > ());
if ( h )
callInterfaceIfPresent ( cl , h -> tempOwner , & IGameEventsReceiver :: heroBonusChanged , h , pack . bonus , false );
2023-10-19 16:19:09 +02:00
}
break ;
case GiveBonus :: ETarget :: PLAYER :
{
//const PlayerState *p = gs.getPlayerState(pack.id);
2023-11-06 18:27:16 +02:00
callInterfaceIfPresent ( cl , pack . whoID . as < PlayerColor > (), & IGameEventsReceiver :: playerBonusChanged , pack . bonus , false );
2023-10-19 16:19:09 +02:00
}
break ;
}
}
void ApplyFirstClientNetPackVisitor :: visitRemoveObject ( RemoveObject & pack )
{
const CGObjectInstance * o = cl . getObj ( pack . objectID );
if ( CGI -> mh )
CGI -> mh -> onObjectFadeOut ( o , pack . initiator );
//notify interfaces about removal
for ( auto i = cl . playerint . begin (); i != cl . playerint . end (); i ++ )
{
//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
//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
if ( gs . isVisible ( o , i -> first ) || ( ! cl . getPlayerState ( i -> first ) -> human && o -> ID == Obj :: HERO && o -> tempOwner != i -> first ))
i -> second -> objectRemoved ( o , pack . initiator );
}
2023-12-03 18:39:25 +02:00
if ( CGI -> mh )
CGI -> mh -> waitForOngoingAnimations ();
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitRemoveObject ( RemoveObject & pack )
{
cl . invalidatePaths ();
for ( auto i = cl . playerint . begin (); i != cl . playerint . end (); i ++ )
i -> second -> objectRemovedAfter ();
}
void ApplyFirstClientNetPackVisitor :: visitTryMoveHero ( TryMoveHero & pack )
{
CGHeroInstance * h = gs . getHero ( pack . id );
if ( CGI -> mh )
{
switch ( pack . result )
{
case TryMoveHero :: EMBARK :
CGI -> mh -> onBeforeHeroEmbark ( h , pack . start , pack . end );
break ;
case TryMoveHero :: TELEPORTATION :
CGI -> mh -> onBeforeHeroTeleported ( h , pack . start , pack . end );
break ;
case TryMoveHero :: DISEMBARK :
CGI -> mh -> onBeforeHeroDisembark ( h , pack . start , pack . end );
break ;
}
CGI -> mh -> waitForOngoingAnimations ();
}
}
void ApplyClientNetPackVisitor :: visitTryMoveHero ( TryMoveHero & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . id );
cl . invalidatePaths ();
if ( CGI -> mh )
{
switch ( pack . result )
{
case TryMoveHero :: SUCCESS :
CGI -> mh -> onHeroMoved ( h , pack . start , pack . end );
break ;
case TryMoveHero :: EMBARK :
CGI -> mh -> onAfterHeroEmbark ( h , pack . start , pack . end );
break ;
case TryMoveHero :: TELEPORTATION :
CGI -> mh -> onAfterHeroTeleported ( h , pack . start , pack . end );
break ;
case TryMoveHero :: DISEMBARK :
CGI -> mh -> onAfterHeroDisembark ( h , pack . start , pack . end );
break ;
}
}
PlayerColor player = h -> tempOwner ;
for ( auto & i : cl . playerint )
if ( cl . getPlayerRelations ( i . first , player ) != PlayerRelations :: ENEMIES )
i . second -> tileRevealed ( pack . fowRevealed );
for ( auto i = cl . playerint . begin (); i != cl . playerint . end (); i ++ )
{
if ( i -> first != PlayerColor :: SPECTATOR && gs . checkForStandardLoss ( i -> first )) // Do not notify vanquished pack.player's interface
continue ;
if ( gs . isVisible ( h -> convertToVisitablePos ( pack . start ), i -> first )
|| gs . isVisible ( h -> convertToVisitablePos ( pack . end ), i -> first ))
{
// pack.src and pack.dst of enemy hero move may be not visible => 'verbose' should be false
const bool verbose = cl . getPlayerRelations ( i -> first , player ) != PlayerRelations :: ENEMIES ;
i -> second -> heroMoved ( pack , verbose );
}
}
}
void ApplyClientNetPackVisitor :: visitNewStructures ( NewStructures & pack )
{
CGTownInstance * town = gs . getTown ( pack . tid );
for ( const auto & id : pack . bid )
{
callInterfaceIfPresent ( cl , town -> getOwner (), & IGameEventsReceiver :: buildChanged , town , id , 1 );
}
// invalidate section of map view with our object and force an update
2023-12-03 18:39:25 +02:00
if ( CGI -> mh )
{
CGI -> mh -> onObjectInstantRemove ( town , town -> getOwner ());
CGI -> mh -> onObjectInstantAdd ( town , town -> getOwner ());
}
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitRazeStructures ( RazeStructures & pack )
{
CGTownInstance * town = gs . getTown ( pack . tid );
for ( const auto & id : pack . bid )
{
callInterfaceIfPresent ( cl , town -> getOwner (), & IGameEventsReceiver :: buildChanged , town , id , 2 );
}
// invalidate section of map view with our object and force an update
2023-12-03 18:39:25 +02:00
if ( CGI -> mh )
{
CGI -> mh -> onObjectInstantRemove ( town , town -> getOwner ());
CGI -> mh -> onObjectInstantAdd ( town , town -> getOwner ());
}
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitSetAvailableCreatures ( SetAvailableCreatures & pack )
{
const CGDwelling * dw = static_cast < const CGDwelling *> ( cl . getObj ( pack . tid ));
PlayerColor p ;
if ( dw -> ID == Obj :: WAR_MACHINE_FACTORY ) //War Machines Factory is not flaggable, it's "owned" by visitor
p = cl . getTile ( dw -> visitablePos ()) -> visitableObjects . back () -> tempOwner ;
else
p = dw -> tempOwner ;
callInterfaceIfPresent ( cl , p , & IGameEventsReceiver :: availableCreaturesChanged , dw );
}
void ApplyClientNetPackVisitor :: visitSetHeroesInTown ( SetHeroesInTown & pack )
{
CGTownInstance * t = gs . getTown ( pack . tid );
CGHeroInstance * hGarr = gs . getHero ( pack . garrison );
CGHeroInstance * hVisit = gs . getHero ( pack . visiting );
//inform all players that see this object
for ( auto i = cl . playerint . cbegin (); i != cl . playerint . cend (); ++ i )
{
if ( ! i -> first . isValidPlayer ())
continue ;
if ( gs . isVisible ( t , i -> first ) ||
( hGarr && gs . isVisible ( hGarr , i -> first )) ||
( hVisit && gs . isVisible ( hVisit , i -> first )))
{
cl . playerint [ i -> first ] -> heroInGarrisonChange ( t );
}
}
}
void ApplyClientNetPackVisitor :: visitHeroRecruited ( HeroRecruited & pack )
{
CGHeroInstance * h = gs . map -> heroesOnMap . back ();
2023-10-28 12:27:10 +03:00
if ( h -> getHeroType () != pack . hid )
2023-10-19 16:19:09 +02:00
{
logNetwork -> error ( "Something wrong with hero recruited!" );
}
if ( callInterfaceIfPresent ( cl , h -> tempOwner , & IGameEventsReceiver :: heroCreated , h ))
{
if ( const CGTownInstance * t = gs . getTown ( pack . tid ))
callInterfaceIfPresent ( cl , h -> getOwner (), & IGameEventsReceiver :: heroInGarrisonChange , t );
}
if ( CGI -> mh )
CGI -> mh -> onObjectInstantAdd ( h , h -> getOwner ());
}
void ApplyClientNetPackVisitor :: visitGiveHero ( GiveHero & pack )
{
CGHeroInstance * h = gs . getHero ( pack . id );
if ( CGI -> mh )
CGI -> mh -> onObjectInstantAdd ( h , h -> getOwner ());
callInterfaceIfPresent ( cl , h -> tempOwner , & IGameEventsReceiver :: heroCreated , h );
}
void ApplyFirstClientNetPackVisitor :: visitGiveHero ( GiveHero & pack )
{
}
void ApplyClientNetPackVisitor :: visitInfoWindow ( InfoWindow & pack )
{
std :: string str = pack . text . toString ();
if ( ! callInterfaceIfPresent ( cl , pack . player , & CGameInterface :: showInfoDialog , pack . type , str , pack . components ,( soundBase :: soundID ) pack . soundID ))
logNetwork -> warn ( "We received InfoWindow for not our player..." );
}
void ApplyFirstClientNetPackVisitor :: visitSetObjectProperty ( SetObjectProperty & pack )
{
//inform all players that see this object
for ( auto it = cl . playerint . cbegin (); it != cl . playerint . cend (); ++ it )
{
if ( gs . isVisible ( gs . getObjInstance ( pack . id ), it -> first ))
callInterfaceIfPresent ( cl , it -> first , & IGameEventsReceiver :: beforeObjectPropertyChanged , & pack );
}
// invalidate section of map view with our object and force an update with new flag color
2023-12-03 18:39:25 +02:00
if ( pack . what == ObjProperty :: OWNER && CGI -> mh )
2023-10-19 16:19:09 +02:00
{
auto object = gs . getObjInstance ( pack . id );
CGI -> mh -> onObjectInstantRemove ( object , object -> getOwner ());
}
}
void ApplyClientNetPackVisitor :: visitSetObjectProperty ( SetObjectProperty & pack )
{
//inform all players that see this object
for ( auto it = cl . playerint . cbegin (); it != cl . playerint . cend (); ++ it )
{
if ( gs . isVisible ( gs . getObjInstance ( pack . id ), it -> first ))
callInterfaceIfPresent ( cl , it -> first , & IGameEventsReceiver :: objectPropertyChanged , & pack );
}
// invalidate section of map view with our object and force an update with new flag color
2023-12-03 18:39:25 +02:00
if ( pack . what == ObjProperty :: OWNER && CGI -> mh )
2023-10-19 16:19:09 +02:00
{
auto object = gs . getObjInstance ( pack . id );
CGI -> mh -> onObjectInstantAdd ( object , object -> getOwner ());
}
}
void ApplyClientNetPackVisitor :: visitHeroLevelUp ( HeroLevelUp & pack )
{
const CGHeroInstance * hero = cl . getHero ( pack . heroId );
assert ( hero );
callOnlyThatInterface ( cl , pack . player , & CGameInterface :: heroGotLevel , hero , pack . primskill , pack . skills , pack . queryID );
}
void ApplyClientNetPackVisitor :: visitCommanderLevelUp ( CommanderLevelUp & pack )
{
const CGHeroInstance * hero = cl . getHero ( pack . heroId );
assert ( hero );
const CCommanderInstance * commander = hero -> commander ;
assert ( commander );
assert ( commander -> armyObj ); //is it possible for Commander to exist beyond armed instance?
callOnlyThatInterface ( cl , pack . player , & CGameInterface :: commanderGotLevel , commander , pack . skills , pack . queryID );
}
void ApplyClientNetPackVisitor :: visitBlockingDialog ( BlockingDialog & pack )
{
std :: string str = pack . text . toString ();
2024-04-17 01:08:27 +02:00
if ( ! callOnlyThatInterface ( cl , pack . player , & CGameInterface :: showBlockingDialog , str , pack . components , pack . queryID , ( soundBase :: soundID ) pack . soundID , pack . selection (), pack . cancel (), pack . safeToAutoaccept ()))
2023-10-19 16:19:09 +02:00
logNetwork -> warn ( "We received YesNoDialog for not our player..." );
}
void ApplyClientNetPackVisitor :: visitGarrisonDialog ( GarrisonDialog & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . hid );
const CArmedInstance * obj = static_cast < const CArmedInstance *> ( cl . getObj ( pack . objid ));
callOnlyThatInterface ( cl , h -> getOwner (), & CGameInterface :: showGarrisonDialog , obj , h , pack . removableUnits , pack . queryID );
}
void ApplyClientNetPackVisitor :: visitExchangeDialog ( ExchangeDialog & pack )
{
callInterfaceIfPresent ( cl , pack . player , & IGameEventsReceiver :: heroExchangeStarted , pack . hero1 , pack . hero2 , pack . queryID );
}
void ApplyClientNetPackVisitor :: visitTeleportDialog ( TeleportDialog & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . hero );
callOnlyThatInterface ( cl , h -> getOwner (), & CGameInterface :: showTeleportDialog , h , pack . channel , pack . exits , pack . impassable , pack . queryID );
}
void ApplyClientNetPackVisitor :: visitMapObjectSelectDialog ( MapObjectSelectDialog & pack )
{
callOnlyThatInterface ( cl , pack . player , & CGameInterface :: showMapObjectSelectDialog , pack . queryID , pack . icon , pack . title , pack . description , pack . objects );
}
void ApplyFirstClientNetPackVisitor :: visitBattleStart ( BattleStart & pack )
{
// Cannot use the usual code because curB is not set yet
callOnlyThatBattleInterface ( cl , pack . info -> sides [ 0 ]. color , & IBattleEventsReceiver :: battleStartBefore , pack . battleID , pack . info -> sides [ 0 ]. armyObject , pack . info -> sides [ 1 ]. armyObject ,
pack . info -> tile , pack . info -> sides [ 0 ]. hero , pack . info -> sides [ 1 ]. hero );
callOnlyThatBattleInterface ( cl , pack . info -> sides [ 1 ]. color , & IBattleEventsReceiver :: battleStartBefore , pack . battleID , pack . info -> sides [ 0 ]. armyObject , pack . info -> sides [ 1 ]. armyObject ,
pack . info -> tile , pack . info -> sides [ 0 ]. hero , pack . info -> sides [ 1 ]. hero );
callOnlyThatBattleInterface ( cl , PlayerColor :: SPECTATOR , & IBattleEventsReceiver :: battleStartBefore , pack . battleID , pack . info -> sides [ 0 ]. armyObject , pack . info -> sides [ 1 ]. armyObject ,
pack . info -> tile , pack . info -> sides [ 0 ]. hero , pack . info -> sides [ 1 ]. hero );
}
void ApplyClientNetPackVisitor :: visitBattleStart ( BattleStart & pack )
{
cl . battleStarted ( pack . info );
}
void ApplyFirstClientNetPackVisitor :: visitBattleNextRound ( BattleNextRound & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleNewRoundFirst , pack . battleID );
}
void ApplyClientNetPackVisitor :: visitBattleNextRound ( BattleNextRound & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleNewRound , pack . battleID );
}
void ApplyClientNetPackVisitor :: visitBattleSetActiveStack ( BattleSetActiveStack & pack )
{
if ( ! pack . askPlayerInterface )
return ;
const CStack * activated = gs . getBattle ( pack . battleID ) -> battleGetStackByID ( pack . stack );
PlayerColor playerToCall ; //pack.player that will move activated stack
if ( activated -> hasBonusOfType ( BonusType :: HYPNOTIZED ))
{
playerToCall = ( gs . getBattle ( pack . battleID ) -> sides [ 0 ]. color == activated -> unitOwner ()
? gs . getBattle ( pack . battleID ) -> sides [ 1 ]. color
: gs . getBattle ( pack . battleID ) -> sides [ 0 ]. color );
}
else
{
playerToCall = activated -> unitOwner ();
}
cl . startPlayerBattleAction ( pack . battleID , playerToCall );
}
void ApplyClientNetPackVisitor :: visitBattleLogMessage ( BattleLogMessage & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleLogMessage , pack . battleID , pack . lines );
}
void ApplyClientNetPackVisitor :: visitBattleTriggerEffect ( BattleTriggerEffect & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleTriggerEffect , pack . battleID , pack );
}
void ApplyFirstClientNetPackVisitor :: visitBattleUpdateGateState ( BattleUpdateGateState & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleGateStateChanged , pack . battleID , pack . state );
}
void ApplyFirstClientNetPackVisitor :: visitBattleResult ( BattleResult & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleEnd , pack . battleID , & pack , pack . queryID );
cl . battleFinished ( pack . battleID );
}
void ApplyFirstClientNetPackVisitor :: visitBattleStackMoved ( BattleStackMoved & pack )
{
const CStack * movedStack = gs . getBattle ( pack . battleID ) -> battleGetStackByID ( pack . stack );
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleStackMoved , pack . battleID , movedStack , pack . tilesToMove , pack . distance , pack . teleporting );
}
void ApplyFirstClientNetPackVisitor :: visitBattleAttack ( BattleAttack & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleAttack , pack . battleID , & pack );
// battleStacksAttacked should be excuted before BattleAttack.applyGs() to play animation before damaging unit
// so this has to be here instead of ApplyClientNetPackVisitor::visitBattleAttack()
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleStacksAttacked , pack . battleID , pack . bsa , pack . shot ());
}
void ApplyClientNetPackVisitor :: visitBattleAttack ( BattleAttack & pack )
{
}
void ApplyFirstClientNetPackVisitor :: visitStartAction ( StartAction & pack )
{
cl . currentBattleAction = std :: make_unique < BattleAction > ( pack . ba );
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: actionStarted , pack . battleID , pack . ba );
}
void ApplyClientNetPackVisitor :: visitBattleSpellCast ( BattleSpellCast & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleSpellCast , pack . battleID , & pack );
}
void ApplyClientNetPackVisitor :: visitSetStackEffect ( SetStackEffect & pack )
{
//informing about effects
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleStacksEffectsSet , pack . battleID , pack );
}
void ApplyClientNetPackVisitor :: visitStacksInjured ( StacksInjured & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleStacksAttacked , pack . battleID , pack . stacks , false );
}
void ApplyClientNetPackVisitor :: visitBattleResultsApplied ( BattleResultsApplied & pack )
{
callInterfaceIfPresent ( cl , pack . player1 , & IGameEventsReceiver :: battleResultsApplied );
callInterfaceIfPresent ( cl , pack . player2 , & IGameEventsReceiver :: battleResultsApplied );
callInterfaceIfPresent ( cl , PlayerColor :: SPECTATOR , & IGameEventsReceiver :: battleResultsApplied );
}
void ApplyClientNetPackVisitor :: visitBattleUnitsChanged ( BattleUnitsChanged & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleUnitsChanged , pack . battleID , pack . changedStacks );
}
void ApplyClientNetPackVisitor :: visitBattleObstaclesChanged ( BattleObstaclesChanged & pack )
{
//inform interfaces about removed obstacles
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleObstaclesChanged , pack . battleID , pack . changes );
}
void ApplyClientNetPackVisitor :: visitCatapultAttack ( CatapultAttack & pack )
{
//inform interfaces about catapult attack
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: battleCatapultAttacked , pack . battleID , pack );
}
void ApplyClientNetPackVisitor :: visitEndAction ( EndAction & pack )
{
callBattleInterfaceIfPresentForBothSides ( cl , pack . battleID , & IBattleEventsReceiver :: actionFinished , pack . battleID , * cl . currentBattleAction );
cl . currentBattleAction . reset ();
}
void ApplyClientNetPackVisitor :: visitPackageApplied ( PackageApplied & pack )
{
callInterfaceIfPresent ( cl , pack . player , & IGameEventsReceiver :: requestRealized , & pack );
if ( ! CClient :: waitingRequest . tryRemovingElement ( pack . requestID ))
logNetwork -> warn ( "Surprising server message! PackageApplied for unknown requestID!" );
}
void ApplyClientNetPackVisitor :: visitSystemMessage ( SystemMessage & pack )
{
2024-03-18 19:52:53 +02:00
// usually used to receive error messages from server
2024-05-02 21:03:23 +02:00
logNetwork -> error ( "System message: %s" , pack . text . toString ());
2023-10-19 16:19:09 +02:00
2024-05-02 21:03:23 +02:00
CSH -> getGameChat (). onNewSystemMessageReceived ( pack . text . toString ());
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitPlayerBlocked ( PlayerBlocked & pack )
{
callInterfaceIfPresent ( cl , pack . player , & IGameEventsReceiver :: playerBlocked , pack . reason , pack . startOrEnd == PlayerBlocked :: BLOCKADE_STARTED );
}
void ApplyClientNetPackVisitor :: visitPlayerStartsTurn ( PlayerStartsTurn & pack )
{
logNetwork -> debug ( "Server gives turn to %s" , pack . player . toString ());
callAllInterfaces ( cl , & IGameEventsReceiver :: playerStartsTurn , pack . player );
callOnlyThatInterface ( cl , pack . player , & CGameInterface :: yourTurn , pack . queryID );
}
void ApplyClientNetPackVisitor :: visitPlayerEndsTurn ( PlayerEndsTurn & pack )
{
logNetwork -> debug ( "Server ends turn of %s" , pack . player . toString ());
callAllInterfaces ( cl , & IGameEventsReceiver :: playerEndsTurn , pack . player );
}
void ApplyClientNetPackVisitor :: visitTurnTimeUpdate ( TurnTimeUpdate & pack )
{
2023-11-23 17:59:18 +02:00
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 ());
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitPlayerMessageClient ( PlayerMessageClient & pack )
{
logNetwork -> debug ( "pack.player %s sends a message: %s" , pack . player . toString (), pack . text );
2024-03-18 19:52:53 +02:00
CSH -> getGameChat (). onNewGameMessageReceived ( pack . player , pack . text );
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitAdvmapSpellCast ( AdvmapSpellCast & pack )
{
cl . invalidatePaths ();
auto caster = cl . getHero ( pack . casterID );
if ( caster )
//consider notifying other interfaces that see hero?
callInterfaceIfPresent ( cl , caster -> getOwner (), & IGameEventsReceiver :: advmapSpellCast , caster , pack . spellID );
else
logNetwork -> error ( "Invalid hero instance" );
}
void ApplyClientNetPackVisitor :: visitShowWorldViewEx ( ShowWorldViewEx & pack )
{
callOnlyThatInterface ( cl , pack . player , & CGameInterface :: showWorldViewEx , pack . objectPositions , pack . showTerrain );
}
void ApplyClientNetPackVisitor :: visitOpenWindow ( OpenWindow & pack )
{
switch ( pack . window )
{
case EOpenWindowMode :: RECRUITMENT_FIRST :
case EOpenWindowMode :: RECRUITMENT_ALL :
{
const CGDwelling * dw = dynamic_cast < const CGDwelling *> ( cl . getObj ( ObjectInstanceID ( pack . object )));
const CArmedInstance * dst = dynamic_cast < const CArmedInstance *> ( cl . getObj ( ObjectInstanceID ( pack . visitor )));
callInterfaceIfPresent ( cl , dst -> tempOwner , & IGameEventsReceiver :: showRecruitmentDialog , dw , dst , pack . window == EOpenWindowMode :: RECRUITMENT_FIRST ? 0 : - 1 , pack . queryID );
}
break ;
case EOpenWindowMode :: SHIPYARD_WINDOW :
{
assert ( pack . queryID == QueryID :: NONE );
2024-02-14 12:56:37 +02:00
const auto * sy = dynamic_cast < const IShipyard *> ( cl . getObj ( ObjectInstanceID ( pack . object )));
2023-10-19 16:19:09 +02:00
callInterfaceIfPresent ( cl , sy -> getObject () -> getOwner (), & IGameEventsReceiver :: showShipyardDialog , sy );
}
break ;
case EOpenWindowMode :: THIEVES_GUILD :
{
assert ( pack . queryID == QueryID :: NONE );
//displays Thieves' Guild window (when hero enters Den of Thieves)
const CGObjectInstance * obj = cl . getObj ( ObjectInstanceID ( pack . object ));
const CGHeroInstance * hero = cl . getHero ( ObjectInstanceID ( pack . visitor ));
callInterfaceIfPresent ( cl , hero -> getOwner (), & IGameEventsReceiver :: showThievesGuildWindow , obj );
}
break ;
case EOpenWindowMode :: UNIVERSITY_WINDOW :
{
//displays University window (when hero enters University on adventure map)
2024-02-14 12:56:37 +02:00
const auto * market = dynamic_cast < const IMarket *> ( cl . getObj ( ObjectInstanceID ( pack . object )));
2023-10-19 16:19:09 +02:00
const CGHeroInstance * hero = cl . getHero ( ObjectInstanceID ( pack . visitor ));
callInterfaceIfPresent ( cl , hero -> tempOwner , & IGameEventsReceiver :: showUniversityWindow , market , hero , pack . queryID );
}
break ;
case EOpenWindowMode :: MARKET_WINDOW :
{
//displays Thieves' Guild window (when hero enters Den of Thieves)
const CGObjectInstance * obj = cl . getObj ( ObjectInstanceID ( pack . object ));
const CGHeroInstance * hero = cl . getHero ( ObjectInstanceID ( pack . visitor ));
2024-02-14 12:56:37 +02:00
const auto * market = dynamic_cast < const IMarket *> ( obj );
2023-10-19 16:19:09 +02:00
callInterfaceIfPresent ( cl , cl . getTile ( obj -> visitablePos ()) -> visitableObjects . back () -> tempOwner , & IGameEventsReceiver :: showMarketWindow , market , hero , pack . queryID );
}
break ;
case EOpenWindowMode :: HILL_FORT_WINDOW :
{
assert ( pack . queryID == QueryID :: NONE );
//displays Hill fort window
const CGObjectInstance * obj = cl . getObj ( ObjectInstanceID ( pack . object ));
const CGHeroInstance * hero = cl . getHero ( ObjectInstanceID ( pack . visitor ));
callInterfaceIfPresent ( cl , cl . getTile ( obj -> visitablePos ()) -> visitableObjects . back () -> tempOwner , & IGameEventsReceiver :: showHillFortWindow , obj , hero );
}
break ;
case EOpenWindowMode :: PUZZLE_MAP :
{
assert ( pack . queryID == QueryID :: NONE );
const CGHeroInstance * hero = cl . getHero ( ObjectInstanceID ( pack . visitor ));
callInterfaceIfPresent ( cl , hero -> getOwner (), & IGameEventsReceiver :: showPuzzleMap );
}
break ;
case EOpenWindowMode :: TAVERN_WINDOW :
{
const CGObjectInstance * obj1 = cl . getObj ( ObjectInstanceID ( pack . object ));
const CGHeroInstance * hero = cl . getHero ( ObjectInstanceID ( pack . visitor ));
callInterfaceIfPresent ( cl , hero -> tempOwner , & IGameEventsReceiver :: showTavernWindow , obj1 , hero , pack . queryID );
}
break ;
}
}
void ApplyClientNetPackVisitor :: visitCenterView ( CenterView & pack )
{
callInterfaceIfPresent ( cl , pack . player , & IGameEventsReceiver :: centerView , pack . pos , pack . focusTime );
}
void ApplyClientNetPackVisitor :: visitNewObject ( NewObject & pack )
{
cl . invalidatePaths ();
const CGObjectInstance * obj = cl . getObj ( pack . createdObjectID );
if ( CGI -> mh )
CGI -> mh -> onObjectFadeIn ( obj , pack . initiator );
for ( auto i = cl . playerint . begin (); i != cl . playerint . end (); i ++ )
{
if ( gs . isVisible ( obj , i -> first ))
i -> second -> newObject ( obj );
}
2023-12-03 18:39:25 +02:00
if ( CGI -> mh )
CGI -> mh -> waitForOngoingAnimations ();
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor :: visitSetAvailableArtifacts ( SetAvailableArtifacts & pack )
{
if ( pack . id < 0 ) //artifact merchants globally
{
callAllInterfaces ( cl , & IGameEventsReceiver :: availableArtifactsChanged , nullptr );
}
else
{
const CGBlackMarket * bm = dynamic_cast < const CGBlackMarket *> ( cl . getObj ( ObjectInstanceID ( pack . id )));
assert ( bm );
callInterfaceIfPresent ( cl , cl . getTile ( bm -> visitablePos ()) -> visitableObjects . back () -> tempOwner , & IGameEventsReceiver :: availableArtifactsChanged , bm );
}
}
void ApplyClientNetPackVisitor :: visitEntitiesChanged ( EntitiesChanged & pack )
{
cl . invalidatePaths ();
}