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 "windows/GUIClasses.h"
2024-09-27 23:52:33 +02:00
# include "windows/CCastleInterface.h"
2023-10-19 16:19:09 +02:00
# 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"
2025-02-10 21:49:23 +00:00
# include "GameEngine.h"
2025-02-11 15:23:33 +00:00
# include "GameInstance.h"
2023-10-19 16:19:09 +02:00
# 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"
2025-04-07 20:10:14 +02:00
# include "UIHelper.h"
2023-10-19 16:19:09 +02:00
# include "../CCallback.h"
# include "../lib/filesystem/Filesystem.h"
# include "../lib/filesystem/FileInfo.h"
2023-11-18 16:34:18 +02:00
# include "../lib/serializer/Connection.h"
2024-07-20 12:55:17 +00:00
# include "../lib/texts/CGeneralTextHandler.h"
2025-02-14 16:23:37 +00:00
# include "../lib/GameLibrary.h"
2023-10-19 16:19:09 +02:00
# 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 ) ) ;
2024-09-04 00:13:00 +02:00
if ( ! cl . gameState ( ) - > getBattle ( battleID ) )
2023-10-19 16:19:09 +02:00
{
logGlobal - > error ( " Attempt to call battle interface without ongoing battle! " ) ;
return ;
}
2024-08-11 20:22:35 +00:00
callOnlyThatBattleInterface ( cl , cl . gameState ( ) - > getBattle ( battleID ) - > getSide ( BattleSide : : ATTACKER ) . color , ptr , std : : forward < Args2 > ( args ) . . . ) ;
callOnlyThatBattleInterface ( cl , cl . gameState ( ) - > getBattle ( battleID ) - > getSide ( BattleSide : : DEFENDER ) . color , ptr , std : : forward < Args2 > ( args ) . . . ) ;
2025-02-11 15:23:33 +00:00
if ( settings [ " session " ] [ " spectate " ] . Bool ( ) & & ! settings [ " session " ] [ " spectate-skip-battle " ] . Bool ( ) & & GAME - > interface ( ) - > battleInt )
2023-10-19 16:19:09 +02:00
{
callOnlyThatBattleInterface ( cl , PlayerColor : : SPECTATOR , ptr , std : : forward < Args2 > ( args ) . . . ) ;
}
}
void ApplyClientNetPackVisitor : : visitSetResources ( SetResources & pack )
{
2024-06-24 03:23:26 +02:00
//todo: inform on actual resource set transferred
2023-10-19 16:19:09 +02:00
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 ;
2025-02-10 21:49:23 +00:00
for ( auto window : ENGINE - > windows ( ) . findWindows < BattleWindow > ( ) )
2023-10-19 16:19:09 +02:00
window - > heroManaPointsChanged ( h ) ;
}
void ApplyClientNetPackVisitor : : visitSetMovePoints ( SetMovePoints & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . hid ) ;
callInterfaceIfPresent ( cl , h - > tempOwner , & IGameEventsReceiver : : heroMovePointsChanged , h ) ;
}
2024-09-28 15:05:13 +02:00
void ApplyClientNetPackVisitor : : visitSetResearchedSpells ( SetResearchedSpells & pack )
2024-09-27 23:52:33 +02:00
{
2025-02-10 21:49:23 +00:00
for ( const auto & win : ENGINE - > windows ( ) . findWindows < CMageGuildScreen > ( ) )
2024-09-28 16:10:03 +02:00
win - > updateSpells ( pack . tid ) ;
2024-09-27 23:52:33 +02:00
}
2023-10-19 16:19:09 +02:00
void ApplyClientNetPackVisitor : : visitFoWChange ( FoWChange & pack )
{
for ( auto & i : cl . playerint )
{
2025-02-11 15:23:33 +00:00
if ( cl . getPlayerRelations ( i . first , pack . player ) = = PlayerRelations : : SAME_PLAYER & & pack . waitForDialogs & & GAME - > interface ( ) = = i . second . get ( ) )
2023-10-19 16:19:09 +02:00
{
2025-02-11 15:23:33 +00:00
GAME - > interface ( ) - > waitWhileDialog ( ) ;
2023-10-19 16:19:09 +02:00
}
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 ) ;
}
}
2025-01-23 14:39:56 +00:00
callAllInterfaces ( cl , & CGameInterface : : invalidatePaths ) ;
2023-10-19 16:19:09 +02:00
}
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 ( ) ) ;
}
void ApplyClientNetPackVisitor : : visitSwapStacks ( SwapStacks & pack )
{
dispatchGarrisonChange ( cl , pack . srcArmy , pack . dstArmy ) ;
}
void ApplyClientNetPackVisitor : : visitInsertNewStack ( InsertNewStack & pack )
{
dispatchGarrisonChange ( cl , pack . army , ObjectInstanceID ( ) ) ;
}
void ApplyClientNetPackVisitor : : visitRebalanceStacks ( RebalanceStacks & pack )
{
dispatchGarrisonChange ( cl , pack . srcArmy , pack . dstArmy ) ;
}
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 ) ;
}
}
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
}
2024-08-29 18:47:06 +03:00
void ApplyClientNetPackVisitor : : visitEraseArtifact ( BulkEraseArtifacts & pack )
2023-10-19 16:19:09 +02:00
{
2024-08-29 18:47:06 +03:00
for ( const auto & slotErase : pack . posPack )
callInterfaceIfPresent ( cl , cl . getOwner ( pack . artHolder ) , & IGameEventsReceiver : : artifactRemoved , ArtifactLocation ( pack . artHolder , slotErase ) ) ;
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor : : visitBulkMoveArtifacts ( BulkMoveArtifacts & pack )
{
2024-03-28 16:16:10 +02:00
const auto dstOwner = cl . getOwner ( pack . dstArtHolder ) ;
2025-04-05 22:55:57 +02:00
const auto applyMove = [ this , & pack , dstOwner ] ( const std : : vector < MoveArtifactInfo > & artsPack )
2023-10-19 16:19:09 +02:00
{
2024-03-28 16:16:10 +02:00
for ( const auto & slotToMove : artsPack )
2023-10-19 16:19:09 +02:00
{
2024-03-28 16:16:10 +02:00
const auto srcLoc = ArtifactLocation ( pack . srcArtHolder , slotToMove . srcPos ) ;
const auto dstLoc = ArtifactLocation ( pack . dstArtHolder , slotToMove . dstPos ) ;
callInterfaceIfPresent ( cl , pack . interfaceOwner , & IGameEventsReceiver : : artifactMoved , srcLoc , dstLoc ) ;
2024-06-22 19:29:39 +03:00
if ( slotToMove . askAssemble )
2024-03-28 16:16:10 +02:00
callInterfaceIfPresent ( cl , pack . interfaceOwner , & IGameEventsReceiver : : askToAssembleArtifact , dstLoc ) ;
if ( pack . interfaceOwner ! = dstOwner )
callInterfaceIfPresent ( cl , dstOwner , & IGameEventsReceiver : : artifactMoved , srcLoc , dstLoc ) ;
2023-10-19 16:19:09 +02:00
}
} ;
2024-06-23 23:48:19 +03:00
size_t possibleAssemblyNumOfArts = 0 ;
const auto calcPossibleAssemblyNumOfArts = [ & possibleAssemblyNumOfArts ] ( const auto & slotToMove )
{
if ( slotToMove . askAssemble )
possibleAssemblyNumOfArts + + ;
} ;
std : : for_each ( pack . artsPack0 . cbegin ( ) , pack . artsPack0 . cend ( ) , calcPossibleAssemblyNumOfArts ) ;
std : : for_each ( pack . artsPack1 . cbegin ( ) , pack . artsPack1 . cend ( ) , calcPossibleAssemblyNumOfArts ) ;
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.
2024-06-23 23:48:19 +03:00
callInterfaceIfPresent ( cl , pack . interfaceOwner , & IGameEventsReceiver : : bulkArtMovementStart ,
pack . artsPack0 . size ( ) + pack . artsPack1 . size ( ) , possibleAssemblyNumOfArts ) ;
2024-03-28 16:16:10 +02:00
if ( pack . interfaceOwner ! = dstOwner )
2024-06-23 23:48:19 +03:00
callInterfaceIfPresent ( cl , dstOwner , & IGameEventsReceiver : : bulkArtMovementStart ,
pack . artsPack0 . size ( ) + pack . artsPack1 . size ( ) , possibleAssemblyNumOfArts ) ;
2023-10-19 16:19:09 +02:00
applyMove ( pack . artsPack0 ) ;
2024-06-23 23:48:19 +03:00
if ( ! pack . artsPack1 . empty ( ) )
2023-10-19 16:19:09 +02:00
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
}
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
}
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 )
{
2025-01-23 14:39:56 +00:00
callAllInterfaces ( cl , & CGameInterface : : invalidatePaths ) ;
2024-08-26 13:49:50 +00:00
2024-09-04 00:13:00 +02:00
if ( pack . newWeekNotification )
2024-08-26 13:49:50 +00:00
{
const auto & newWeek = * pack . newWeekNotification ;
std : : string str = newWeek . text . toString ( ) ;
callAllInterfaces ( cl , & CGameInterface : : showInfoDialog , newWeek . type , str , newWeek . components , ( soundBase : : soundID ) newWeek . soundID ) ;
}
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor : : visitGiveBonus ( GiveBonus & pack )
{
2025-01-23 14:39:56 +00:00
callAllInterfaces ( cl , & CGameInterface : : invalidatePaths ) ;
2023-10-19 16:19:09 +02:00
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 > ( ) ) ;
2024-09-04 00:13:00 +02:00
if ( h )
2023-11-06 18:27:16 +02:00
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 ) ;
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . onObjectFadeOut ( obj , pack . initiator ) ;
GAME - > map ( ) . waitForOngoingAnimations ( ) ;
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor : : visitChangeObjPos ( ChangeObjPos & pack )
{
CGObjectInstance * obj = gs . getObjInstance ( pack . objid ) ;
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . onObjectFadeIn ( obj , pack . initiator ) ;
GAME - > map ( ) . waitForOngoingAnimations ( ) ;
2025-01-23 14:39:56 +00:00
callAllInterfaces ( cl , & CGameInterface : : invalidatePaths ) ;
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor : : visitPlayerEndsGame ( PlayerEndsGame & pack )
{
callAllInterfaces ( cl , & IGameEventsReceiver : : gameOver , pack . player , pack . victoryLossCheckResult ) ;
2024-12-10 11:15:58 +00:00
bool localHumanWinsGame = vstd : : contains ( cl . playerint , pack . player ) & & cl . getPlayerState ( pack . player ) - > human & & pack . victoryLossCheckResult . victory ( ) ;
2025-02-11 15:23:33 +00:00
bool lastHumanEndsGame = GAME - > server ( ) . howManyPlayerInterfaces ( ) = = 1 & & vstd : : contains ( cl . playerint , pack . player ) & & cl . getPlayerState ( pack . player ) - > human & & ! settings [ " session " ] [ " spectate " ] . Bool ( ) ;
2024-04-07 14:19:57 +03:00
2024-12-10 11:15:58 +00:00
if ( lastHumanEndsGame | | localHumanWinsGame )
2024-04-07 14:19:57 +03:00
{
assert ( adventureInt ) ;
if ( adventureInt )
{
2025-02-10 21:49:23 +00:00
ENGINE - > windows ( ) . popWindows ( ENGINE - > windows ( ) . count ( ) ) ;
2024-04-07 14:19:57 +03:00
adventureInt . reset ( ) ;
}
2025-02-11 15:23:33 +00:00
GAME - > server ( ) . showHighScoresAndEndGameplay ( pack . player , pack . victoryLossCheckResult . victory ( ) , pack . statistic ) ;
2024-04-07 14:19:57 +03:00
}
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 " ) ;
2025-03-03 21:45:58 +00:00
GAME - > onShutdownRequested ( 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 ( ) ;
2024-09-04 00:13:00 +02:00
for ( PlayerColor player ( 0 ) ; player < PlayerColor : : PLAYER_LIMIT ; + + player )
2023-10-19 16:19:09 +02:00
{
2024-09-04 00:13:00 +02:00
if ( cl . gameState ( ) - > isPlayerMakingTurn ( player ) )
2023-10-19 16:19:09 +02:00
{
callAllInterfaces ( cl , & IGameEventsReceiver : : playerStartsTurn , player ) ;
callOnlyThatInterface ( cl , player , & CGameInterface : : yourTurn , QueryID : : NONE ) ;
}
}
} ;
for ( auto player : pack . players )
{
2025-02-11 15:23:33 +00:00
auto & plSettings = GAME - > server ( ) . si - > getIthPlayersSettings ( player ) ;
2023-10-19 16:19:09 +02:00
if ( pack . playerConnectionId = = PlayerSettings : : PLAYER_AI )
{
plSettings . connectedPlayerIDs . clear ( ) ;
cl . initPlayerEnvironments ( ) ;
initInterfaces ( ) ;
}
2025-02-11 15:23:33 +00:00
else if ( pack . playerConnectionId = = GAME - > server ( ) . logicConnection - > connectionID )
2023-10-19 16:19:09 +02:00
{
plSettings . connectedPlayerIDs . insert ( pack . playerConnectionId ) ;
cl . playerint . clear ( ) ;
initInterfaces ( ) ;
}
}
}
void ApplyClientNetPackVisitor : : visitRemoveBonus ( RemoveBonus & pack )
{
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 > ( ) ) ;
2024-09-04 00:13:00 +02:00
if ( h )
2023-11-06 18:27:16 +02:00
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 ) ;
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . onObjectFadeOut ( o , pack . initiator ) ;
2023-10-19 16:19:09 +02:00
//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 ) ;
}
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . waitForOngoingAnimations ( ) ;
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor : : visitRemoveObject ( RemoveObject & pack )
{
2025-01-23 14:39:56 +00:00
callAllInterfaces ( cl , & CGameInterface : : invalidatePaths ) ;
2023-10-19 16:19:09 +02:00
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 ) ;
2025-02-11 15:23:33 +00:00
switch ( pack . result )
2023-10-19 16:19:09 +02:00
{
2025-02-11 15:23:33 +00:00
case TryMoveHero : : EMBARK :
GAME - > map ( ) . onBeforeHeroEmbark ( h , pack . start , pack . end ) ;
break ;
case TryMoveHero : : TELEPORTATION :
GAME - > map ( ) . onBeforeHeroTeleported ( h , pack . start , pack . end ) ;
break ;
case TryMoveHero : : DISEMBARK :
GAME - > map ( ) . onBeforeHeroDisembark ( h , pack . start , pack . end ) ;
break ;
2023-10-19 16:19:09 +02:00
}
}
void ApplyClientNetPackVisitor : : visitTryMoveHero ( TryMoveHero & pack )
{
const CGHeroInstance * h = cl . getHero ( pack . id ) ;
2025-01-23 14:39:56 +00:00
callAllInterfaces ( cl , & CGameInterface : : invalidatePaths ) ;
2023-10-19 16:19:09 +02:00
2025-02-11 15:23:33 +00:00
switch ( pack . result )
2023-10-19 16:19:09 +02:00
{
2025-02-11 15:23:33 +00:00
case TryMoveHero : : SUCCESS :
GAME - > map ( ) . onHeroMoved ( h , pack . start , pack . end ) ;
break ;
case TryMoveHero : : EMBARK :
GAME - > map ( ) . onAfterHeroEmbark ( h , pack . start , pack . end ) ;
break ;
case TryMoveHero : : TELEPORTATION :
GAME - > map ( ) . onAfterHeroTeleported ( h , pack . start , pack . end ) ;
break ;
case TryMoveHero : : DISEMBARK :
GAME - > map ( ) . onAfterHeroDisembark ( h , pack . start , pack . end ) ;
break ;
2023-10-19 16:19:09 +02:00
}
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
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . onObjectInstantRemove ( town , town - > getOwner ( ) ) ;
GAME - > map ( ) . onObjectInstantAdd ( town , town - > getOwner ( ) ) ;
2023-10-19 16:19:09 +02:00
}
2025-02-11 15:23:33 +00:00
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
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . onObjectInstantRemove ( town , town - > getOwner ( ) ) ;
GAME - > map ( ) . 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 )
{
2025-03-13 21:49:57 +00:00
auto * h = gs . getMap ( ) . getHero ( pack . hid ) ;
2024-10-05 19:37:52 +00:00
if ( h - > getHeroTypeID ( ) ! = pack . hid )
2023-10-19 16:19:09 +02:00
{
logNetwork - > error ( " Something wrong with hero recruited! " ) ;
}
2025-03-13 21:49:57 +00:00
if ( callInterfaceIfPresent ( cl , h - > tempOwner , & IGameEventsReceiver : : heroCreated , h ) )
2023-10-19 16:19:09 +02:00
{
if ( const CGTownInstance * t = gs . getTown ( pack . tid ) )
callInterfaceIfPresent ( cl , h - > getOwner ( ) , & IGameEventsReceiver : : heroInGarrisonChange , t ) ;
}
2025-03-13 21:49:57 +00:00
GAME - > map ( ) . onObjectInstantAdd ( h , h - > getOwner ( ) ) ;
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor : : visitGiveHero ( GiveHero & pack )
{
CGHeroInstance * h = gs . getHero ( pack . id ) ;
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . onObjectInstantAdd ( h , h - > getOwner ( ) ) ;
2023-10-19 16:19:09 +02:00
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
2025-02-11 15:23:33 +00:00
if ( pack . what = = ObjProperty : : OWNER )
2023-10-19 16:19:09 +02:00
{
auto object = gs . getObjInstance ( pack . id ) ;
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . onObjectInstantRemove ( object , object - > getOwner ( ) ) ;
2023-10-19 16:19:09 +02:00
}
}
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
2025-02-11 15:23:33 +00:00
if ( pack . what = = ObjProperty : : OWNER )
2023-10-19 16:19:09 +02:00
{
auto object = gs . getObjInstance ( pack . id ) ;
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . onObjectInstantAdd ( object , object - > getOwner ( ) ) ;
2023-10-19 16:19:09 +02:00
}
}
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 ) ;
2025-03-09 21:51:33 +00:00
const auto & commander = hero - > getCommander ( ) ;
2023-10-19 16:19:09 +02:00
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
2025-03-14 17:07:30 +00:00
callOnlyThatBattleInterface ( cl , pack . info - > getSide ( BattleSide : : ATTACKER ) . color , & IBattleEventsReceiver : : battleStartBefore , pack . battleID , pack . info - > getSideArmy ( BattleSide : : ATTACKER ) , pack . info - > getSideArmy ( BattleSide : : DEFENDER ) ,
pack . info - > tile , pack . info - > getSide ( BattleSide : : ATTACKER ) . hero , pack . info - > getSideHero ( BattleSide : : DEFENDER ) ) ;
callOnlyThatBattleInterface ( cl , pack . info - > getSide ( BattleSide : : DEFENDER ) . color , & IBattleEventsReceiver : : battleStartBefore , pack . battleID , pack . info - > getSideArmy ( BattleSide : : ATTACKER ) , pack . info - > getSideArmy ( BattleSide : : DEFENDER ) ,
pack . info - > tile , pack . info - > getSide ( BattleSide : : ATTACKER ) . hero , pack . info - > getSideHero ( BattleSide : : DEFENDER ) ) ;
callOnlyThatBattleInterface ( cl , PlayerColor : : SPECTATOR , & IBattleEventsReceiver : : battleStartBefore , pack . battleID , pack . info - > getSideArmy ( BattleSide : : ATTACKER ) , pack . info - > getSideArmy ( BattleSide : : DEFENDER ) ,
pack . info - > tile , pack . info - > getSide ( BattleSide : : ATTACKER ) . hero , pack . info - > getSideHero ( BattleSide : : DEFENDER ) ) ;
2023-10-19 16:19:09 +02:00
}
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
2024-12-25 21:25:06 +00:00
if ( activated - > isHypnotized ( ) )
2023-10-19 16:19:09 +02:00
{
2024-08-11 20:22:35 +00:00
playerToCall = gs . getBattle ( pack . battleID ) - > getSide ( BattleSide : : ATTACKER ) . color = = activated - > unitOwner ( )
? gs . getBattle ( pack . battleID ) - > getSide ( BattleSide : : DEFENDER ) . color
: gs . getBattle ( pack . battleID ) - > getSide ( BattleSide : : ATTACKER ) . color ;
2023-10-19 16:19:09 +02:00
}
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 ) ;
2024-06-24 03:23:26 +02:00
// battleStacksAttacked should be executed before BattleAttack.applyGs() to play animation before damaging unit
2023-10-19 16:19:09 +02:00
// 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 )
{
2025-04-07 20:10:14 +02:00
if ( ! pack . learnedSpells . spells . empty ( ) )
{
const auto hero = GAME - > interface ( ) - > cb - > getHero ( pack . learnedSpells . hid ) ;
assert ( hero ) ;
callInterfaceIfPresent ( cl , pack . victor , & CGameInterface : : showInfoDialog , EInfoWindowMode : : MODAL ,
UIHelper : : getEagleEyeInfoWindowText ( * hero , pack . learnedSpells . spells ) , UIHelper : : getSpellsComponents ( pack . learnedSpells . spells ) , soundBase : : soundID ( 0 ) ) ;
}
2025-04-15 23:08:30 +02:00
if ( ! pack . artifacts . empty ( ) )
2025-04-05 22:55:57 +02:00
{
2025-04-15 23:08:30 +02:00
const auto artSet = GAME - > interface ( ) - > cb - > getArtSet ( ArtifactLocation ( pack . artifacts . front ( ) . dstArtHolder ) ) ;
assert ( artSet ) ;
std : : vector < Component > artComponents ;
for ( const auto & artPack : pack . artifacts )
{
auto packComponents = UIHelper : : getArtifactsComponents ( * artSet , artPack . artsPack0 ) ;
artComponents . insert ( artComponents . end ( ) , std : : make_move_iterator ( packComponents . begin ( ) ) , std : : make_move_iterator ( packComponents . end ( ) ) ) ;
}
2025-04-07 20:10:14 +02:00
callInterfaceIfPresent ( cl , pack . victor , & CGameInterface : : showInfoDialog , EInfoWindowMode : : MODAL , UIHelper : : getArtifactsInfoWindowText ( ) ,
artComponents , soundBase : : soundID ( 0 ) ) ;
2025-04-15 23:08:30 +02:00
}
2025-04-05 22:55:57 +02:00
for ( auto & artPack : pack . artifacts )
visitBulkMoveArtifacts ( artPack ) ;
2025-04-07 20:10:14 +02:00
if ( pack . raisedStack . getCreature ( ) )
callInterfaceIfPresent ( cl , pack . victor , & CGameInterface : : showInfoDialog , EInfoWindowMode : : AUTO ,
UIHelper : : getNecromancyInfoWindowText ( pack . raisedStack ) , std : : vector < Component > { Component ( ComponentType : : CREATURE , pack . raisedStack . getId ( ) ,
pack . raisedStack . count ) } , UIHelper : : getNecromancyInfoWindowSound ( ) ) ;
2025-04-05 17:45:24 +02:00
callInterfaceIfPresent ( cl , pack . victor , & IGameEventsReceiver : : battleResultsApplied ) ;
callInterfaceIfPresent ( cl , pack . loser , & IGameEventsReceiver : : battleResultsApplied ) ;
2023-10-19 16:19:09 +02:00
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 ) ;
2025-03-02 12:56:01 +00:00
if ( ! cl . waitingRequest . tryRemovingElement ( pack . requestID ) )
2023-10-19 16:19:09 +02:00
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
2025-02-11 15:23:33 +00:00
GAME - > server ( ) . 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 ) ;
2025-02-11 15:23:33 +00:00
GAME - > server ( ) . getGameChat ( ) . onNewGameMessageReceived ( pack . player , pack . text ) ;
2023-10-19 16:19:09 +02:00
}
void ApplyClientNetPackVisitor : : visitAdvmapSpellCast ( AdvmapSpellCast & pack )
{
2025-01-23 14:39:56 +00:00
callAllInterfaces ( cl , & CGameInterface : : invalidatePaths ) ;
2023-10-19 16:19:09 +02:00
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-08-20 17:15:50 +03:00
const auto * market = cl . getMarket ( 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-08-20 17:15:50 +03:00
const auto market = cl . getMarket ( pack . object ) ;
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 )
{
2025-01-23 14:39:56 +00:00
callAllInterfaces ( cl , & CGameInterface : : invalidatePaths ) ;
2023-10-19 16:19:09 +02:00
2025-03-13 19:42:18 +00:00
const CGObjectInstance * obj = pack . newObject . get ( ) ;
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . onObjectFadeIn ( obj , pack . initiator ) ;
2023-10-19 16:19:09 +02:00
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
2025-02-11 15:23:33 +00:00
GAME - > map ( ) . 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 )
{
2025-01-23 14:39:56 +00:00
callAllInterfaces ( cl , & CGameInterface : : invalidatePaths ) ;
2023-10-19 16:19:09 +02:00
}