2023-10-19 16:19:09 +02:00
/*
* CCallback.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 "CCallback.h"
#include "lib/CCreatureHandler.h"
#include "lib/gameState/CGameState.h"
#include "client/CPlayerInterface.h"
#include "client/Client.h"
#include "lib/mapping/CMap.h"
2023-10-23 13:59:15 +03:00
#include "lib/mapObjects/CGHeroInstance.h"
2024-01-09 16:43:36 +02:00
#include "lib/mapObjects/CGTownInstance.h"
2024-07-20 12:55:17 +00:00
#include "lib/texts/CGeneralTextHandler.h"
2023-10-19 16:19:09 +02:00
#include "lib/CArtHandler.h"
#include "lib/GameConstants.h"
#include "lib/CPlayerState.h"
#include "lib/UnlockGuard.h"
#include "lib/battle/BattleInfo.h"
2023-10-23 13:59:15 +03:00
#include "lib/networkPacks/PacksForServer.h"
2024-10-08 19:55:51 +00:00
#include "lib/networkPacks/SaveLocalState.h"
2023-10-19 16:19:09 +02:00
bool CCallback :: teleportHero ( const CGHeroInstance * who , const CGTownInstance * where )
{
CastleTeleportHero pack ( who -> id , where -> id , 1 );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return true ;
}
2024-04-18 15:13:16 +03:00
void CCallback :: moveHero ( const CGHeroInstance * h , const int3 & destination , bool transit )
2023-10-19 16:19:09 +02:00
{
2024-04-18 15:13:16 +03:00
MoveHero pack ({ destination }, h -> id , transit );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2024-04-18 15:13:16 +03:00
}
void CCallback :: moveHero ( const CGHeroInstance * h , const std :: vector < int3 > & path , bool transit )
{
MoveHero pack ( path , h -> id , transit );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
int CCallback :: selectionMade ( int selection , QueryID queryID )
{
return sendQueryReply ( selection , queryID );
}
int CCallback :: sendQueryReply ( std :: optional < int32_t > reply , QueryID queryID )
{
ASSERT_IF_CALLED_WITH_PLAYER
if ( queryID == QueryID ( - 1 ))
{
logGlobal -> error ( "Cannot answer the query -1!" );
return - 1 ;
}
QueryReply pack ( queryID , reply );
pack . player = * player ;
2024-10-04 14:01:10 +00:00
return sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
void CCallback :: recruitCreatures ( const CGDwelling * obj , const CArmedInstance * dst , CreatureID ID , ui32 amount , si32 level )
{
// TODO exception for neutral dwellings shouldn't be hardcoded
if ( player != obj -> tempOwner && obj -> ID != Obj :: WAR_MACHINE_FACTORY && obj -> ID != Obj :: REFUGEE_CAMP )
return ;
RecruitCreatures pack ( obj -> id , dst -> id , ID , amount , level );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
bool CCallback :: dismissCreature ( const CArmedInstance * obj , SlotID stackPos )
{
if (( player && obj -> tempOwner != player ) || ( obj -> stacksCount () < 2 && obj -> needsLastStack ()))
return false ;
DisbandCreature pack ( stackPos , obj -> id );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return true ;
}
bool CCallback :: upgradeCreature ( const CArmedInstance * obj , SlotID stackPos , CreatureID newID )
{
UpgradeCreature pack ( stackPos , obj -> id , newID );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return false ;
}
void CCallback :: endTurn ()
{
logGlobal -> trace ( "Player %d ended his turn." , player -> getNum ());
EndTurn pack ;
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
int CCallback :: swapCreatures ( const CArmedInstance * s1 , const CArmedInstance * s2 , SlotID p1 , SlotID p2 )
{
ArrangeStacks pack ( 1 , p1 , p2 , s1 -> id , s2 -> id , 0 );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return 0 ;
}
int CCallback :: mergeStacks ( const CArmedInstance * s1 , const CArmedInstance * s2 , SlotID p1 , SlotID p2 )
{
ArrangeStacks pack ( 2 , p1 , p2 , s1 -> id , s2 -> id , 0 );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return 0 ;
}
int CCallback :: splitStack ( const CArmedInstance * s1 , const CArmedInstance * s2 , SlotID p1 , SlotID p2 , int val )
{
ArrangeStacks pack ( 3 , p1 , p2 , s1 -> id , s2 -> id , val );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return 0 ;
}
int CCallback :: bulkMoveArmy ( ObjectInstanceID srcArmy , ObjectInstanceID destArmy , SlotID srcSlot )
{
BulkMoveArmy pack ( srcArmy , destArmy , srcSlot );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return 0 ;
}
int CCallback :: bulkSplitStack ( ObjectInstanceID armyId , SlotID srcSlot , int howMany )
{
BulkSplitStack pack ( armyId , srcSlot , howMany );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return 0 ;
}
int CCallback :: bulkSmartSplitStack ( ObjectInstanceID armyId , SlotID srcSlot )
{
BulkSmartSplitStack pack ( armyId , srcSlot );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return 0 ;
}
int CCallback :: bulkMergeStacks ( ObjectInstanceID armyId , SlotID srcSlot )
{
BulkMergeStacks pack ( armyId , srcSlot );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return 0 ;
}
bool CCallback :: dismissHero ( const CGHeroInstance * hero )
{
if ( player != hero -> tempOwner ) return false ;
DismissHero pack ( hero -> id );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return true ;
}
bool CCallback :: swapArtifacts ( const ArtifactLocation & l1 , const ArtifactLocation & l2 )
{
ExchangeArtifacts ea ;
ea . src = l1 ;
ea . dst = l2 ;
2024-10-04 14:01:10 +00:00
sendRequest ( ea );
2023-10-19 16:19:09 +02:00
return true ;
}
/**
* Assembles or disassembles a combination artifact.
* @param hero Hero holding the artifact(s).
* @param artifactSlot The worn slot ID of the combination- or constituent artifact.
* @param assemble True for assembly operation, false for disassembly.
* @param assembleTo If assemble is true, this represents the artifact ID of the combination
* artifact to assemble to. Otherwise it's not used.
*/
2024-06-23 23:48:19 +03:00
void CCallback :: assembleArtifacts ( const ObjectInstanceID & heroID , ArtifactPosition artifactSlot , bool assemble , ArtifactID assembleTo )
2023-10-19 16:19:09 +02:00
{
2024-06-23 23:48:19 +03:00
AssembleArtifacts aa ( heroID , artifactSlot , assemble , assembleTo );
2024-10-04 14:01:10 +00:00
sendRequest ( aa );
2023-10-19 16:19:09 +02:00
}
void CCallback :: bulkMoveArtifacts ( ObjectInstanceID srcHero , ObjectInstanceID dstHero , bool swap , bool equipped , bool backpack )
{
BulkExchangeArtifacts bma ( srcHero , dstHero , swap , equipped , backpack );
2024-10-04 14:01:10 +00:00
sendRequest ( bma );
2023-10-19 16:19:09 +02:00
}
2024-03-06 15:16:35 +02:00
void CCallback :: scrollBackpackArtifacts ( ObjectInstanceID hero , bool left )
{
ManageBackpackArtifacts mba ( hero , ManageBackpackArtifacts :: ManageCmd :: SCROLL_RIGHT );
if ( left )
mba . cmd = ManageBackpackArtifacts :: ManageCmd :: SCROLL_LEFT ;
2024-10-04 14:01:10 +00:00
sendRequest ( mba );
2024-03-06 15:16:35 +02:00
}
2024-09-26 13:47:26 +03:00
void CCallback :: sortBackpackArtifactsBySlot ( const ObjectInstanceID hero )
{
ManageBackpackArtifacts mba ( hero , ManageBackpackArtifacts :: ManageCmd :: SORT_BY_SLOT );
2024-10-07 13:25:39 +00:00
sendRequest ( mba );
2024-09-26 13:47:26 +03:00
}
void CCallback :: sortBackpackArtifactsByCost ( const ObjectInstanceID hero )
{
ManageBackpackArtifacts mba ( hero , ManageBackpackArtifacts :: ManageCmd :: SORT_BY_COST );
2024-10-07 13:25:39 +00:00
sendRequest ( mba );
2024-09-26 13:47:26 +03:00
}
void CCallback :: sortBackpackArtifactsByClass ( const ObjectInstanceID hero )
{
ManageBackpackArtifacts mba ( hero , ManageBackpackArtifacts :: ManageCmd :: SORT_BY_CLASS );
2024-10-07 13:25:39 +00:00
sendRequest ( mba );
2024-09-26 13:47:26 +03:00
}
2024-03-30 17:18:50 +02:00
void CCallback :: manageHeroCostume ( ObjectInstanceID hero , size_t costumeIndex , bool saveCostume )
{
ManageEquippedArtifacts mea ( hero , costumeIndex , saveCostume );
2024-10-04 14:01:10 +00:00
sendRequest ( mea );
2024-03-30 17:18:50 +02:00
}
2023-10-19 16:19:09 +02:00
void CCallback :: eraseArtifactByClient ( const ArtifactLocation & al )
{
EraseArtifactByClient ea ( al );
2024-10-04 14:01:10 +00:00
sendRequest ( ea );
2023-10-19 16:19:09 +02:00
}
bool CCallback :: buildBuilding ( const CGTownInstance * town , BuildingID buildingID )
{
if ( town -> tempOwner != player )
return false ;
if ( canBuildStructure ( town , buildingID ) != EBuildingState :: ALLOWED )
return false ;
BuildStructure pack ( town -> id , buildingID );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
return true ;
}
2024-09-03 16:31:07 +00:00
bool CCallback :: visitTownBuilding ( const CGTownInstance * town , BuildingID buildingID )
2024-08-04 15:44:57 +02:00
{
if ( town -> tempOwner != player )
return false ;
2024-09-03 16:31:07 +00:00
VisitTownBuilding pack ( town -> id , buildingID );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2024-08-04 15:44:57 +02:00
return true ;
}
2023-10-19 16:19:09 +02:00
void CBattleCallback :: battleMakeSpellAction ( const BattleID & battleID , const BattleAction & action )
{
assert ( action . actionType == EActionType :: HERO_SPELL );
MakeAction mca ( action );
mca . battleID = battleID ;
2024-10-04 14:01:10 +00:00
sendRequest ( mca );
2023-10-19 16:19:09 +02:00
}
2024-10-04 14:01:10 +00:00
int CBattleCallback :: sendRequest ( const CPackForServer & request )
2023-10-19 16:19:09 +02:00
{
int requestID = cl -> sendRequest ( request , * getPlayerID ());
if ( waitTillRealize )
{
logGlobal -> trace ( "We'll wait till request %d is answered. \n " , requestID );
auto gsUnlocker = vstd :: makeUnlockSharedGuardIf ( CGameState :: mutex , unlockGsWhenWaiting );
2025-03-02 12:56:01 +00:00
cl -> waitingRequest . waitWhileContains ( requestID );
2023-10-19 16:19:09 +02:00
}
return requestID ;
}
2024-09-28 15:05:13 +02:00
void CCallback :: spellResearch ( const CGTownInstance * town , SpellID spellAtSlot , bool accepted )
2024-09-27 22:47:22 +02:00
{
2024-09-28 15:05:13 +02:00
SpellResearch pack ( town -> id , spellAtSlot , accepted );
2024-10-07 13:25:39 +00:00
sendRequest ( pack );
2024-09-27 22:47:22 +02:00
}
2023-10-19 16:19:09 +02:00
void CCallback :: swapGarrisonHero ( const CGTownInstance * town )
{
if ( town -> tempOwner == * player || ( town -> garrisonHero && town -> garrisonHero -> tempOwner == * player ))
{
GarrisonHeroSwap pack ( town -> id );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
}
void CCallback :: buyArtifact ( const CGHeroInstance * hero , ArtifactID aid )
{
if ( hero -> tempOwner != * player ) return ;
BuyArtifact pack ( hero -> id , aid );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
2024-08-20 17:15:50 +03:00
void CCallback :: trade ( const ObjectInstanceID marketId , EMarketMode mode , TradeItemSell id1 , TradeItemBuy id2 , ui32 val1 , const CGHeroInstance * hero )
2023-10-19 16:19:09 +02:00
{
2024-08-20 17:15:50 +03:00
trade ( marketId , mode , std :: vector ( 1 , id1 ), std :: vector ( 1 , id2 ), std :: vector ( 1 , val1 ), hero );
2023-10-19 16:19:09 +02:00
}
2024-08-20 17:15:50 +03:00
void CCallback :: trade ( const ObjectInstanceID marketId , EMarketMode mode , const std :: vector < TradeItemSell > & id1 , const std :: vector < TradeItemBuy > & id2 , const std :: vector < ui32 > & val1 , const CGHeroInstance * hero )
2023-10-19 16:19:09 +02:00
{
TradeOnMarketplace pack ;
2024-08-20 17:15:50 +03:00
pack . marketId = marketId ;
2023-10-19 16:19:09 +02:00
pack . heroId = hero ? hero -> id : ObjectInstanceID ();
pack . mode = mode ;
pack . r1 = id1 ;
pack . r2 = id2 ;
pack . val = val1 ;
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
2023-11-06 18:27:16 +02:00
void CCallback :: setFormation ( const CGHeroInstance * hero , EArmyFormation mode )
2023-10-19 16:19:09 +02:00
{
2023-11-06 18:27:16 +02:00
SetFormation pack ( hero -> id , mode );
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
2024-01-20 01:09:55 +01:00
void CCallback :: recruitHero ( const CGObjectInstance * townOrTavern , const CGHeroInstance * hero , const HeroTypeID & nextHero )
2023-10-19 16:19:09 +02:00
{
assert ( townOrTavern );
assert ( hero );
2024-10-05 19:37:52 +00:00
HireHero pack ( hero -> getHeroTypeID (), townOrTavern -> id , nextHero );
2023-10-19 16:19:09 +02:00
pack . player = * player ;
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
2024-10-08 19:55:51 +00:00
void CCallback :: saveLocalState ( const JsonNode & data )
{
SaveLocalState state ;
state . data = data ;
state . player = * player ;
sendRequest ( state );
}
2023-10-19 16:19:09 +02:00
void CCallback :: save ( const std :: string & fname )
{
cl -> save ( fname );
}
void CCallback :: gamePause ( bool pause )
{
if ( pause )
{
GamePause pack ;
pack . player = * player ;
2024-10-04 14:01:10 +00:00
sendRequest ( pack );
2023-10-19 16:19:09 +02:00
}
else
{
sendQueryReply ( 0 , QueryID :: CLIENT );
}
}
void CCallback :: sendMessage ( const std :: string & mess , const CGObjectInstance * currentObject )
{
ASSERT_IF_CALLED_WITH_PLAYER
PlayerMessage pm ( mess , currentObject ? currentObject -> id : ObjectInstanceID ( - 1 ));
if ( player )
pm . player = * player ;
2024-10-04 14:01:10 +00:00
sendRequest ( pm );
2023-10-19 16:19:09 +02:00
}
void CCallback :: buildBoat ( const IShipyard * obj )
{
BuildBoat bb ;
bb . objid = dynamic_cast < const CGObjectInstance *> ( obj ) -> id ;
2024-10-04 14:01:10 +00:00
sendRequest ( bb );
2023-10-19 16:19:09 +02:00
}
CCallback :: CCallback ( CGameState * GS , std :: optional < PlayerColor > Player , CClient * C )
: CBattleCallback ( Player , C )
{
gs = GS ;
waitTillRealize = false ;
unlockGsWhenWaiting = false ;
}
CCallback ::~ CCallback () = default ;
bool CCallback :: canMoveBetween ( const int3 & a , const int3 & b )
{
//bidirectional
2025-03-03 15:19:38 +00:00
return gs -> getMap (). canMoveBetween ( a , b );
2023-10-19 16:19:09 +02:00
}
std :: optional < PlayerColor > CCallback :: getPlayerID () const
{
return CBattleCallback :: getPlayerID ();
}
int3 CCallback :: getGuardingCreaturePosition ( int3 tile )
{
2025-03-03 15:19:38 +00:00
if ( ! gs -> getMap (). isInTheMap ( tile ))
2023-10-19 16:19:09 +02:00
return int3 ( - 1 , - 1 , - 1 );
2025-03-03 15:19:38 +00:00
return gs -> getMap (). guardingCreaturePositions [ tile . z ][ tile . x ][ tile . y ];
2023-10-19 16:19:09 +02:00
}
void CCallback :: dig ( const CGObjectInstance * hero )
{
DigWithHero dwh ;
dwh . id = hero -> id ;
2024-10-04 14:01:10 +00:00
sendRequest ( dwh );
2023-10-19 16:19:09 +02:00
}
void CCallback :: castSpell ( const CGHeroInstance * hero , SpellID spellID , const int3 & pos )
{
CastAdvSpell cas ;
cas . hid = hero -> id ;
cas . sid = spellID ;
cas . pos = pos ;
2024-10-04 14:01:10 +00:00
sendRequest ( cas );
2023-10-19 16:19:09 +02:00
}
int CCallback :: mergeOrSwapStacks ( const CArmedInstance * s1 , const CArmedInstance * s2 , SlotID p1 , SlotID p2 )
{
if ( s1 -> getCreature ( p1 ) == s2 -> getCreature ( p2 ))
return mergeStacks ( s1 , s2 , p1 , p2 );
else
return swapCreatures ( s1 , s2 , p1 , p2 );
}
void CCallback :: registerBattleInterface ( std :: shared_ptr < IBattleEventsReceiver > battleEvents )
{
cl -> additionalBattleInts [ * player ]. push_back ( battleEvents );
}
void CCallback :: unregisterBattleInterface ( std :: shared_ptr < IBattleEventsReceiver > battleEvents )
{
cl -> additionalBattleInts [ * player ] -= battleEvents ;
}
CBattleCallback :: CBattleCallback ( std :: optional < PlayerColor > player , CClient * C ) :
cl ( C ),
player ( player )
{
}
void CBattleCallback :: battleMakeUnitAction ( const BattleID & battleID , const BattleAction & action )
{
assert ( ! cl -> gs -> getBattle ( battleID ) -> tacticDistance );
MakeAction ma ;
ma . ba = action ;
ma . battleID = battleID ;
2024-10-04 14:01:10 +00:00
sendRequest ( ma );
2023-10-19 16:19:09 +02:00
}
void CBattleCallback :: battleMakeTacticAction ( const BattleID & battleID , const BattleAction & action )
{
assert ( cl -> gs -> getBattle ( battleID ) -> tacticDistance );
MakeAction ma ;
ma . ba = action ;
ma . battleID = battleID ;
2024-10-04 14:01:10 +00:00
sendRequest ( ma );
2023-10-19 16:19:09 +02:00
}
std :: optional < BattleAction > CBattleCallback :: makeSurrenderRetreatDecision ( const BattleID & battleID , const BattleStateInfoForRetreat & battleState )
{
return cl -> playerint [ getPlayerID (). value ()] -> makeSurrenderRetreatDecision ( battleID , battleState );
}
std :: shared_ptr < CPlayerBattleCallback > CBattleCallback :: getBattle ( const BattleID & battleID )
{
2024-01-16 15:21:49 +02:00
if ( activeBattles . count ( battleID ))
return activeBattles . at ( battleID );
throw std :: runtime_error ( "Failed to find battle " + std :: to_string ( battleID . getNum ()) + " of player " + player -> toString () + ". Number of ongoing battles: " + std :: to_string ( activeBattles . size ()));
2023-10-19 16:19:09 +02:00
}
std :: optional < PlayerColor > CBattleCallback :: getPlayerID () const
{
return player ;
}
void CBattleCallback :: onBattleStarted ( const IBattleInfo * info )
{
2024-01-16 15:21:49 +02:00
if ( activeBattles . count ( info -> getBattleID ()) > 0 )
throw std :: runtime_error ( "Player " + player -> toString () + " is already engaged in battle " + std :: to_string ( info -> getBattleID (). getNum ()));
logGlobal -> debug ( "Battle %d started for player %s" , info -> getBattleID (), player -> toString ());
2023-10-19 16:19:09 +02:00
activeBattles [ info -> getBattleID ()] = std :: make_shared < CPlayerBattleCallback > ( info , * getPlayerID ());
}
void CBattleCallback :: onBattleEnded ( const BattleID & battleID )
{
2024-01-16 15:21:49 +02:00
if ( activeBattles . count ( battleID ) == 0 )
throw std :: runtime_error ( "Player " + player -> toString () + " is not engaged in battle " + std :: to_string ( battleID . getNum ()));
logGlobal -> debug ( "Battle %d ended for player %s" , battleID , player -> toString ());
2023-10-19 16:19:09 +02:00
activeBattles . erase ( battleID );
}