2017-07-13 10:26:03 +02:00
/*
* CPlayerInterface . 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
*
*/
2011-12-14 00:23:17 +03:00
# include "StdInc.h"
2023-04-17 01:02:31 +02:00
# include "CPlayerInterface.h"
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
# include <vcmi/Artifact.h>
2023-05-08 14:18:34 +02:00
# include "adventureMap/AdventureMapInterface.h"
2023-03-01 12:31:23 +02:00
# include "mapView/mapHandler.h"
2023-02-10 16:26:32 +02:00
# include "adventureMap/CList.h"
2022-12-09 13:38:46 +02:00
# include "battle/BattleInterface.h"
# include "battle/BattleEffectsController.h"
# include "battle/BattleFieldController.h"
# include "battle/BattleInterfaceClasses.h"
2022-12-21 17:06:47 +02:00
# include "battle/BattleWindow.h"
2009-05-20 13:08:56 +03:00
# include "../CCallback.h"
2014-07-13 20:53:37 +03:00
# include "windows/CCastleInterface.h"
2023-05-18 22:31:05 +02:00
# include "eventsSDL/InputHandler.h"
2023-06-26 20:51:10 +02:00
# include "mainmenu/CMainMenu.h"
2023-01-05 19:34:37 +02:00
# include "gui/CursorHandler.h"
2014-07-13 20:53:37 +03:00
# include "windows/CKingdomInterface.h"
2008-08-27 13:19:18 +03:00
# include "CGameInfo.h"
2023-04-17 01:02:31 +02:00
# include "PlayerLocalState.h"
2023-02-02 21:41:39 +02:00
# include "CMT.h"
2014-07-13 20:53:37 +03:00
# include "windows/CHeroWindow.h"
# include "windows/CCreatureWindow.h"
# include "windows/CQuestLog.h"
2023-02-20 22:16:50 +02:00
# include "windows/CPuzzleWindow.h"
2014-07-15 10:14:49 +03:00
# include "widgets/CComponent.h"
2023-07-07 00:08:29 +02:00
# include "widgets/CGarrisonInt.h"
2023-02-01 20:42:06 +02:00
# include "widgets/Buttons.h"
2014-07-13 20:53:37 +03:00
# include "windows/CTradeWindow.h"
2017-07-03 20:38:00 +02:00
# include "windows/CSpellWindow.h"
2012-09-29 13:59:43 +03:00
# include "../lib/CConfigHandler.h"
2014-07-13 20:53:37 +03:00
# include "windows/GUIClasses.h"
2023-02-01 20:42:06 +02:00
# include "render/CAnimation.h"
# include "render/IImage.h"
2010-12-20 23:22:53 +02:00
# include "../lib/CArtHandler.h"
# include "../lib/CGeneralTextHandler.h"
# include "../lib/CHeroHandler.h"
2023-04-30 16:35:15 +02:00
# include "../lib/bonuses/CBonusSystemNode.h"
2023-04-30 15:52:48 +02:00
# include "../lib/bonuses/Limiters.h"
# include "../lib/bonuses/Updaters.h"
2023-04-30 16:35:15 +02:00
# include "../lib/bonuses/Propagators.h"
2016-09-10 02:32:40 +02:00
# include "../lib/serializer/CTypeList.h"
# include "../lib/serializer/BinaryDeserializer.h"
# include "../lib/serializer/BinarySerializer.h"
2015-02-02 10:25:26 +02:00
# include "../lib/spells/CSpellHandler.h"
2010-12-20 23:22:53 +02:00
# include "../lib/CTownHandler.h"
2023-05-24 01:05:59 +02:00
# include "../lib/mapObjects/CGTownInstance.h"
# include "../lib/mapObjects/MiscObjects.h"
2023-06-02 20:47:37 +02:00
# include "../lib/mapObjects/ObjectTemplate.h"
2023-07-08 23:04:01 +02:00
# include "../lib/mapping/CMapHeader.h"
2023-06-21 12:46:09 +02:00
# include "../lib/pathfinder/CGPathNode.h"
2017-03-17 17:48:44 +02:00
# include "../lib/CStack.h"
2012-01-12 18:23:00 +03:00
# include "../lib/JsonNode.h"
2010-12-20 23:22:53 +02:00
# include "CMusicHandler.h"
2009-05-20 13:08:56 +03:00
# include "../lib/CondSh.h"
2017-07-20 06:08:49 +02:00
# include "../lib/NetPacksBase.h"
# include "../lib/NetPacks.h" //todo: remove
2009-10-10 08:47:59 +03:00
# include "../lib/VCMIDirs.h"
2011-12-17 21:59:59 +03:00
# include "../lib/CStopWatch.h"
2011-12-14 00:23:17 +03:00
# include "../lib/StartInfo.h"
2023-08-23 23:34:33 +02:00
# include "../lib/TextOperations.h"
2015-12-02 21:39:53 +02:00
# include "../lib/CPlayerState.h"
2011-12-14 00:23:17 +03:00
# include "../lib/GameConstants.h"
2013-04-07 14:52:07 +03:00
# include "gui/CGuiHandler.h"
2023-05-16 14:10:26 +02:00
# include "gui/WindowHandler.h"
2014-07-15 10:14:49 +03:00
# include "windows/InfoWindows.h"
2012-02-20 00:03:43 +03:00
# include "../lib/UnlockGuard.h"
2023-01-11 15:17:24 +02:00
# include "../lib/RoadHandler.h"
2023-01-09 01:17:37 +02:00
# include "../lib/TerrainHandler.h"
2023-08-20 22:09:17 +02:00
# include "../lib/CThreadHelper.h"
2018-01-05 19:21:07 +02:00
# include "CServerHandler.h"
// FIXME: only needed for CGameState::mutex
2023-06-23 17:02:48 +02:00
# include "../lib/gameState/CGameState.h"
2023-05-18 19:32:29 +02:00
# include "eventsSDL/NotificationHandler.h"
2023-02-01 20:42:06 +02:00
# include "adventureMap/CInGameConsole.h"
2009-12-28 06:08:24 +02:00
2012-04-06 18:02:15 +03:00
// The macro below is used to mark functions that are called by client when game state changes.
// They all assume that CPlayerInterface::pim mutex is locked.
# define EVENT_HANDLER_CALLED_BY_CLIENT
// The macro marks functions that are run on a new thread by client.
// They do not own any mutexes intiially.
# define THREAD_CREATED_BY_CLIENT
2013-06-23 14:25:48 +03:00
# define RETURN_IF_QUICK_COMBAT \
2016-10-30 11:00:25 +02:00
if ( isAutoFightOn & & ! battleInt ) \
2013-06-23 14:25:48 +03:00
return ;
# define BATTLE_EVENT_POSSIBLE_RETURN\
2016-10-30 11:00:25 +02:00
if ( LOCPLINT ! = this ) \
2013-06-23 14:25:48 +03:00
return ; \
RETURN_IF_QUICK_COMBAT
2012-02-14 21:04:45 +03:00
boost : : recursive_mutex * CPlayerInterface : : pim = new boost : : recursive_mutex ;
2008-01-30 16:19:35 +02:00
2009-04-21 01:57:07 +03:00
CPlayerInterface * LOCPLINT ;
2010-02-19 18:02:34 +02:00
2022-12-21 17:02:53 +02:00
std : : shared_ptr < BattleInterface > CPlayerInterface : : battleInt ;
2010-02-19 18:02:34 +02:00
2009-04-21 01:57:07 +03:00
enum EMoveState { STOP_MOVE , WAITING_MOVE , CONTINUE_MOVE , DURING_MOVE } ;
2016-11-27 18:13:40 +02:00
CondSh < EMoveState > stillMoveHero ( STOP_MOVE ) ; //used during hero movement
2008-07-31 00:27:15 +03:00
2023-04-16 23:24:11 +02:00
struct HeroObjectRetriever
2016-11-03 20:49:55 +02:00
{
const CGHeroInstance * operator ( ) ( const ConstTransitivePtr < CGHeroInstance > & h ) const
{
return h ;
}
const CGHeroInstance * operator ( ) ( const ConstTransitivePtr < CStackInstance > & s ) const
{
return nullptr ;
}
} ;
2023-02-15 12:08:32 +02:00
CPlayerInterface : : CPlayerInterface ( PlayerColor Player ) :
2023-04-17 01:02:31 +02:00
localState ( std : : make_unique < PlayerLocalState > ( * this ) )
2008-01-26 21:36:31 +02:00
{
2023-09-04 21:21:02 +02:00
logGlobal - > trace ( " \t Human player interface for player %s being constructed " , Player . toString ( ) ) ;
2015-03-08 16:17:24 +02:00
destinationTeleport = ObjectInstanceID ( ) ;
2015-12-04 00:54:25 +02:00
destinationTeleportPos = int3 ( - 1 ) ;
2009-08-18 11:22:56 +03:00
GH . defActionsDef = 0 ;
2009-05-19 21:23:04 +03:00
LOCPLINT = this ;
playerID = Player ;
human = true ;
2013-06-26 14:18:27 +03:00
battleInt = nullptr ;
2018-07-25 00:36:48 +02:00
castleInt = nullptr ;
2009-05-19 21:23:04 +03:00
makingTurn = false ;
showingDialog = new CondSh < bool > ( false ) ;
2017-07-16 11:58:05 +02:00
cingconsole = new CInGameConsole ( ) ;
2009-12-28 06:08:24 +02:00
firstCall = 1 ; //if loading will be overwritten in serialize
autosaveCount = 0 ;
2013-06-23 19:09:15 +03:00
isAutoFightOn = false ;
2014-06-21 09:49:27 +03:00
duringMovement = false ;
2016-01-22 22:30:24 +02:00
ignoreEvents = false ;
2023-02-13 21:10:53 +02:00
numOfMovedArts = 0 ;
2008-01-26 21:36:31 +02:00
}
2013-02-19 02:10:46 +03:00
2009-05-19 21:23:04 +03:00
CPlayerInterface : : ~ CPlayerInterface ( )
2008-05-23 22:50:11 +03:00
{
2023-09-04 21:21:02 +02:00
logGlobal - > trace ( " \t Human player interface for player %s being destructed " , playerID . toString ( ) ) ;
2009-05-19 21:23:04 +03:00
delete showingDialog ;
delete cingconsole ;
2016-10-30 11:00:25 +02:00
if ( LOCPLINT = = this )
2013-09-02 01:55:57 +03:00
LOCPLINT = nullptr ;
2009-02-04 15:40:54 +02:00
}
2022-12-07 21:50:45 +02:00
void CPlayerInterface : : initGameInterface ( std : : shared_ptr < Environment > ENV , std : : shared_ptr < CCallback > CB )
2008-01-26 21:36:31 +02:00
{
2013-06-22 17:47:20 +03:00
cb = CB ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
env = ENV ;
2022-09-21 13:43:00 +02:00
2022-11-21 16:16:23 +02:00
CCS - > musich - > loadTerrainMusicThemes ( ) ;
2022-09-21 13:43:00 +02:00
2018-01-05 19:21:07 +02:00
initializeHeroTownList ( ) ;
2010-12-23 02:33:48 +02:00
2015-02-16 22:45:16 +02:00
// always recreate advmap interface to avoid possible memory-corruption bugs
2023-05-08 14:18:34 +02:00
adventureInt . reset ( new AdventureMapInterface ( ) ) ;
2008-01-26 21:36:31 +02:00
}
2023-04-16 01:15:12 +02:00
void CPlayerInterface : : playerStartsTurn ( PlayerColor player )
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-08-23 01:47:55 +02:00
2023-08-14 02:06:26 +02:00
makingTurn = false ;
stillMoveHero . setn ( STOP_MOVE ) ;
2023-05-16 15:07:03 +02:00
2023-05-16 15:20:35 +02:00
if ( GH . windows ( ) . findWindows < AdventureMapInterface > ( ) . empty ( ) )
2023-04-16 01:15:12 +02:00
{
2023-04-17 00:09:25 +02:00
// after map load - remove all active windows and replace them with adventure map
2023-05-16 15:07:03 +02:00
GH . windows ( ) . clear ( ) ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( adventureInt ) ;
2023-04-16 01:15:12 +02:00
}
2023-04-17 00:09:25 +02:00
2023-08-23 21:37:44 +02:00
// close window from another player
2023-08-14 13:37:21 +02:00
if ( auto w = GH . windows ( ) . topWindow < CInfoWindow > ( ) )
2023-08-20 19:09:32 +02:00
if ( w - > ID = = QueryID : : NONE & & player ! = playerID )
2023-08-14 13:37:21 +02:00
w - > close ( ) ;
2023-04-17 00:09:25 +02:00
// remove all dialogs that do not expect query answer
2023-05-16 17:34:23 +02:00
while ( ! GH . windows ( ) . topWindow < AdventureMapInterface > ( ) & & ! GH . windows ( ) . topWindow < CInfoWindow > ( ) )
2023-05-16 15:20:35 +02:00
GH . windows ( ) . popWindows ( 1 ) ;
2023-04-17 00:09:25 +02:00
if ( player ! = playerID & & LOCPLINT = = this )
2023-04-16 01:15:12 +02:00
{
2023-04-17 00:09:25 +02:00
waitWhileDialog ( ) ;
2023-07-03 18:36:10 +02:00
bool isHuman = cb - > getStartInfo ( ) - > playerInfos . count ( player ) & & cb - > getStartInfo ( ) - > playerInfos . at ( player ) . isControlledByHuman ( ) ;
adventureInt - > onEnemyTurnStarted ( player , isHuman ) ;
2023-04-16 01:15:12 +02:00
}
2023-04-17 00:09:25 +02:00
}
2023-04-16 01:15:12 +02:00
2023-04-17 00:09:25 +02:00
void CPlayerInterface : : performAutosave ( )
{
int frequency = static_cast < int > ( settings [ " general " ] [ " saveFrequency " ] . Integer ( ) ) ;
2023-07-08 23:04:01 +02:00
if ( frequency > 0 & & cb - > getDate ( ) % frequency = = 0 )
2023-04-16 01:15:12 +02:00
{
2023-07-08 23:04:01 +02:00
bool usePrefix = settings [ " general " ] [ " useSavePrefix " ] . Bool ( ) ;
std : : string prefix = std : : string ( ) ;
2023-04-17 00:09:25 +02:00
2023-07-08 23:04:01 +02:00
if ( usePrefix )
2023-04-17 00:09:25 +02:00
{
2023-07-08 23:04:01 +02:00
prefix = settings [ " general " ] [ " savePrefix " ] . String ( ) ;
if ( prefix . empty ( ) )
{
2023-08-23 23:25:48 +02:00
std : : string name = cb - > getMapHeader ( ) - > name ;
2023-08-24 18:34:00 +02:00
int txtlen = TextOperations : : getUnicodeCharactersCount ( name ) ;
2023-08-23 23:25:48 +02:00
TextOperations : : trimRightUnicode ( name , std : : max ( 0 , txtlen - 15 ) ) ;
2023-08-23 22:23:05 +02:00
std : : string forbiddenChars ( " \\ /:? \" <>| " ) ;
std : : replace_if ( name . begin ( ) , name . end ( ) , [ & ] ( char c ) { return std : : string : : npos ! = forbiddenChars . find ( c ) ; } , ' _ ' ) ;
prefix = name + " _ " + cb - > getStartInfo ( ) - > startTimeIso8601 + " / " ;
2023-07-08 23:04:01 +02:00
}
}
autosaveCount + + ;
int autosaveCountLimit = settings [ " general " ] [ " autosaveCountLimit " ] . Integer ( ) ;
if ( autosaveCountLimit > 0 )
{
2023-08-23 23:25:48 +02:00
cb - > save ( " Saves/Autosave/ " + prefix + std : : to_string ( autosaveCount ) ) ;
2023-07-08 23:04:01 +02:00
autosaveCount % = autosaveCountLimit ;
}
else
{
2023-07-13 21:55:51 +02:00
std : : string stringifiedDate = std : : to_string ( cb - > getDate ( Date : : MONTH ) )
2023-07-08 23:04:01 +02:00
+ std : : to_string ( cb - > getDate ( Date : : WEEK ) )
+ std : : to_string ( cb - > getDate ( Date : : DAY_OF_WEEK ) ) ;
2023-08-23 23:25:48 +02:00
cb - > save ( " Saves/Autosave/ " + prefix + stringifiedDate ) ;
2023-04-17 00:09:25 +02:00
}
2023-04-16 01:15:12 +02:00
}
}
2023-08-28 02:42:05 +02:00
void CPlayerInterface : : yourTurn ( QueryID queryID )
2008-01-26 21:36:31 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2010-02-20 15:24:38 +02:00
{
LOCPLINT = this ;
GH . curInt = this ;
2009-04-14 15:47:09 +03:00
2020-03-22 15:57:13 +02:00
NotificationHandler : : notify ( " Your turn " ) ;
2023-07-13 22:07:42 +02:00
if ( settings [ " general " ] [ " startTurnAutosave " ] . Bool ( ) )
{
performAutosave ( ) ;
}
2010-07-24 14:46:04 +03:00
2018-01-05 19:21:07 +02:00
if ( CSH - > howManyPlayerInterfaces ( ) > 1 ) //hot seat message
2010-02-20 15:24:38 +02:00
{
2023-04-17 00:09:25 +02:00
adventureInt - > onHotseatWaitStarted ( playerID ) ;
2010-02-24 15:03:36 +02:00
makingTurn = true ;
2010-02-20 15:24:38 +02:00
std : : string msg = CGI - > generaltexth - > allTexts [ 13 ] ;
2010-08-03 14:36:52 +03:00
boost : : replace_first ( msg , " %s " , cb - > getStartInfo ( ) - > playerInfos . find ( playerID ) - > second . name ) ;
2018-04-07 13:34:11 +02:00
std : : vector < std : : shared_ptr < CComponent > > cmp ;
cmp . push_back ( std : : make_shared < CComponent > ( CComponent : : flag , playerID . getNum ( ) , 0 ) ) ;
2010-02-20 15:24:38 +02:00
showInfoDialog ( msg , cmp ) ;
}
else
2010-02-24 15:03:36 +02:00
{
makingTurn = true ;
2023-04-17 00:09:25 +02:00
adventureInt - > onPlayerTurnStarted ( playerID ) ;
2010-02-24 15:03:36 +02:00
}
2010-02-20 15:24:38 +02:00
}
2023-08-28 02:42:05 +02:00
acceptTurn ( queryID ) ;
2008-01-26 21:36:31 +02:00
}
2009-05-19 21:23:04 +03:00
2023-08-28 02:42:05 +02:00
void CPlayerInterface : : acceptTurn ( QueryID queryID )
2023-04-16 01:15:12 +02:00
{
if ( settings [ " session " ] [ " autoSkip " ] . Bool ( ) )
{
2023-05-16 17:34:23 +02:00
while ( auto iw = GH . windows ( ) . topWindow < CInfoWindow > ( ) )
2023-04-16 01:15:12 +02:00
iw - > close ( ) ;
}
if ( CSH - > howManyPlayerInterfaces ( ) > 1 )
{
waitWhileDialog ( ) ; // wait for player to accept turn in hot-seat mode
2023-04-17 00:09:25 +02:00
adventureInt - > onPlayerTurnStarted ( playerID ) ;
2023-04-16 01:15:12 +02:00
}
// warn player if he has no town
if ( cb - > howManyTowns ( ) = = 0 )
{
auto playerColor = * cb - > getPlayerID ( ) ;
std : : vector < Component > components ;
components . emplace_back ( Component : : EComponentType : : FLAG , playerColor . getNum ( ) , 0 , 0 ) ;
MetaString text ;
const auto & optDaysWithoutCastle = cb - > getPlayerState ( playerColor ) - > daysWithoutCastle ;
if ( optDaysWithoutCastle )
{
auto daysWithoutCastle = optDaysWithoutCastle . value ( ) ;
if ( daysWithoutCastle < 6 )
{
2023-06-18 11:18:25 +02:00
text . appendLocalString ( EMetaText : : ARRAY_TXT , 128 ) ; //%s, you only have %d days left to capture a town or you will be banished from this land.
text . replaceLocalString ( EMetaText : : COLOR , playerColor . getNum ( ) ) ;
text . replaceNumber ( 7 - daysWithoutCastle ) ;
2023-04-16 01:15:12 +02:00
}
else if ( daysWithoutCastle = = 6 )
{
2023-06-18 11:18:25 +02:00
text . appendLocalString ( EMetaText : : ARRAY_TXT , 129 ) ; //%s, this is your last day to capture a town or you will be banished from this land.
text . replaceLocalString ( EMetaText : : COLOR , playerColor . getNum ( ) ) ;
2023-04-16 01:15:12 +02:00
}
showInfoDialogAndWait ( components , text ) ;
}
else
logGlobal - > warn ( " Player has no towns, but daysWithoutCastle is not set " ) ;
}
2023-08-28 02:42:05 +02:00
2023-08-28 11:21:49 +02:00
cb - > selectionMade ( 0 , queryID ) ;
2023-04-16 01:15:12 +02:00
}
2022-01-25 13:19:48 +02:00
void CPlayerInterface : : heroMoved ( const TryMoveHero & details , bool verbose )
2008-01-26 21:36:31 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2012-02-17 00:19:07 +03:00
waitWhileDialog ( ) ;
2022-06-05 15:20:01 +02:00
if ( LOCPLINT ! = this )
2010-02-20 15:24:38 +02:00
return ;
2012-04-06 18:02:15 +03:00
2022-09-18 16:39:10 +02:00
//FIXME: read once and store
2017-06-03 07:25:10 +02:00
if ( settings [ " session " ] [ " spectate " ] . Bool ( ) & & settings [ " session " ] [ " spectate-ignore-hero " ] . Bool ( ) )
return ;
2013-04-20 14:34:01 +03:00
const CGHeroInstance * hero = cb - > getHero ( details . id ) ; //object representing this hero
2012-02-14 21:04:45 +03:00
2023-02-16 21:35:15 +02:00
if ( ! hero )
return ;
2011-07-17 21:49:05 +03:00
2023-03-15 00:30:58 +02:00
if ( details . result = = TryMoveHero : : EMBARK | | details . result = = TryMoveHero : : DISEMBARK )
{
if ( hero - > getRemovalSound ( ) & & hero - > tempOwner = = playerID )
2023-04-16 19:42:56 +02:00
CCS - > soundh - > playSound ( hero - > getRemovalSound ( ) . value ( ) ) ;
2023-03-15 00:30:58 +02:00
}
2023-04-16 00:48:49 +02:00
std : : unordered_set < int3 > changedTiles {
hero - > convertToVisitablePos ( details . start ) ,
hero - > convertToVisitablePos ( details . end )
} ;
adventureInt - > onMapTilesChanged ( changedTiles ) ;
2023-04-19 16:38:25 +02:00
adventureInt - > onHeroMovementStarted ( hero ) ;
2023-02-10 15:50:46 +02:00
2023-04-17 01:02:31 +02:00
bool directlyAttackingCreature = details . attackedFrom & & localState - > hasPath ( hero ) & & localState - > getPath ( hero ) . endPos ( ) = = * details . attackedFrom ;
2009-07-31 23:10:22 +03:00
2022-06-05 15:20:01 +02:00
if ( makingTurn & & hero - > tempOwner = = playerID ) //we are moving our hero - we may need to update assigned path
2008-08-02 18:08:03 +03:00
{
2022-06-05 15:20:01 +02:00
if ( details . result = = TryMoveHero : : TELEPORTATION )
2010-01-02 03:48:44 +02:00
{
2023-04-17 01:02:31 +02:00
if ( localState - > hasPath ( hero ) )
2014-03-07 16:21:09 +03:00
{
2023-04-17 01:02:31 +02:00
assert ( localState - > getPath ( hero ) . nodes . size ( ) > = 2 ) ;
auto nodesIt = localState - > getPath ( hero ) . nodes . end ( ) - 1 ;
2012-02-18 17:59:37 +03:00
2022-12-09 14:42:47 +02:00
if ( ( nodesIt ) - > coord = = hero - > convertToVisitablePos ( details . start )
& & ( nodesIt - 1 ) - > coord = = hero - > convertToVisitablePos ( details . end ) )
2012-02-18 17:59:37 +03:00
{
//path was between entrance and exit of teleport -> OK, erase node as usual
2023-04-17 01:02:31 +02:00
localState - > removeLastNode ( hero ) ;
2012-02-18 17:59:37 +03:00
}
else
{
//teleport was not along current path, it'll now be invalid (hero is somewhere else)
2023-04-17 01:02:31 +02:00
localState - > erasePath ( hero ) ;
2012-02-18 17:59:37 +03:00
}
}
2010-01-02 03:48:44 +02:00
}
2022-06-05 15:20:01 +02:00
if ( hero - > pos ! = details . end //hero didn't change tile but visit succeeded
2010-05-06 15:13:31 +03:00
| | directlyAttackingCreature ) // or creature was attacked from endangering tile.
2008-01-26 21:36:31 +02:00
{
2023-04-17 01:02:31 +02:00
localState - > erasePath ( hero ) ;
2008-01-26 21:36:31 +02:00
}
2023-04-17 01:02:31 +02:00
else if ( localState - > hasPath ( hero ) & & hero - > pos = = details . end ) //&& hero is moving
2009-07-31 23:10:22 +03:00
{
2022-06-05 15:20:01 +02:00
if ( details . start ! = details . end ) //so we don't touch path when revisiting with spacebar
2023-04-17 01:02:31 +02:00
localState - > removeLastNode ( hero ) ;
2009-07-31 23:10:22 +03:00
}
}
2009-07-03 22:57:14 +03:00
2023-03-15 00:30:58 +02:00
if ( details . stopMovement ( ) ) //hero failed to move
{
stillMoveHero . setn ( STOP_MOVE ) ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2023-03-15 00:30:58 +02:00
return ;
}
2009-05-19 21:23:04 +03:00
2023-02-18 17:37:09 +02:00
CGI - > mh - > waitForOngoingAnimations ( ) ;
2009-05-19 21:23:04 +03:00
2010-01-02 03:48:44 +02:00
//move finished
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2009-05-19 21:23:04 +03:00
2010-01-02 03:48:44 +02:00
//check if user cancelled movement
2008-08-17 12:11:16 +03:00
{
2023-05-18 22:31:05 +02:00
if ( GH . input ( ) . ignoreEventsUntilInput ( ) )
stillMoveHero . setn ( STOP_MOVE ) ;
2010-01-02 03:48:44 +02:00
}
2009-05-19 21:23:04 +03:00
2016-10-30 11:00:25 +02:00
if ( stillMoveHero . get ( ) = = WAITING_MOVE )
2010-01-02 03:48:44 +02:00
stillMoveHero . setn ( DURING_MOVE ) ;
2009-05-19 21:23:04 +03:00
2010-05-06 15:13:31 +03:00
// Hero attacked creature directly, set direction to face it.
if ( directlyAttackingCreature ) {
// Get direction to attacker.
2013-04-20 14:34:01 +03:00
int3 posOffset = * details . attackedFrom - details . end + int3 ( 2 , 1 , 0 ) ;
2010-10-31 00:53:41 +03:00
static const ui8 dirLookup [ 3 ] [ 3 ] = {
{ 1 , 2 , 3 } ,
{ 8 , 0 , 4 } ,
{ 7 , 6 , 5 }
2010-05-06 15:13:31 +03:00
} ;
// FIXME: Avoid const_cast, make moveDir mutable in some other way?
2013-04-20 14:34:01 +03:00
const_cast < CGHeroInstance * > ( hero ) - > moveDir = dirLookup [ posOffset . y ] [ posOffset . x ] ;
2010-05-06 15:13:31 +03:00
}
2010-01-02 03:48:44 +02:00
}
2023-02-16 21:35:15 +02:00
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : heroKilled ( const CGHeroInstance * hero )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-01-02 13:27:03 +02:00
LOG_TRACE_PARAMS ( logGlobal , " Hero %s killed handler for player %s " , hero - > getNameTranslated ( ) % playerID ) ;
2023-04-18 22:08:27 +02:00
2023-07-15 15:04:54 +02:00
// if hero is not in town garrison
if ( vstd : : contains ( localState - > getWanderingHeroes ( ) , hero ) )
localState - > removeWanderingHero ( hero ) ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2023-04-17 01:02:31 +02:00
localState - > erasePath ( hero ) ;
2010-01-02 03:48:44 +02:00
}
2012-06-22 14:40:16 +03:00
2017-09-13 02:35:58 +02:00
void CPlayerInterface : : heroVisit ( const CGHeroInstance * visitor , const CGObjectInstance * visitedObj , bool start )
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
2018-03-10 21:19:55 +02:00
if ( start & & visitedObj )
2017-09-13 02:35:58 +02:00
{
2018-03-10 21:19:55 +02:00
if ( visitedObj - > getVisitSound ( ) )
2023-04-16 19:42:56 +02:00
CCS - > soundh - > playSound ( visitedObj - > getVisitSound ( ) . value ( ) ) ;
2017-09-13 02:35:58 +02:00
}
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : heroCreated ( const CGHeroInstance * hero )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-17 14:17:15 +02:00
localState - > addWanderingHero ( hero ) ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2010-01-02 03:48:44 +02:00
}
void CPlayerInterface : : openTownWindow ( const CGTownInstance * town )
{
2018-07-25 00:36:48 +02:00
if ( castleInt )
2012-06-15 20:08:19 +03:00
castleInt - > close ( ) ;
2018-07-25 00:36:48 +02:00
castleInt = nullptr ;
auto newCastleInt = std : : make_shared < CCastleInterface > ( town ) ;
2017-09-13 02:35:58 +02:00
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( newCastleInt ) ;
2010-01-02 03:48:44 +02:00
}
2009-05-19 21:23:04 +03:00
2023-08-19 20:43:50 +02:00
void CPlayerInterface : : heroPrimarySkillChanged ( const CGHeroInstance * hero , PrimarySkill which , si64 val )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-08-19 20:43:50 +02:00
if ( which = = PrimarySkill : : EXPERIENCE )
2010-07-20 09:05:45 +03:00
{
2023-05-16 17:34:23 +02:00
for ( auto ctw : GH . windows ( ) . findWindows < CAltarWindow > ( ) )
2010-07-20 09:05:45 +03:00
ctw - > setExpToLevel ( ) ;
}
2023-04-20 21:03:28 +02:00
else
adventureInt - > onHeroChanged ( hero ) ;
2010-01-02 03:48:44 +02:00
}
2010-07-20 17:08:13 +03:00
void CPlayerInterface : : heroSecondarySkillChanged ( const CGHeroInstance * hero , int which , int val )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-16 17:34:23 +02:00
for ( auto cuw : GH . windows ( ) . findWindows < CUniversityWindow > ( ) )
cuw - > redraw ( ) ;
2010-07-20 17:08:13 +03:00
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : heroManaPointsChanged ( const CGHeroInstance * hero )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2016-10-30 11:00:25 +02:00
if ( makingTurn & & hero - > tempOwner = = playerID )
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2010-01-02 03:48:44 +02:00
}
void CPlayerInterface : : heroMovePointsChanged ( const CGHeroInstance * hero )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2016-10-30 11:00:25 +02:00
if ( makingTurn & & hero - > tempOwner = = playerID )
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2010-01-02 03:48:44 +02:00
}
2016-11-26 14:14:43 +02:00
void CPlayerInterface : : receivedResource ( )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-16 17:34:23 +02:00
for ( auto mw : GH . windows ( ) . findWindows < CMarketplaceWindow > ( ) )
2016-11-26 14:14:43 +02:00
mw - > resourceChanged ( ) ;
2012-02-16 20:10:58 +03:00
2023-05-16 14:10:26 +02:00
GH . windows ( ) . totalRedraw ( ) ;
2010-01-02 03:48:44 +02:00
}
2008-01-26 21:36:31 +02:00
2023-08-19 20:43:50 +02:00
void CPlayerInterface : : heroGotLevel ( const CGHeroInstance * hero , PrimarySkill pskill , std : : vector < SecondarySkill > & skills , QueryID queryID )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2010-01-02 03:48:44 +02:00
waitWhileDialog ( ) ;
2010-12-19 16:39:56 +02:00
CCS - > soundh - > playSound ( soundBase : : heroNewLevel ) ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CLevelWindow > ( hero , pskill , skills , [ = ] ( ui32 selection )
2018-07-25 00:36:48 +02:00
{
cb - > selectionMade ( selection , queryID ) ;
} ) ;
2010-01-02 03:48:44 +02:00
}
2018-07-25 00:36:48 +02:00
2013-05-27 13:53:28 +03:00
void CPlayerInterface : : commanderGotLevel ( const CCommanderInstance * commander , std : : vector < ui32 > skills , QueryID queryID )
2012-05-16 20:29:05 +03:00
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
waitWhileDialog ( ) ;
CCS - > soundh - > playSound ( soundBase : : heroNewLevel ) ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CStackWindow > ( commander , skills , [ = ] ( ui32 selection )
2014-06-26 22:07:33 +03:00
{
cb - > selectionMade ( selection , queryID ) ;
2018-07-25 00:36:48 +02:00
} ) ;
2012-05-16 20:29:05 +03:00
}
2014-06-26 22:07:33 +03:00
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : heroInGarrisonChange ( const CGTownInstance * town )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2009-05-19 21:23:04 +03:00
2023-04-17 14:17:15 +02:00
if ( town - > garrisonHero ) //wandering hero moved to the garrison
2008-01-26 21:36:31 +02:00
{
2023-04-19 16:38:25 +02:00
// This method also gets called on hero recruitment -> garrisoned hero is already in garrison
2023-07-13 15:20:45 +02:00
if ( town - > garrisonHero - > tempOwner = = playerID & & vstd : : contains ( localState - > getWanderingHeroes ( ) , town - > garrisonHero ) )
2023-04-17 14:17:15 +02:00
localState - > removeWanderingHero ( town - > garrisonHero ) ;
2010-01-02 03:48:44 +02:00
}
2008-01-26 21:36:31 +02:00
2023-04-17 14:17:15 +02:00
if ( town - > visitingHero ) //hero leaves garrison
2010-01-02 03:48:44 +02:00
{
2023-04-17 22:16:45 +02:00
// This method also gets called on hero recruitment -> wandering heroes already contains new hero
if ( town - > visitingHero - > tempOwner = = playerID & & ! vstd : : contains ( localState - > getWanderingHeroes ( ) , town - > visitingHero ) )
2023-04-17 14:17:15 +02:00
localState - > addWanderingHero ( town - > visitingHero ) ;
2010-01-02 03:48:44 +02:00
}
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( nullptr ) ;
adventureInt - > onTownChanged ( town ) ;
2009-04-11 04:32:50 +03:00
2018-07-25 00:36:48 +02:00
if ( castleInt )
2010-01-02 03:48:44 +02:00
{
2018-07-25 00:36:48 +02:00
castleInt - > garr - > selectSlot ( nullptr ) ;
2023-07-07 00:08:29 +02:00
castleInt - > garr - > setArmy ( town - > getUpperArmy ( ) , EGarrisonType : : UPPER ) ;
castleInt - > garr - > setArmy ( town - > visitingHero , EGarrisonType : : LOWER ) ;
2018-07-25 00:36:48 +02:00
castleInt - > garr - > recreateSlots ( ) ;
castleInt - > heroes - > update ( ) ;
2023-07-13 15:20:45 +02:00
// Perform totalRedraw to update hero list on adventure map
GH . windows ( ) . totalRedraw ( ) ;
2008-08-15 15:11:42 +03:00
}
2023-05-16 15:07:03 +02:00
2023-05-16 15:20:35 +02:00
for ( auto ki : GH . windows ( ) . findWindows < CKingdomInterface > ( ) )
2011-07-22 19:22:22 +03:00
{
2023-05-16 15:07:03 +02:00
ki - > townChanged ( town ) ;
ki - > updateGarrisons ( ) ;
ki - > redraw ( ) ;
2011-07-22 19:22:22 +03:00
}
2010-01-02 03:48:44 +02:00
}
2023-05-16 15:07:03 +02:00
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : heroVisitsTown ( const CGHeroInstance * hero , const CGTownInstance * town )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2016-10-30 11:00:25 +02:00
if ( hero - > tempOwner ! = playerID )
2010-01-02 03:48:44 +02:00
return ;
2011-05-25 17:48:49 +03:00
waitWhileDialog ( ) ;
2010-01-02 03:48:44 +02:00
openTownWindow ( town ) ;
}
2018-03-10 23:19:36 +02:00
void CPlayerInterface : : garrisonsChanged ( ObjectInstanceID id1 , ObjectInstanceID id2 )
{
std : : vector < const CGObjectInstance * > instances ;
if ( auto obj = cb - > getObj ( id1 ) )
instances . push_back ( obj ) ;
if ( id2 ! = ObjectInstanceID ( ) & & id2 ! = id1 )
{
if ( auto obj = cb - > getObj ( id2 ) )
instances . push_back ( obj ) ;
}
garrisonsChanged ( instances ) ;
}
2012-12-11 15:12:46 +03:00
void CPlayerInterface : : garrisonsChanged ( std : : vector < const CGObjectInstance * > objs )
2010-01-02 03:48:44 +02:00
{
boost : : unique_lock < boost : : recursive_mutex > un ( * pim ) ;
2016-10-30 11:00:25 +02:00
for ( auto object : objs )
2023-04-20 21:03:28 +02:00
{
auto * hero = dynamic_cast < const CGHeroInstance * > ( object ) ;
auto * town = dynamic_cast < const CGTownInstance * > ( object ) ;
if ( hero )
2023-07-16 10:18:43 +02:00
{
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2023-07-16 10:18:43 +02:00
if ( hero - > inTownGarrison )
{
adventureInt - > onTownChanged ( hero - > visitedTown ) ;
}
}
2023-04-20 21:03:28 +02:00
if ( town )
adventureInt - > onTownChanged ( town ) ;
}
2009-04-14 15:47:09 +03:00
2023-07-07 00:08:29 +02:00
for ( auto cgh : GH . windows ( ) . findWindows < IGarrisonHolder > ( ) )
2023-05-16 15:07:03 +02:00
cgh - > updateGarrisons ( ) ;
2012-06-13 16:04:06 +03:00
2023-05-16 15:20:35 +02:00
for ( auto cmw : GH . windows ( ) . findWindows < CTradeWindow > ( ) )
2023-05-16 15:07:03 +02:00
{
if ( vstd : : contains ( objs , cmw - > hero ) )
cmw - > garrisonChanged ( ) ;
2008-01-31 23:35:30 +02:00
}
2008-05-18 20:33:39 +03:00
2023-05-16 14:10:26 +02:00
GH . windows ( ) . totalRedraw ( ) ;
2008-05-18 20:33:39 +03:00
}
2010-01-02 03:48:44 +02:00
2013-02-11 22:11:34 +03:00
void CPlayerInterface : : buildChanged ( const CGTownInstance * town , BuildingID buildingID , int what ) //what: 1 - built, 2 - demolished
2008-05-18 20:33:39 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-07-13 13:35:24 +02:00
adventureInt - > onTownChanged ( town ) ;
2016-10-30 11:00:25 +02:00
if ( castleInt )
2010-01-02 03:48:44 +02:00
{
2023-07-13 13:35:24 +02:00
castleInt - > townlist - > updateElement ( town ) ;
2016-08-24 05:19:25 +02:00
2016-10-30 11:00:25 +02:00
if ( castleInt - > town = = town )
2016-08-24 05:19:25 +02:00
{
switch ( what )
{
case 1 :
CCS - > soundh - > playSound ( soundBase : : newBuilding ) ;
castleInt - > addBuilding ( buildingID ) ;
break ;
case 2 :
castleInt - > removeBuilding ( buildingID ) ;
break ;
}
}
2023-07-13 13:35:24 +02:00
// Perform totalRedraw in order to force redraw of updated town list icon from adventure map
GH . windows ( ) . totalRedraw ( ) ;
2010-01-02 03:48:44 +02:00
}
2008-05-18 20:33:39 +03:00
}
2010-01-02 03:48:44 +02:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleStartBefore ( const BattleID & battleID , const CCreatureSet * army1 , const CCreatureSet * army2 , int3 tile , const CGHeroInstance * hero1 , const CGHeroInstance * hero2 )
2013-09-14 22:09:35 +03:00
{
2023-08-22 21:15:32 +02:00
// when battle starts, game will send battleStart pack *before* movement confirmation
// and since network thread wait for battle intro to play, movement confirmation will only happen after intro
// leading to several bugs, such as blocked input during intro
stillMoveHero . setn ( STOP_MOVE ) ;
2013-09-14 22:09:35 +03:00
//Don't wait for dialogs when we are non-active hot-seat player
2016-10-30 11:00:25 +02:00
if ( LOCPLINT = = this )
2013-09-14 22:09:35 +03:00
waitForAllDialogs ( ) ;
}
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleStart ( const BattleID & battleID , const CCreatureSet * army1 , const CCreatureSet * army2 , int3 tile , const CGHeroInstance * hero1 , const CGHeroInstance * hero2 , bool side , bool replayAllowed )
2008-05-18 20:33:39 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-07-27 17:40:47 +02:00
bool useQuickCombat = settings [ " adventure " ] [ " quickCombat " ] . Bool ( ) ;
bool forceQuickCombat = settings [ " adventure " ] [ " forceQuickCombat " ] . Bool ( ) ;
if ( ( replayAllowed & & useQuickCombat ) | | forceQuickCombat )
2013-06-23 14:25:48 +03:00
{
2017-01-17 13:17:37 +02:00
autofightingAI = CDynLibHandler : : getNewBattleAI ( settings [ " server " ] [ " friendlyAI " ] . String ( ) ) ;
2023-08-19 17:23:55 +02:00
AutocombatPreferences autocombatPreferences = AutocombatPreferences ( ) ;
autocombatPreferences . enableSpellsUsage = settings [ " battle " ] [ " enableAutocombatSpells " ] . Bool ( ) ;
autofightingAI - > initBattleInterface ( env , cb , autocombatPreferences ) ;
2023-08-30 21:07:02 +02:00
autofightingAI - > battleStart ( battleID , army1 , army2 , tile , hero1 , hero2 , side , false ) ;
2013-06-23 14:25:48 +03:00
isAutoFightOn = true ;
cb - > registerBattleInterface ( autofightingAI ) ;
2010-02-19 18:02:34 +02:00
}
2014-03-07 16:21:09 +03:00
2013-09-02 01:55:57 +03:00
//Don't wait for dialogs when we are non-active hot-seat player
2016-10-30 11:00:25 +02:00
if ( LOCPLINT = = this )
2013-09-02 01:55:57 +03:00
waitForAllDialogs ( ) ;
2014-03-07 16:21:09 +03:00
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2008-05-18 20:33:39 +03:00
}
2010-01-02 03:48:44 +02:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleUnitsChanged ( const BattleID & battleID , const std : : vector < UnitChanges > & units )
2008-05-18 20:33:39 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-02-19 18:02:34 +02:00
2017-07-20 06:08:49 +02:00
for ( auto & info : units )
2008-05-18 20:33:39 +03:00
{
2017-07-20 06:08:49 +02:00
switch ( info . operation )
2008-05-18 20:33:39 +03:00
{
2017-07-20 06:08:49 +02:00
case UnitChanges : : EOperation : : RESET_STATE :
{
2023-08-30 21:07:02 +02:00
const CStack * stack = cb - > getBattle ( battleID ) - > battleGetStackByID ( info . id ) ;
2010-05-07 15:29:41 +03:00
2022-11-20 19:11:34 +02:00
if ( ! stack )
2017-07-20 06:08:49 +02:00
{
logGlobal - > error ( " Invalid unit ID %d " , info . id ) ;
continue ;
}
2022-11-20 19:11:34 +02:00
battleInt - > stackReset ( stack ) ;
2017-07-20 06:08:49 +02:00
}
break ;
case UnitChanges : : EOperation : : REMOVE :
battleInt - > stackRemoved ( info . id ) ;
break ;
case UnitChanges : : EOperation : : ADD :
{
2023-08-30 21:07:02 +02:00
const CStack * unit = cb - > getBattle ( battleID ) - > battleGetStackByID ( info . id ) ;
2017-07-20 06:08:49 +02:00
if ( ! unit )
{
logGlobal - > error ( " Invalid unit ID %d " , info . id ) ;
continue ;
}
2022-11-20 19:11:34 +02:00
battleInt - > stackAdded ( unit ) ;
2017-07-20 06:08:49 +02:00
}
break ;
default :
logGlobal - > error ( " Unknown unit operation %d " , ( int ) info . operation ) ;
break ;
2017-07-04 13:24:46 +02:00
}
2011-06-21 15:45:57 +03:00
}
2010-01-02 03:48:44 +02:00
}
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleObstaclesChanged ( const BattleID & battleID , const std : : vector < ObstacleChanges > & obstacles )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2012-04-06 18:02:15 +03:00
2022-12-01 22:06:42 +02:00
std : : vector < std : : shared_ptr < const CObstacleInstance > > newObstacles ;
2023-03-26 18:57:21 +02:00
std : : vector < ObstacleChanges > removedObstacles ;
2010-01-02 03:48:44 +02:00
2017-07-20 06:08:49 +02:00
for ( auto & change : obstacles )
{
if ( change . operation = = BattleChanges : : EOperation : : ADD )
{
2023-08-30 21:07:02 +02:00
auto instance = cb - > getBattle ( battleID ) - > battleGetObstacleByID ( change . id ) ;
2017-07-20 06:08:49 +02:00
if ( instance )
2022-12-01 22:06:42 +02:00
newObstacles . push_back ( instance ) ;
2017-07-20 06:08:49 +02:00
else
logNetwork - > error ( " Invalid obstacle instance %d " , change . id ) ;
}
2023-03-26 18:57:21 +02:00
if ( change . operation = = BattleChanges : : EOperation : : REMOVE )
removedObstacles . push_back ( change ) ; //Obstacles are already removed, so, show animation based on json struct
2017-07-20 06:08:49 +02:00
}
2010-02-19 18:02:34 +02:00
2022-12-01 22:06:42 +02:00
if ( ! newObstacles . empty ( ) )
battleInt - > obstaclePlaced ( newObstacles ) ;
2023-03-26 18:57:21 +02:00
if ( ! removedObstacles . empty ( ) )
battleInt - > obstacleRemoved ( removedObstacles ) ;
2022-12-01 22:06:42 +02:00
battleInt - > fieldController - > redrawBackgroundWithHexes ( ) ;
2008-05-18 20:33:39 +03:00
}
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleCatapultAttacked ( const BattleID & battleID , const CatapultAttack & ca )
2008-05-18 20:33:39 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-02-19 18:02:34 +02:00
2010-01-02 03:48:44 +02:00
battleInt - > stackIsCatapulting ( ca ) ;
2008-05-18 20:33:39 +03:00
}
2010-01-02 03:48:44 +02:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleNewRound ( const BattleID & battleID ) //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
2008-05-31 23:37:54 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-02-19 18:02:34 +02:00
2023-08-30 21:07:02 +02:00
battleInt - > newRound ( ) ;
2008-05-31 23:37:54 +03:00
}
2010-01-02 03:48:44 +02:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : actionStarted ( const BattleID & battleID , const BattleAction & action )
2008-05-18 20:33:39 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-02-19 18:02:34 +02:00
2023-08-17 18:18:14 +02:00
battleInt - > startAction ( action ) ;
2008-05-18 20:33:39 +03:00
}
2010-01-02 03:48:44 +02:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : actionFinished ( const BattleID & battleID , const BattleAction & action )
2008-05-18 20:33:39 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-02-19 18:02:34 +02:00
2023-08-17 18:18:14 +02:00
battleInt - > endAction ( action ) ;
2008-05-27 16:16:35 +03:00
}
2008-08-13 03:44:31 +03:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : activeStack ( const BattleID & battleID , const CStack * stack ) //called when it's turn of that stack
2008-08-13 03:44:31 +03:00
{
2023-07-18 18:55:59 +02:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2017-08-11 13:38:10 +02:00
logGlobal - > trace ( " Awaiting command for %s " , stack - > nodeName ( ) ) ;
2023-07-18 15:29:02 +02:00
2023-08-30 21:07:02 +02:00
assert ( ! cb - > getBattle ( battleID ) - > battleIsFinished ( ) ) ;
if ( cb - > getBattle ( battleID ) - > battleIsFinished ( ) )
2023-07-18 15:29:02 +02:00
{
logGlobal - > error ( " Received CPlayerInterface::activeStack after battle is finished! " ) ;
2023-07-18 18:55:59 +02:00
2023-08-30 21:07:02 +02:00
cb - > battleMakeUnitAction ( battleID , BattleAction : : makeDefend ( stack ) ) ;
2023-07-18 18:55:59 +02:00
return ;
2023-07-18 15:29:02 +02:00
}
2016-10-30 11:00:25 +02:00
if ( autofightingAI )
2013-06-23 14:25:48 +03:00
{
2016-10-30 11:00:25 +02:00
if ( isAutoFightOn )
2013-06-23 14:25:48 +03:00
{
2023-07-19 11:09:03 +02:00
//FIXME: we want client rendering to proceed while AI is making actions
// so unlock mutex while AI is busy since this might take quite a while, especially if hero has many spells
auto unlockPim = vstd : : makeUnlockGuard ( * pim ) ;
2023-08-30 21:07:02 +02:00
autofightingAI - > activeStack ( battleID , stack ) ;
2023-07-18 19:06:39 +02:00
return ;
2013-06-23 14:25:48 +03:00
}
cb - > unregisterBattleInterface ( autofightingAI ) ;
2013-06-23 22:35:54 +03:00
autofightingAI . reset ( ) ;
2013-06-23 14:25:48 +03:00
}
2022-12-21 17:02:53 +02:00
assert ( battleInt ) ;
if ( ! battleInt )
2022-05-15 11:59:56 +02:00
{
2023-07-18 18:55:59 +02:00
// probably battle is finished already
2023-08-30 21:07:02 +02:00
cb - > battleMakeUnitAction ( battleID , BattleAction : : makeDefend ( stack ) ) ;
2014-01-14 17:34:08 +03:00
}
2010-01-02 03:48:44 +02:00
{
boost : : unique_lock < boost : : recursive_mutex > un ( * pim ) ;
2022-12-21 17:02:53 +02:00
battleInt - > stackActivated ( stack ) ;
2011-07-06 20:00:45 +03:00
//Regeneration & mana drain go there
2010-01-02 03:48:44 +02:00
}
}
2009-05-19 21:23:04 +03:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleEnd ( const BattleID & battleID , const BattleResult * br , QueryID queryID )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2022-11-01 17:16:45 +02:00
if ( isAutoFightOn | | autofightingAI )
2013-06-23 14:25:48 +03:00
{
isAutoFightOn = false ;
cb - > unregisterBattleInterface ( autofightingAI ) ;
2013-06-23 22:35:54 +03:00
autofightingAI . reset ( ) ;
2013-06-23 14:25:48 +03:00
2022-11-01 17:16:45 +02:00
if ( ! battleInt )
2013-06-23 19:09:15 +03:00
{
2023-08-19 23:22:31 +02:00
bool allowManualReplay = queryID ! = QueryID : : NONE ;
2023-07-27 17:40:47 +02:00
2023-04-08 18:56:05 +02:00
auto wnd = std : : make_shared < BattleResultWindow > ( * br , * this , allowManualReplay ) ;
2023-07-27 17:40:47 +02:00
if ( allowManualReplay )
2023-04-06 17:34:07 +02:00
{
2023-07-27 17:40:47 +02:00
wnd - > resultCallback = [ = ] ( ui32 selection )
{
cb - > selectionMade ( selection , queryID ) ;
} ;
}
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( wnd ) ;
2013-10-13 20:37:59 +03:00
// #1490 - during AI turn when quick combat is on, we need to display the message and wait for user to close it.
// Otherwise NewTurn causes freeze.
waitWhileDialog ( ) ;
2013-06-23 19:09:15 +03:00
return ;
}
2010-02-19 18:02:34 +02:00
}
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2023-04-06 17:34:07 +02:00
battleInt - > battleFinished ( * br , queryID ) ;
2009-05-19 21:23:04 +03:00
}
2010-01-02 03:48:44 +02:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleLogMessage ( const BattleID & battleID , const std : : vector < MetaString > & lines )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
BATTLE_EVENT_POSSIBLE_RETURN ;
battleInt - > displayBattleLog ( lines ) ;
}
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleStackMoved ( const BattleID & battleID , const CStack * stack , std : : vector < BattleHex > dest , int distance , bool teleport )
2008-08-13 03:44:31 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-02-19 18:02:34 +02:00
2022-12-18 18:26:43 +02:00
battleInt - > stackMoved ( stack , dest , distance , teleport ) ;
2010-01-02 03:48:44 +02:00
}
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleSpellCast ( const BattleID & battleID , const BattleSpellCast * sc )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-02-19 18:02:34 +02:00
2010-01-02 03:48:44 +02:00
battleInt - > spellCast ( sc ) ;
}
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleStacksEffectsSet ( const BattleID & battleID , const SetStackEffect & sse )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-02-19 18:02:34 +02:00
2010-01-02 03:48:44 +02:00
battleInt - > battleStacksEffectsSet ( sse ) ;
}
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleTriggerEffect ( const BattleID & battleID , const BattleTriggerEffect & bte )
2011-10-08 16:02:58 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-08 17:53:28 +02:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2013-06-23 14:25:48 +03:00
RETURN_IF_QUICK_COMBAT ;
2022-11-24 16:30:04 +02:00
battleInt - > effectsController - > battleTriggerEffect ( bte ) ;
2023-08-27 18:58:07 +02:00
if ( bte . effect = = vstd : : to_underlying ( BonusType : : MANA_DRAIN ) )
{
const CGHeroInstance * manaDrainedHero = LOCPLINT - > cb - > getHero ( ObjectInstanceID ( bte . additionalInfo ) ) ;
battleInt - > windowObject - > heroManaPointsChanged ( manaDrainedHero ) ;
}
2011-10-08 16:02:58 +03:00
}
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleStacksAttacked ( const BattleID & battleID , const std : : vector < BattleStackAttacked > & bsa , bool ranged )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2015-01-13 21:57:41 +02:00
2011-12-22 16:05:19 +03:00
std : : vector < StackAttackedInfo > arg ;
2017-07-20 06:08:49 +02:00
for ( auto & elem : bsa )
2009-02-14 15:51:21 +02:00
{
2023-08-30 21:07:02 +02:00
const CStack * defender = cb - > getBattle ( battleID ) - > battleGetStackByID ( elem . stackAttacked , false ) ;
const CStack * attacker = cb - > getBattle ( battleID ) - > battleGetStackByID ( elem . attackerID , false ) ;
2013-08-09 20:37:41 +03:00
2022-12-09 13:10:35 +02:00
assert ( defender ) ;
2013-08-09 20:37:41 +03:00
2022-12-09 13:10:35 +02:00
StackAttackedInfo info ;
info . defender = defender ;
info . attacker = attacker ;
info . damageDealt = elem . damageAmount ;
info . amountKilled = elem . killedAmount ;
info . spellEffect = SpellID : : NONE ;
info . indirectAttack = ranged ;
info . killed = elem . killed ( ) ;
info . rebirth = elem . willRebirth ( ) ;
info . cloneKilled = elem . cloneKilled ( ) ;
2022-12-25 17:42:54 +02:00
info . fireShield = elem . fireShield ( ) ;
2022-12-09 13:10:35 +02:00
if ( elem . isSpell ( ) )
info . spellEffect = elem . spellID ;
2013-08-09 20:37:41 +03:00
2022-12-09 13:10:35 +02:00
arg . push_back ( info ) ;
2009-02-04 05:55:12 +02:00
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
battleInt - > stacksAreAttacked ( arg ) ;
2010-01-02 03:48:44 +02:00
}
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleAttack ( const BattleID & battleID , const BattleAttack * ba )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-02-19 18:02:34 +02:00
2022-12-11 12:29:11 +02:00
StackAttackInfo info ;
2023-08-30 21:07:02 +02:00
info . attacker = cb - > getBattle ( battleID ) - > battleGetStackByID ( ba - > stackAttacking ) ;
2022-12-11 12:29:11 +02:00
info . defender = nullptr ;
info . indirectAttack = ba - > shot ( ) ;
info . lucky = ba - > lucky ( ) ;
info . unlucky = ba - > unlucky ( ) ;
info . deathBlow = ba - > deathBlow ( ) ;
info . lifeDrain = ba - > lifeDrain ( ) ;
info . tile = ba - > tile ;
info . spellEffect = SpellID : : NONE ;
2017-07-20 06:08:49 +02:00
2022-12-11 12:29:11 +02:00
if ( ba - > spellLike ( ) )
info . spellEffect = ba - > spellID ;
2009-02-14 16:37:13 +02:00
2022-12-11 12:29:11 +02:00
for ( auto & elem : ba - > bsa )
2011-07-06 20:00:45 +03:00
{
2022-12-11 12:29:11 +02:00
if ( ! elem . isSecondary ( ) )
2010-01-02 03:48:44 +02:00
{
2022-12-11 12:29:11 +02:00
assert ( info . defender = = nullptr ) ;
2023-08-30 21:07:02 +02:00
info . defender = cb - > getBattle ( battleID ) - > battleGetStackByID ( elem . stackAttacked ) ;
2010-01-02 03:48:44 +02:00
}
2022-12-11 12:29:11 +02:00
else
2022-05-14 16:38:06 +02:00
{
2023-08-30 21:07:02 +02:00
info . secondaryDefender . push_back ( cb - > getBattle ( battleID ) - > battleGetStackByID ( elem . stackAttacked ) ) ;
2022-05-14 16:38:06 +02:00
}
2009-02-14 16:37:13 +02:00
}
2022-12-11 12:29:11 +02:00
assert ( info . defender ! = nullptr ) ;
assert ( info . attacker ! = nullptr ) ;
2015-01-13 21:57:41 +02:00
2022-12-11 12:29:11 +02:00
battleInt - > stackAttacking ( info ) ;
2008-08-13 03:44:31 +03:00
}
2012-05-05 00:16:39 +03:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleGateStateChanged ( const BattleID & battleID , const EGateState state )
2016-02-10 06:10:32 +02:00
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
BATTLE_EVENT_POSSIBLE_RETURN ;
2016-02-13 16:40:31 +02:00
battleInt - > gateStateChanged ( state ) ;
2016-02-10 06:10:32 +02:00
}
2023-08-28 17:59:12 +02:00
void CPlayerInterface : : yourTacticPhase ( const BattleID & battleID , int distance )
2011-08-25 18:24:37 +03:00
{
2023-07-18 20:43:53 +02:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-04 21:54:30 +02:00
}
2023-03-06 01:30:21 +02:00
void CPlayerInterface : : showInfoDialog ( EInfoWindowMode type , const std : : string & text , const std : : vector < Component > & components , int soundID )
2008-08-13 03:44:31 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-03-07 02:29:04 +02:00
bool autoTryHover = settings [ " gameTweaks " ] [ " infoBarPick " ] . Bool ( ) & & type = = EInfoWindowMode : : AUTO ;
auto timer = type = = EInfoWindowMode : : INFO ? 3000 : 4500 ; //Implement long info windows like in HD mod
if ( autoTryHover | | type = = EInfoWindowMode : : INFO )
{
2023-03-12 13:59:44 +02:00
waitWhileDialog ( ) ; //Fix for mantis #98
2023-04-20 21:03:28 +02:00
adventureInt - > showInfoBoxMessage ( components , text , timer ) ;
2023-03-09 23:18:35 +02:00
2023-05-16 15:07:03 +02:00
if ( makingTurn & & GH . windows ( ) . count ( ) > 0 & & LOCPLINT = = this )
2023-03-09 23:18:35 +02:00
CCS - > soundh - > playSound ( static_cast < soundBase : : soundID > ( soundID ) ) ;
return ;
2023-03-06 01:30:21 +02:00
}
2010-01-02 03:48:44 +02:00
2023-02-02 16:15:39 +02:00
if ( settings [ " session " ] [ " autoSkip " ] . Bool ( ) & & ! GH . isKeyboardShiftDown ( ) )
2012-09-28 18:46:09 +03:00
{
return ;
}
2023-03-12 16:18:21 +02:00
std : : vector < Component > vect = components ; //I do not know currently how to avoid copy here
do
{
std : : vector < Component > sender = { vect . begin ( ) , vect . begin ( ) + std : : min ( vect . size ( ) , static_cast < size_t > ( 8 ) ) } ;
std : : vector < std : : shared_ptr < CComponent > > intComps ;
for ( auto & component : sender )
intComps . push_back ( std : : make_shared < CComponent > ( component ) ) ;
showInfoDialog ( text , intComps , soundID ) ;
vect . erase ( vect . begin ( ) , vect . begin ( ) + std : : min ( vect . size ( ) , static_cast < size_t > ( 8 ) ) ) ;
}
while ( ! vect . empty ( ) ) ;
2010-01-02 03:48:44 +02:00
}
2018-04-07 13:34:11 +02:00
void CPlayerInterface : : showInfoDialog ( const std : : string & text , std : : shared_ptr < CComponent > component )
2012-12-11 15:12:46 +03:00
{
2018-04-07 13:34:11 +02:00
std : : vector < std : : shared_ptr < CComponent > > intComps ;
2012-12-11 15:12:46 +03:00
intComps . push_back ( component ) ;
2018-04-07 13:34:11 +02:00
showInfoDialog ( text , intComps , soundBase : : sound_todo ) ;
2012-12-11 15:12:46 +03:00
}
2018-04-07 13:34:11 +02:00
void CPlayerInterface : : showInfoDialog ( const std : : string & text , const std : : vector < std : : shared_ptr < CComponent > > & components , int soundID )
2008-08-17 12:11:16 +03:00
{
2013-09-14 22:09:35 +03:00
LOG_TRACE_PARAMS ( logGlobal , " player=%s, text=%s, is LOCPLINT=%d " , playerID % text % ( this = = LOCPLINT ) ) ;
2010-01-02 03:48:44 +02:00
waitWhileDialog ( ) ;
2012-02-16 20:10:58 +03:00
2023-02-02 16:15:39 +02:00
if ( settings [ " session " ] [ " autoSkip " ] . Bool ( ) & & ! GH . isKeyboardShiftDown ( ) )
2012-09-28 18:46:09 +03:00
{
return ;
}
2018-07-25 00:36:48 +02:00
std : : shared_ptr < CInfoWindow > temp = CInfoWindow : : create ( text , playerID , components ) ;
2018-04-07 13:34:11 +02:00
2023-05-16 15:07:03 +02:00
if ( makingTurn & & GH . windows ( ) . count ( ) > 0 & & LOCPLINT = = this )
2008-08-17 12:11:16 +03:00
{
2010-12-19 16:39:56 +02:00
CCS - > soundh - > playSound ( static_cast < soundBase : : soundID > ( soundID ) ) ;
2010-01-02 03:48:44 +02:00
showingDialog - > set ( true ) ;
2012-10-05 22:57:28 +03:00
stopMovement ( ) ; // interrupt movement to show dialog
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( temp ) ;
2008-08-17 12:11:16 +03:00
}
2010-01-02 03:48:44 +02:00
else
2009-05-19 21:23:04 +03:00
{
2010-01-02 03:48:44 +02:00
dialogs . push_back ( temp ) ;
}
}
2013-11-30 12:43:31 +03:00
void CPlayerInterface : : showInfoDialogAndWait ( std : : vector < Component > & components , const MetaString & text )
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
2018-04-07 13:34:11 +02:00
2023-06-18 11:18:25 +02:00
std : : string str = text . toString ( ) ;
2013-11-30 12:43:31 +03:00
2023-03-06 01:30:21 +02:00
showInfoDialog ( EInfoWindowMode : : MODAL , str , components , 0 ) ;
2013-11-30 12:43:31 +03:00
waitWhileDialog ( ) ;
}
2018-04-07 13:34:11 +02:00
void CPlayerInterface : : showYesNoDialog ( const std : : string & text , CFunctionList < void ( ) > onYes , CFunctionList < void ( ) > onNo , const std : : vector < std : : shared_ptr < CComponent > > & components )
2010-01-02 03:48:44 +02:00
{
boost : : unique_lock < boost : : recursive_mutex > un ( * pim ) ;
2010-05-14 05:18:37 +03:00
stopMovement ( ) ;
2010-01-02 03:48:44 +02:00
LOCPLINT - > showingDialog - > setn ( true ) ;
2018-04-07 13:34:11 +02:00
CInfoWindow : : showYesNoDialog ( text , components , onYes , onNo , playerID ) ;
2010-01-02 03:48:44 +02:00
}
2016-11-27 21:14:41 +02:00
void CPlayerInterface : : showBlockingDialog ( const std : : string & text , const std : : vector < Component > & components , QueryID askID , const int soundID , bool selection , bool cancel )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2010-01-02 03:48:44 +02:00
waitWhileDialog ( ) ;
2012-02-16 20:10:58 +03:00
2010-05-14 05:18:37 +03:00
stopMovement ( ) ;
2010-12-19 16:39:56 +02:00
CCS - > soundh - > playSound ( static_cast < soundBase : : soundID > ( soundID ) ) ;
2010-01-02 03:48:44 +02:00
2016-10-30 11:00:25 +02:00
if ( ! selection & & cancel ) //simple yes/no dialog
2010-01-02 03:48:44 +02:00
{
2018-04-07 13:34:11 +02:00
std : : vector < std : : shared_ptr < CComponent > > intComps ;
2016-10-30 11:00:25 +02:00
for ( auto & component : components )
2018-04-07 13:34:11 +02:00
intComps . push_back ( std : : make_shared < CComponent > ( component ) ) ; //will be deleted by close in window
2009-04-14 15:47:09 +03:00
2018-04-07 13:34:11 +02:00
showYesNoDialog ( text , [ = ] ( ) { cb - > selectionMade ( 1 , askID ) ; } , [ = ] ( ) { cb - > selectionMade ( 0 , askID ) ; } , intComps ) ;
2009-05-19 21:23:04 +03:00
}
2016-10-30 11:00:25 +02:00
else if ( selection )
2009-05-19 21:23:04 +03:00
{
2018-04-07 13:34:11 +02:00
std : : vector < std : : shared_ptr < CSelectableComponent > > intComps ;
2016-10-30 11:00:25 +02:00
for ( auto & component : components )
2018-04-07 13:34:11 +02:00
intComps . push_back ( std : : make_shared < CSelectableComponent > ( component ) ) ; //will be deleted by CSelWindow::close
2010-01-02 03:48:44 +02:00
2023-08-23 14:07:50 +02:00
std : : vector < std : : pair < AnimationPath , CFunctionList < void ( ) > > > pom ;
pom . push_back ( { AnimationPath : : builtin ( " IOKAY.DEF " ) , 0 } ) ;
2016-10-30 11:00:25 +02:00
if ( cancel )
2009-05-19 21:23:04 +03:00
{
2023-08-23 14:07:50 +02:00
pom . push_back ( { AnimationPath : : builtin ( " ICANCEL.DEF " ) , 0 } ) ;
2009-05-19 21:23:04 +03:00
}
2010-01-02 03:48:44 +02:00
int charperline = 35 ;
if ( pom . size ( ) > 1 )
2010-01-25 05:09:42 +02:00
charperline = 50 ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CSelWindow > ( text , playerID , charperline , intComps , pom , askID ) ;
2023-07-08 13:33:04 +02:00
intComps [ 0 ] - > clickPressed ( GH . getCursorPosition ( ) ) ;
intComps [ 0 ] - > clickReleased ( GH . getCursorPosition ( ) ) ;
2009-05-19 21:23:04 +03:00
}
2008-08-27 13:19:18 +03:00
}
2015-12-02 16:56:26 +02:00
void CPlayerInterface : : showTeleportDialog ( TeleportChannelID channel , TTeleportExitsList exits , bool impassable , QueryID askID )
2015-03-08 16:17:24 +02:00
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
2015-12-02 16:56:26 +02:00
int choosenExit = - 1 ;
auto neededExit = std : : make_pair ( destinationTeleport , destinationTeleportPos ) ;
2016-10-30 11:00:25 +02:00
if ( destinationTeleport ! = ObjectInstanceID ( ) & & vstd : : contains ( exits , neededExit ) )
2015-12-02 16:56:26 +02:00
choosenExit = vstd : : find_pos ( exits , neededExit ) ;
2015-03-08 16:17:24 +02:00
2015-12-02 16:56:26 +02:00
cb - > selectionMade ( choosenExit , askID ) ;
2015-03-08 16:17:24 +02:00
}
2017-06-06 06:53:51 +02:00
void CPlayerInterface : : showMapObjectSelectDialog ( QueryID askID , const Component & icon , const MetaString & title , const MetaString & description , const std : : vector < ObjectInstanceID > & objects )
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
auto selectCallback = [ = ] ( int selection )
{
2017-11-26 23:18:18 +02:00
JsonNode reply ( JsonNode : : JsonType : : DATA_INTEGER ) ;
2017-06-06 06:53:51 +02:00
reply . Integer ( ) = selection ;
cb - > sendQueryReply ( reply , askID ) ;
} ;
auto cancelCallback = [ = ] ( )
{
2017-11-26 23:18:18 +02:00
JsonNode reply ( JsonNode : : JsonType : : DATA_NULL ) ;
2017-06-06 06:53:51 +02:00
cb - > sendQueryReply ( reply , askID ) ;
} ;
const std : : string localTitle = title . toString ( ) ;
const std : : string localDescription = description . toString ( ) ;
std : : vector < int > tempList ;
tempList . reserve ( objects . size ( ) ) ;
for ( auto item : objects )
tempList . push_back ( item . getNum ( ) ) ;
2018-07-25 00:36:48 +02:00
CComponent localIconC ( icon ) ;
2017-07-03 21:01:03 +02:00
2018-07-25 00:36:48 +02:00
std : : shared_ptr < CIntObject > localIcon = localIconC . image ;
localIconC . removeChild ( localIcon . get ( ) , false ) ;
2017-07-03 21:01:03 +02:00
2018-07-25 00:36:48 +02:00
std : : shared_ptr < CObjectListWindow > wnd = std : : make_shared < CObjectListWindow > ( tempList , localIcon , localTitle , localDescription , selectCallback ) ;
2017-06-06 06:53:51 +02:00
wnd - > onExit = cancelCallback ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( wnd ) ;
2017-06-06 06:53:51 +02:00
}
2023-04-16 00:48:49 +02:00
void CPlayerInterface : : tileRevealed ( const std : : unordered_set < int3 > & pos )
2023-03-15 00:30:58 +02:00
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
//FIXME: wait for dialog? Magi hut/eye would benefit from this but may break other areas
2023-04-16 00:48:49 +02:00
adventureInt - > onMapTilesChanged ( pos ) ;
2023-03-15 00:30:58 +02:00
}
2023-04-16 00:48:49 +02:00
void CPlayerInterface : : tileHidden ( const std : : unordered_set < int3 > & pos )
2023-03-15 00:30:58 +02:00
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-16 00:48:49 +02:00
adventureInt - > onMapTilesChanged ( pos ) ;
2023-03-15 00:30:58 +02:00
}
void CPlayerInterface : : openHeroWindow ( const CGHeroInstance * hero )
2008-09-07 06:38:37 +03:00
{
2010-01-02 03:48:44 +02:00
boost : : unique_lock < boost : : recursive_mutex > un ( * pim ) ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CHeroWindow > ( hero ) ;
2008-09-07 06:38:37 +03:00
}
2009-09-01 16:54:13 +03:00
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : availableCreaturesChanged ( const CGDwelling * town )
2009-09-05 17:10:26 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2016-10-30 11:00:25 +02:00
if ( const CGTownInstance * townObj = dynamic_cast < const CGTownInstance * > ( town ) )
2009-09-05 17:10:26 +03:00
{
2023-05-16 17:34:23 +02:00
for ( auto fortScreen : GH . windows ( ) . findWindows < CFortScreen > ( ) )
2023-02-02 21:54:47 +02:00
fortScreen - > creaturesChangedEventHandler ( ) ;
2023-05-16 17:34:23 +02:00
for ( auto castleInterface : GH . windows ( ) . findWindows < CCastleInterface > ( ) )
2023-02-02 21:54:47 +02:00
castleInterface - > creaturesChangedEventHandler ( ) ;
2011-07-22 19:22:22 +03:00
2023-05-16 15:07:03 +02:00
if ( townObj )
2023-05-16 15:20:35 +02:00
for ( auto ki : GH . windows ( ) . findWindows < CKingdomInterface > ( ) )
2011-07-22 19:22:22 +03:00
ki - > townChanged ( townObj ) ;
2010-01-02 03:48:44 +02:00
}
2023-05-16 15:07:03 +02:00
else if ( town & & GH . windows ( ) . count ( ) > 0 & & ( town - > ID = = Obj : : CREATURE_GENERATOR1
2012-09-23 21:01:04 +03:00
| | town - > ID = = Obj : : CREATURE_GENERATOR4 | | town - > ID = = Obj : : WAR_MACHINE_FACTORY ) )
2010-01-02 03:48:44 +02:00
{
2023-05-16 17:34:23 +02:00
for ( auto crw : GH . windows ( ) . findWindows < CRecruitmentWindow > ( ) )
if ( crw - > dwelling = = town )
crw - > availableCreaturesChanged ( ) ;
2009-09-05 17:10:26 +03:00
}
}
2010-05-02 21:20:26 +03:00
void CPlayerInterface : : heroBonusChanged ( const CGHeroInstance * hero , const Bonus & bonus , bool gain )
2008-09-07 06:38:37 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-01 00:20:01 +02:00
if ( bonus . type = = BonusType : : NONE )
2012-04-06 18:02:15 +03:00
return ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2023-05-01 00:20:01 +02:00
if ( ( bonus . type = = BonusType : : FLYING_MOVEMENT | | bonus . type = = BonusType : : WATER_WALKING ) & & ! gain )
2010-05-15 18:00:19 +03:00
{
//recalculate paths because hero has lost bonus influencing pathfinding
2023-04-17 01:02:31 +02:00
localState - > erasePath ( hero ) ;
2010-05-15 18:00:19 +03:00
}
2008-09-07 06:38:37 +03:00
}
2016-09-10 02:32:40 +02:00
void CPlayerInterface : : saveGame ( BinarySerializer & h , const int version )
2008-09-07 06:38:37 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-17 12:06:58 +02:00
localState - > serialize ( h , version ) ;
2008-09-07 06:38:37 +03:00
}
2016-09-10 02:32:40 +02:00
void CPlayerInterface : : loadGame ( BinaryDeserializer & h , const int version )
2008-09-07 06:38:37 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-17 12:06:58 +02:00
localState - > serialize ( h , version ) ;
2010-01-02 03:48:44 +02:00
firstCall = - 1 ;
}
2023-02-15 12:08:32 +02:00
void CPlayerInterface : : moveHero ( const CGHeroInstance * h , const CGPath & path )
2010-01-02 03:48:44 +02:00
{
2017-08-11 13:38:10 +02:00
LOG_TRACE ( logGlobal ) ;
2016-10-30 11:00:25 +02:00
if ( ! LOCPLINT - > makingTurn )
2014-06-18 13:31:11 +03:00
return ;
2010-01-02 03:48:44 +02:00
if ( ! h )
2014-06-18 13:31:11 +03:00
return ; //can't find hero
2010-01-02 03:48:44 +02:00
2012-09-08 01:43:41 +03:00
//It shouldn't be possible to move hero with open dialog (or dialog waiting in bg)
2016-10-30 11:00:25 +02:00
if ( showingDialog - > get ( ) | | ! dialogs . empty ( ) )
2014-06-18 13:31:11 +03:00
return ;
2012-09-08 01:43:41 +03:00
2016-01-22 23:35:41 +02:00
setMovementStatus ( true ) ;
2015-01-13 21:57:41 +02:00
2023-04-17 22:16:45 +02:00
if ( localState - > isHeroSleeping ( h ) )
localState - > setHeroAwaken ( h ) ;
2015-01-13 21:57:41 +02:00
2014-08-04 21:33:59 +03:00
boost : : thread moveHeroTask ( std : : bind ( & CPlayerInterface : : doMoveHero , this , h , path ) ) ;
2008-09-07 06:38:37 +03:00
}
2010-01-02 03:48:44 +02:00
2013-05-27 13:53:28 +03:00
void CPlayerInterface : : showGarrisonDialog ( const CArmedInstance * up , const CGHeroInstance * down , bool removableUnits , QueryID queryID )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2017-07-17 14:35:57 +02:00
auto onEnd = [ = ] ( ) { cb - > selectionMade ( 0 , queryID ) ; } ;
2012-04-06 18:02:15 +03:00
2023-04-17 01:02:31 +02:00
if ( stillMoveHero . get ( ) = = DURING_MOVE & & localState - > hasPath ( down ) & & localState - > getPath ( down ) . nodes . size ( ) > 1 ) //to ignore calls on passing through garrisons
2012-04-06 18:02:15 +03:00
{
2011-09-17 21:15:10 +03:00
onEnd ( ) ;
2010-01-02 03:48:44 +02:00
return ;
2011-09-17 21:15:10 +03:00
}
2008-09-07 06:38:37 +03:00
2012-04-06 18:02:15 +03:00
waitForAllDialogs ( ) ;
2018-07-25 00:36:48 +02:00
auto cgw = std : : make_shared < CGarrisonWindow > ( up , down , removableUnits ) ;
2014-08-03 14:16:19 +03:00
cgw - > quit - > addCallback ( onEnd ) ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( cgw ) ;
2010-01-02 03:48:44 +02:00
}
2008-09-25 17:09:31 +03:00
2010-02-27 03:05:39 +02:00
/**
* Shows the dialog that appears when right - clicking an artifact that can be assembled
* into a combinational one on an artifact screen . Does not require the combination of
* artifacts to be legal .
*/
2023-09-03 20:43:41 +02:00
void CPlayerInterface : : showArtifactAssemblyDialog ( const Artifact * artifact , const Artifact * assembledArtifact , CFunctionList < void ( ) > onYes )
2010-02-27 03:05:39 +02:00
{
2023-01-02 15:58:56 +02:00
std : : string text = artifact - > getDescriptionTranslated ( ) ;
2010-02-27 03:05:39 +02:00
text + = " \n \n " ;
2018-04-07 13:34:11 +02:00
std : : vector < std : : shared_ptr < CComponent > > scs ;
2010-02-27 03:05:39 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( assembledArtifact )
2018-04-07 13:34:11 +02:00
{
2010-02-27 03:05:39 +02:00
// You possess all of the components to...
2023-01-02 15:58:56 +02:00
text + = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 732 ] ) % assembledArtifact - > getNameTranslated ( ) ) ;
2010-02-27 03:05:39 +02:00
// Picture of assembled artifact at bottom.
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
auto sc = std : : make_shared < CComponent > ( CComponent : : artifact , assembledArtifact - > getIndex ( ) , 0 ) ;
2010-02-27 03:05:39 +02:00
scs . push_back ( sc ) ;
2018-04-07 13:34:11 +02:00
}
else
{
2010-02-27 03:05:39 +02:00
// Do you wish to disassemble this artifact?
text + = CGI - > generaltexth - > allTexts [ 733 ] ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
showYesNoDialog ( text , onYes , nullptr , scs ) ;
2010-02-27 03:05:39 +02:00
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : requestRealized ( PackageApplied * pa )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2016-10-30 11:00:25 +02:00
if ( pa - > packType = = typeList . getTypeID < MoveHero > ( ) & & stillMoveHero . get ( ) = = DURING_MOVE
2015-03-08 16:17:24 +02:00
& & destinationTeleport = = ObjectInstanceID ( ) )
stillMoveHero . setn ( CONTINUE_MOVE ) ;
2016-10-30 11:00:25 +02:00
if ( destinationTeleport ! = ObjectInstanceID ( )
2015-03-08 16:17:24 +02:00
& & pa - > packType = = typeList . getTypeID < QueryReply > ( )
& & stillMoveHero . get ( ) = = DURING_MOVE )
{ // After teleportation via CGTeleport object is finished
destinationTeleport = ObjectInstanceID ( ) ;
2015-12-04 00:54:25 +02:00
destinationTeleportPos = int3 ( - 1 ) ;
2010-01-02 03:48:44 +02:00
stillMoveHero . setn ( CONTINUE_MOVE ) ;
2015-03-08 16:17:24 +02:00
}
2009-05-19 21:23:04 +03:00
}
2010-01-02 03:48:44 +02:00
2023-04-17 12:06:58 +02:00
void CPlayerInterface : : showHeroExchange ( ObjectInstanceID hero1 , ObjectInstanceID hero2 )
{
heroExchangeStarted ( hero1 , hero2 , QueryID ( - 1 ) ) ;
}
2013-05-27 13:53:28 +03:00
void CPlayerInterface : : heroExchangeStarted ( ObjectInstanceID hero1 , ObjectInstanceID hero2 , QueryID query )
2008-09-25 17:09:31 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CExchangeWindow > ( hero1 , hero2 , query ) ;
2010-01-02 03:48:44 +02:00
}
2009-05-06 13:14:48 +03:00
2023-07-04 19:02:41 +02:00
void CPlayerInterface : : beforeObjectPropertyChanged ( const SetObjectProperty * sop )
{
if ( sop - > what = = ObjProperty : : OWNER )
{
const CGObjectInstance * obj = cb - > getObj ( sop - > id ) ;
if ( obj - > ID = = Obj : : TOWN )
{
auto town = static_cast < const CGTownInstance * > ( obj ) ;
if ( obj - > tempOwner = = playerID )
{
localState - > removeOwnedTown ( town ) ;
adventureInt - > onTownChanged ( town ) ;
}
}
}
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : objectPropertyChanged ( const SetObjectProperty * sop )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-20 21:03:28 +02:00
2016-10-30 11:00:25 +02:00
if ( sop - > what = = ObjProperty : : OWNER )
2009-05-19 21:23:04 +03:00
{
2011-05-03 06:14:18 +03:00
const CGObjectInstance * obj = cb - > getObj ( sop - > id ) ;
2021-03-23 16:47:07 +02:00
if ( obj - > ID = = Obj : : TOWN )
2011-03-22 15:19:07 +02:00
{
2023-04-20 21:03:28 +02:00
auto town = static_cast < const CGTownInstance * > ( obj ) ;
2021-03-23 16:47:07 +02:00
if ( obj - > tempOwner = = playerID )
2023-07-04 19:02:41 +02:00
{
2023-04-17 14:32:18 +02:00
localState - > addOwnedTown ( town ) ;
2023-07-04 19:02:41 +02:00
adventureInt - > onTownChanged ( town ) ;
}
2011-03-22 15:19:07 +02:00
}
2023-04-16 00:48:49 +02:00
2023-07-04 19:02:41 +02:00
//redraw minimap if owner changed
2023-04-16 00:48:49 +02:00
std : : set < int3 > pos = obj - > getBlockedPos ( ) ;
std : : unordered_set < int3 > upos ( pos . begin ( ) , pos . end ( ) ) ;
adventureInt - > onMapTilesChanged ( upos ) ;
2023-04-17 14:32:18 +02:00
assert ( cb - > getTownsInfo ( ) . size ( ) = = localState - > getOwnedTowns ( ) . size ( ) ) ;
2009-05-08 05:58:41 +03:00
}
2008-09-25 17:09:31 +03:00
}
2012-06-13 16:04:06 +03:00
void CPlayerInterface : : initializeHeroTownList ( )
2008-09-25 17:09:31 +03:00
{
2023-04-17 14:17:15 +02:00
if ( localState - > getWanderingHeroes ( ) . empty ( ) )
2018-01-05 19:21:07 +02:00
{
2023-04-17 14:32:18 +02:00
for ( auto & hero : cb - > getHeroesInfo ( ) )
2011-10-10 23:37:11 +03:00
{
2018-01-05 19:21:07 +02:00
if ( ! hero - > inTownGarrison )
2023-04-17 14:17:15 +02:00
localState - > addWanderingHero ( hero ) ;
2011-10-10 23:37:11 +03:00
}
2018-01-05 19:21:07 +02:00
}
2010-02-26 13:18:09 +02:00
2023-04-17 14:32:18 +02:00
if ( localState - > getOwnedTowns ( ) . empty ( ) )
{
for ( auto & town : cb - > getTownsInfo ( ) )
localState - > addOwnedTown ( town ) ;
}
2011-10-04 22:43:49 +03:00
2018-01-05 19:21:07 +02:00
if ( adventureInt )
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( nullptr ) ;
2008-09-25 17:09:31 +03:00
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : showRecruitmentDialog ( const CGDwelling * dwelling , const CArmedInstance * dst , int level )
2008-09-25 17:09:31 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2009-07-21 02:34:06 +03:00
waitWhileDialog ( ) ;
2018-07-25 00:36:48 +02:00
auto recruitCb = [ = ] ( CreatureID id , int count )
{
LOCPLINT - > cb - > recruitCreatures ( dwelling , dst , id , count , - 1 ) ;
} ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CRecruitmentWindow > ( dwelling , level , dst , recruitCb ) ;
2010-01-02 03:48:44 +02:00
}
2009-03-28 02:38:48 +02:00
2017-07-15 13:08:20 +02:00
void CPlayerInterface : : waitWhileDialog ( bool unlockPim )
2010-01-02 03:48:44 +02:00
{
2016-10-30 11:00:25 +02:00
if ( GH . amIGuiThread ( ) )
2012-04-08 04:15:18 +03:00
{
2017-08-10 18:39:27 +02:00
logGlobal - > warn ( " Cannot wait for dialogs in gui thread (deadlock risk)! " ) ;
2012-04-08 04:15:18 +03:00
return ;
}
2012-04-06 18:02:15 +03:00
auto unlock = vstd : : makeUnlockGuardIf ( * pim , unlockPim ) ;
2010-01-02 03:48:44 +02:00
boost : : unique_lock < boost : : mutex > un ( showingDialog - > mx ) ;
while ( showingDialog - > data )
showingDialog - > cond . wait ( un ) ;
2008-09-25 17:09:31 +03:00
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : showShipyardDialog ( const IShipyard * obj )
2008-09-25 17:09:31 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-07-21 13:08:32 +03:00
auto state = obj - > shipyardStatus ( ) ;
2023-03-17 01:19:04 +02:00
TResources cost ;
2010-01-02 03:48:44 +02:00
obj - > getBoatCost ( cost ) ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CShipyardWindow > ( cost , state , obj - > getBoatType ( ) , [ = ] ( ) { cb - > buildBoat ( obj ) ; } ) ;
2008-09-25 17:09:31 +03:00
}
2008-10-26 22:58:34 +02:00
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : newObject ( const CGObjectInstance * obj )
2008-10-26 22:58:34 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2010-01-02 03:48:44 +02:00
//we might have built a boat in shipyard in opened town screen
2016-10-30 11:00:25 +02:00
if ( obj - > ID = = Obj : : BOAT
2010-01-25 05:09:42 +02:00
& & LOCPLINT - > castleInt
2022-12-09 14:42:47 +02:00
& & obj - > visitablePos ( ) = = LOCPLINT - > castleInt - > town - > bestLocation ( ) )
2009-02-09 18:18:48 +02:00
{
2010-12-19 16:39:56 +02:00
CCS - > soundh - > playSound ( soundBase : : newBuilding ) ;
2013-02-11 02:24:57 +03:00
LOCPLINT - > castleInt - > addBuilding ( BuildingID : : SHIP ) ;
2009-02-09 18:18:48 +02:00
}
2010-01-02 03:48:44 +02:00
}
void CPlayerInterface : : centerView ( int3 pos , int focusTime )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2010-08-18 13:53:59 +03:00
waitWhileDialog ( ) ;
2016-01-22 23:35:41 +02:00
CCS - > curh - > hide ( ) ;
2023-02-23 19:46:41 +02:00
adventureInt - > centerOnTile ( pos ) ;
2016-10-30 11:00:25 +02:00
if ( focusTime )
2009-02-09 18:18:48 +02:00
{
2023-05-16 14:10:26 +02:00
GH . windows ( ) . totalRedraw ( ) ;
2014-06-21 16:41:05 +03:00
{
auto unlockPim = vstd : : makeUnlockGuard ( * pim ) ;
IgnoreEvents ignore ( * this ) ;
2023-08-20 22:45:41 +02:00
boost : : this_thread : : sleep_for ( boost : : chrono : : milliseconds ( focusTime ) ) ;
2014-06-21 16:41:05 +03:00
}
2009-05-19 21:23:04 +03:00
}
2016-01-22 23:35:41 +02:00
CCS - > curh - > show ( ) ;
2008-10-26 22:58:34 +02:00
}
2017-09-13 02:35:58 +02:00
void CPlayerInterface : : objectRemoved ( const CGObjectInstance * obj )
2008-10-26 22:58:34 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-08-25 00:08:48 +02:00
if ( LOCPLINT - > cb - > isPlayerMakingTurn ( playerID ) & & obj - > getRemovalSound ( ) )
2017-09-13 02:35:58 +02:00
{
waitWhileDialog ( ) ;
2023-04-16 19:42:56 +02:00
CCS - > soundh - > playSound ( obj - > getRemovalSound ( ) . value ( ) ) ;
2014-07-30 20:49:19 +03:00
}
2023-02-22 22:06:27 +02:00
CGI - > mh - > waitForOngoingAnimations ( ) ;
2017-09-13 02:35:58 +02:00
if ( obj - > ID = = Obj : : HERO & & obj - > tempOwner = = playerID )
2010-01-02 03:48:44 +02:00
{
2017-09-13 02:35:58 +02:00
const CGHeroInstance * h = static_cast < const CGHeroInstance * > ( obj ) ;
2010-01-02 03:48:44 +02:00
heroKilled ( h ) ;
}
2023-03-22 19:49:57 +02:00
GH . fakeMouseMove ( ) ;
2008-10-26 22:58:34 +02:00
}
2023-02-12 13:02:06 +02:00
void CPlayerInterface : : objectRemovedAfter ( )
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-16 00:48:49 +02:00
adventureInt - > onMapTilesChanged ( boost : : none ) ;
2023-07-15 15:04:54 +02:00
// visiting or garrisoned hero removed - recreate castle window
if ( castleInt )
openTownWindow ( castleInt - > town ) ;
2023-07-15 15:09:07 +02:00
for ( auto ki : GH . windows ( ) . findWindows < CKingdomInterface > ( ) )
ki - > heroRemoved ( ) ;
2023-02-12 13:02:06 +02:00
}
2019-05-03 12:01:48 +02:00
void CPlayerInterface : : playerBlocked ( int reason , bool start )
{
if ( reason = = PlayerBlocked : : EReason : : UPCOMING_BATTLE )
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
if ( CSH - > howManyPlayerInterfaces ( ) > 1 & & LOCPLINT ! = this & & LOCPLINT - > makingTurn = = false )
{
2019-05-03 12:01:48 +02:00
//one of our players who isn't last in order got attacked not by our another player (happens for example in hotseat mode)
LOCPLINT = this ;
GH . curInt = this ;
2023-04-20 21:03:28 +02:00
adventureInt - > onCurrentPlayerChanged ( playerID ) ;
2022-12-27 22:19:05 +02:00
std : : string msg = CGI - > generaltexth - > translate ( " vcmi.adventureMap.playerAttacked " ) ;
2019-05-03 12:01:48 +02:00
boost : : replace_first ( msg , " %s " , cb - > getStartInfo ( ) - > playerInfos . find ( playerID ) - > second . name ) ;
std : : vector < std : : shared_ptr < CComponent > > cmp ;
cmp . push_back ( std : : make_shared < CComponent > ( CComponent : : flag , playerID . getNum ( ) , 0 ) ) ;
makingTurn = true ; //workaround for stiff showInfoDialog implementation
showInfoDialog ( msg , cmp ) ;
makingTurn = false ;
}
}
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : update ( )
2008-10-26 22:58:34 +02:00
{
2015-06-22 20:53:47 +02:00
// Make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request
2017-06-14 06:59:41 +02:00
boost : : shared_lock < boost : : shared_mutex > gsLock ( CGameState : : mutex ) ;
2016-01-22 22:30:24 +02:00
// While mutexes were locked away we may be have stopped being the active interface
2016-10-30 11:00:25 +02:00
if ( LOCPLINT ! = this )
2010-02-13 06:47:31 +02:00
return ;
2016-01-22 22:30:24 +02:00
2010-01-02 03:48:44 +02:00
//if there are any waiting dialogs, show them
2018-01-05 19:21:07 +02:00
if ( ( CSH - > howManyPlayerInterfaces ( ) < = 1 | | makingTurn ) & & ! dialogs . empty ( ) & & ! showingDialog - > get ( ) )
2009-02-09 18:18:48 +02:00
{
2010-01-02 03:48:44 +02:00
showingDialog - > set ( true ) ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( dialogs . front ( ) ) ;
2010-01-02 03:48:44 +02:00
dialogs . pop_front ( ) ;
2009-02-09 18:18:48 +02:00
}
2010-01-02 03:48:44 +02:00
2023-02-10 23:29:13 +02:00
assert ( adventureInt ) ;
2010-06-01 02:53:35 +03:00
2011-04-13 22:52:56 +03:00
// Handles mouse and key input
GH . handleEvents ( ) ;
2023-05-16 14:10:26 +02:00
GH . windows ( ) . simpleRedraw ( ) ;
2010-01-02 03:48:44 +02:00
}
int CPlayerInterface : : getLastIndex ( std : : string namePrefix )
{
using namespace boost : : filesystem ;
using namespace boost : : algorithm ;
2013-07-08 23:55:22 +03:00
path gamesDir = VCMIDirs : : get ( ) . userSavePath ( ) ;
2010-01-02 03:48:44 +02:00
std : : map < std : : time_t , int > dates ; //save number => datestamp
2014-08-21 23:26:28 +03:00
const directory_iterator enddir ;
2016-10-30 11:00:25 +02:00
if ( ! exists ( gamesDir ) )
2012-12-19 21:19:09 +03:00
create_directory ( gamesDir ) ;
2014-08-21 23:26:28 +03:00
else
for ( directory_iterator dir ( gamesDir ) ; dir ! = enddir ; + + dir )
2010-01-02 03:48:44 +02:00
{
2023-01-14 15:27:11 +02:00
if ( is_regular_file ( dir - > status ( ) ) )
2009-06-28 16:49:39 +03:00
{
2014-08-21 23:26:28 +03:00
std : : string name = dir - > path ( ) . filename ( ) . string ( ) ;
2016-10-30 11:00:25 +02:00
if ( starts_with ( name , namePrefix ) & & ends_with ( name , " .vcgm1 " ) )
2009-06-28 16:49:39 +03:00
{
2010-01-02 03:48:44 +02:00
char nr = name [ namePrefix . size ( ) ] ;
2016-10-30 11:00:25 +02:00
if ( std : : isdigit ( nr ) )
2010-01-02 03:48:44 +02:00
dates [ last_write_time ( dir - > path ( ) ) ] = boost : : lexical_cast < int > ( nr ) ;
2009-06-28 16:49:39 +03:00
}
}
}
2010-01-02 03:48:44 +02:00
2016-10-30 11:00:25 +02:00
if ( ! dates . empty ( ) )
2010-01-02 03:48:44 +02:00
return ( - - dates . end ( ) ) - > second ; //return latest file number
return 0 ;
2008-10-26 22:58:34 +02:00
}
2009-12-28 06:08:24 +02:00
2013-11-17 20:57:04 +03:00
void CPlayerInterface : : gameOver ( PlayerColor player , const EVictoryLossCheckResult & victoryLossCheckResult )
2010-01-29 22:52:45 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2010-02-22 15:30:46 +02:00
2016-10-30 11:00:25 +02:00
if ( player = = playerID )
2010-01-29 22:52:45 +02:00
{
2016-10-30 11:00:25 +02:00
if ( victoryLossCheckResult . loss ( ) )
2013-12-29 14:27:38 +03:00
showInfoDialog ( CGI - > generaltexth - > allTexts [ 95 ] ) ;
2013-12-06 22:44:11 +03:00
2023-04-17 00:09:25 +02:00
assert ( GH . curInt = = LOCPLINT ) ;
2019-06-01 19:28:21 +02:00
auto previousInterface = LOCPLINT ; //without multiple player interfaces some of lines below are useless, but for hotseat we wanna swap player interface temporarily
2023-04-17 00:09:25 +02:00
2019-06-01 19:28:21 +02:00
LOCPLINT = this ; //this is needed for dialog to show and avoid freeze, dialog showing logic should be reworked someday
GH . curInt = this ; //waiting for dialogs requires this to get events
2023-04-17 00:09:25 +02:00
2019-06-01 19:28:21 +02:00
if ( ! makingTurn )
2013-09-02 01:55:57 +03:00
{
2019-06-01 19:28:21 +02:00
makingTurn = true ; //also needed for dialog to show with current implementation
waitForAllDialogs ( ) ;
makingTurn = false ;
2013-09-02 01:55:57 +03:00
}
2019-06-01 19:28:21 +02:00
else
waitForAllDialogs ( ) ;
GH . curInt = previousInterface ;
LOCPLINT = previousInterface ;
2010-01-29 22:52:45 +02:00
2018-01-05 19:21:07 +02:00
if ( CSH - > howManyPlayerInterfaces ( ) = = 1 & & ! settings [ " session " ] [ " spectate " ] . Bool ( ) ) //all human players eliminated
2013-12-20 18:00:48 +03:00
{
2018-01-05 19:21:07 +02:00
if ( adventureInt )
2013-12-20 18:00:48 +03:00
{
2023-05-16 15:20:35 +02:00
GH . windows ( ) . popWindows ( GH . windows ( ) . count ( ) ) ;
2018-07-25 00:36:48 +02:00
adventureInt . reset ( ) ;
2013-12-20 18:00:48 +03:00
}
}
2018-01-05 19:21:07 +02:00
if ( victoryLossCheckResult . victory ( ) & & LOCPLINT = = this )
2013-12-07 13:04:17 +03:00
{
2018-01-05 19:21:07 +02:00
// end game if current human player has won
2019-06-08 16:59:04 +02:00
CSH - > sendClientDisconnecting ( ) ;
2018-01-05 19:21:07 +02:00
requestReturningToMainMenu ( true ) ;
2013-12-07 13:04:17 +03:00
}
2018-01-05 19:21:07 +02:00
else if ( CSH - > howManyPlayerInterfaces ( ) = = 1 & & ! settings [ " session " ] [ " spectate " ] . Bool ( ) )
2010-07-24 14:46:04 +03:00
{
2018-01-05 19:21:07 +02:00
//all human players eliminated
2019-06-08 16:59:04 +02:00
CSH - > sendClientDisconnecting ( ) ;
2018-01-05 19:21:07 +02:00
requestReturningToMainMenu ( false ) ;
2010-07-24 14:46:04 +03:00
}
2013-09-02 01:55:57 +03:00
2023-04-17 00:09:25 +02:00
if ( GH . curInt = = this )
GH . curInt = nullptr ;
2010-01-29 22:52:45 +02:00
}
}
2010-05-02 21:20:26 +03:00
void CPlayerInterface : : playerBonusChanged ( const Bonus & bonus , bool gain )
2010-02-10 04:56:00 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2010-02-10 04:56:00 +02:00
}
void CPlayerInterface : : showPuzzleMap ( )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2010-02-10 04:56:00 +02:00
waitWhileDialog ( ) ;
//TODO: interface should not know the real position of Grail...
2011-12-14 00:23:17 +03:00
double ratio = 0 ;
2016-01-20 09:44:13 +02:00
int3 grailPos = cb - > getGrailPos ( & ratio ) ;
2010-02-10 04:56:00 +02:00
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CPuzzleWindow > ( grailPos , ratio ) ;
2010-02-10 04:56:00 +02:00
}
2015-01-13 21:57:41 +02:00
void CPlayerInterface : : viewWorldMap ( )
{
2023-02-21 14:38:08 +02:00
adventureInt - > openWorldView ( ) ;
2015-01-13 21:57:41 +02:00
}
2023-09-04 12:03:15 +02:00
void CPlayerInterface : : advmapSpellCast ( const CGHeroInstance * caster , SpellID spellID )
2010-05-16 16:42:19 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2017-07-03 20:38:00 +02:00
2023-05-16 17:34:23 +02:00
if ( GH . windows ( ) . topWindow < CSpellWindow > ( ) )
2023-05-16 15:20:35 +02:00
GH . windows ( ) . popWindows ( 1 ) ;
2017-07-03 20:38:00 +02:00
2017-07-04 00:32:40 +02:00
if ( spellID = = SpellID : : FLY | | spellID = = SpellID : : WATER_WALK )
2023-04-17 01:02:31 +02:00
localState - > erasePath ( caster ) ;
2017-07-04 00:32:40 +02:00
2023-09-04 12:03:15 +02:00
auto castSoundPath = spellID . toSpell ( ) - > getCastSound ( ) ;
2017-07-04 00:32:40 +02:00
if ( ! castSoundPath . empty ( ) )
2014-03-10 19:00:58 +03:00
CCS - > soundh - > playSound ( castSoundPath ) ;
2010-05-16 16:42:19 +03:00
}
2023-07-13 21:56:53 +02:00
void CPlayerInterface : : tryDigging ( const CGHeroInstance * h )
2010-02-24 15:03:36 +02:00
{
2015-11-29 11:32:06 +02:00
int msgToShow = - 1 ;
2021-11-05 23:08:48 +02:00
2023-02-22 17:24:42 +02:00
const auto diggingStatus = h - > diggingStatus ( ) ;
2021-11-05 23:08:48 +02:00
switch ( diggingStatus )
2010-02-24 15:03:36 +02:00
{
2015-11-29 11:32:06 +02:00
case EDiggingStatus : : CAN_DIG :
2012-01-03 04:55:26 +03:00
break ;
2015-11-29 11:32:06 +02:00
case EDiggingStatus : : LACK_OF_MOVEMENT :
2012-01-03 04:55:26 +03:00
msgToShow = 56 ; //"Digging for artifacts requires a whole day, try again tomorrow."
break ;
2015-11-29 11:32:06 +02:00
case EDiggingStatus : : TILE_OCCUPIED :
2012-01-03 04:55:26 +03:00
msgToShow = 97 ; //Try searching on clear ground.
break ;
2015-11-29 11:32:06 +02:00
case EDiggingStatus : : WRONG_TERRAIN :
2012-01-03 04:55:26 +03:00
msgToShow = 60 ; ////Try looking on land!
break ;
2023-03-21 20:02:58 +02:00
case EDiggingStatus : : BACKPACK_IS_FULL :
msgToShow = 247 ; //Searching for the Grail is fruitless...
break ;
2012-01-03 04:55:26 +03:00
default :
assert ( 0 ) ;
2010-02-24 15:03:36 +02:00
}
2012-01-03 04:55:26 +03:00
2021-11-05 23:08:48 +02:00
if ( msgToShow < 0 )
2012-01-03 04:55:26 +03:00
cb - > dig ( h ) ;
else
showInfoDialog ( CGI - > generaltexth - > allTexts [ msgToShow ] ) ;
2010-02-27 17:59:21 +02:00
}
2010-03-21 00:17:19 +02:00
2023-08-30 21:07:02 +02:00
void CPlayerInterface : : battleNewRoundFirst ( const BattleID & battleID )
2010-05-07 15:29:41 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
BATTLE_EVENT_POSSIBLE_RETURN ;
2010-05-07 15:29:41 +03:00
2023-08-30 21:07:02 +02:00
battleInt - > newRoundFirst ( ) ;
2010-05-07 15:29:41 +03:00
}
2010-05-14 05:18:37 +03:00
void CPlayerInterface : : stopMovement ( )
{
2016-10-30 11:00:25 +02:00
if ( stillMoveHero . get ( ) = = DURING_MOVE ) //if we are in the middle of hero movement
2010-05-14 05:18:37 +03:00
stillMoveHero . setn ( STOP_MOVE ) ; //after showing dialog movement will be stopped
2010-05-18 10:01:54 +03:00
}
void CPlayerInterface : : showMarketWindow ( const IMarket * market , const CGHeroInstance * visitor )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-29 13:53:44 +02:00
if ( market - > allowsTrade ( EMarketMode : : ARTIFACT_EXP ) & & visitor - > getAlignment ( ) ! = EAlignment : : EVIL )
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CAltarWindow > ( market , visitor , EMarketMode : : ARTIFACT_EXP ) ;
2023-04-29 13:53:44 +02:00
else if ( market - > allowsTrade ( EMarketMode : : CREATURE_EXP ) & & visitor - > getAlignment ( ) ! = EAlignment : : GOOD )
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CAltarWindow > ( market , visitor , EMarketMode : : CREATURE_EXP ) ;
2023-04-29 13:53:44 +02:00
else if ( market - > allowsTrade ( EMarketMode : : CREATURE_UNDEAD ) )
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CTransformerWindow > ( market , visitor ) ;
2023-04-29 13:53:44 +02:00
else if ( ! market - > availableModes ( ) . empty ( ) )
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CMarketplaceWindow > ( market , visitor , market - > availableModes ( ) . front ( ) ) ;
2010-06-27 19:03:01 +03:00
}
2010-07-20 17:08:13 +03:00
void CPlayerInterface : : showUniversityWindow ( const IMarket * market , const CGHeroInstance * visitor )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CUniversityWindow > ( visitor , market ) ;
2010-07-20 17:08:13 +03:00
}
2010-07-22 03:32:45 +03:00
void CPlayerInterface : : showHillFortWindow ( const CGObjectInstance * object , const CGHeroInstance * visitor )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CHillFortWindow > ( visitor , object ) ;
2010-07-22 03:32:45 +03:00
}
2017-07-15 13:08:20 +02:00
void CPlayerInterface : : availableArtifactsChanged ( const CGBlackMarket * bm )
2010-06-27 19:03:01 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-16 17:34:23 +02:00
for ( auto cmw : GH . windows ( ) . findWindows < CMarketplaceWindow > ( ) )
2010-06-27 19:03:01 +03:00
cmw - > artifactsChanged ( false ) ;
2010-07-09 02:03:27 +03:00
}
void CPlayerInterface : : showTavernWindow ( const CGObjectInstance * townOrTavern )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CTavernWindow > ( townOrTavern ) ;
2010-07-13 08:25:40 +03:00
}
2012-03-07 17:05:54 +03:00
void CPlayerInterface : : showThievesGuildWindow ( const CGObjectInstance * obj )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CThievesGuildWindow > ( obj ) ;
2012-03-07 17:05:54 +03:00
}
2012-07-06 22:12:04 +03:00
void CPlayerInterface : : showQuestLog ( )
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CQuestLog > ( LOCPLINT - > cb - > getMyQuests ( ) ) ;
2012-07-06 22:12:04 +03:00
}
2010-07-13 08:25:40 +03:00
void CPlayerInterface : : showShipyardDialogOrProblemPopup ( const IShipyard * obj )
{
2016-10-30 11:00:25 +02:00
if ( obj - > shipyardStatus ( ) ! = IBoatGenerator : : GOOD )
2010-07-13 08:25:40 +03:00
{
MetaString txt ;
obj - > getProblemText ( txt ) ;
showInfoDialog ( txt . toString ( ) ) ;
}
else
showShipyardDialog ( obj ) ;
2010-07-21 13:09:29 +03:00
}
2018-01-05 19:21:07 +02:00
void CPlayerInterface : : requestReturningToMainMenu ( bool won )
2010-08-20 16:34:39 +03:00
{
2018-01-05 19:21:07 +02:00
if ( won & & cb - > getStartInfo ( ) - > campState )
CSH - > startCampaignScenario ( cb - > getStartInfo ( ) - > campState ) ;
else
2023-06-26 20:51:10 +02:00
{
GH . dispatchMainThread (
[ ] ( )
{
CSH - > endGameplay ( ) ;
GH . defActionsDef = 63 ;
CMM - > menu - > switchToTab ( " main " ) ;
}
) ;
}
2010-08-18 12:50:25 +03:00
}
2016-01-23 14:20:51 +02:00
void CPlayerInterface : : askToAssembleArtifact ( const ArtifactLocation & al )
{
2023-04-15 03:33:00 +02:00
auto hero = std : : visit ( HeroObjectRetriever ( ) , al . artHolder ) ;
2016-11-03 20:49:55 +02:00
if ( hero )
2016-01-23 14:20:51 +02:00
{
2016-10-30 11:00:25 +02:00
auto art = hero - > getArt ( al . slot ) ;
2016-11-03 20:49:55 +02:00
if ( art = = nullptr )
2016-10-30 11:00:25 +02:00
{
logGlobal - > error ( " artifact location %d points to nothing " ,
2020-10-06 01:27:04 +02:00
al . slot . num ) ;
2016-10-30 11:00:25 +02:00
return ;
}
2023-04-23 14:10:35 +02:00
ArtifactUtilsClient : : askToAssemble ( hero , al . slot ) ;
2016-01-23 14:20:51 +02:00
}
}
2011-01-06 22:00:19 +02:00
void CPlayerInterface : : artifactPut ( const ArtifactLocation & al )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-15 03:33:00 +02:00
auto hero = std : : visit ( HeroObjectRetriever ( ) , al . artHolder ) ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2011-01-06 22:00:19 +02:00
}
void CPlayerInterface : : artifactRemoved ( const ArtifactLocation & al )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-15 03:33:00 +02:00
auto hero = std : : visit ( HeroObjectRetriever ( ) , al . artHolder ) ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2023-05-16 15:07:03 +02:00
2023-05-16 15:20:35 +02:00
for ( auto artWin : GH . windows ( ) . findWindows < CArtifactHolder > ( ) )
2023-05-16 15:07:03 +02:00
artWin - > artifactRemoved ( al ) ;
2022-12-27 14:24:42 +02:00
waitWhileDialog ( ) ;
2011-01-06 22:00:19 +02:00
}
void CPlayerInterface : : artifactMoved ( const ArtifactLocation & src , const ArtifactLocation & dst )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-15 03:33:00 +02:00
auto hero = std : : visit ( HeroObjectRetriever ( ) , dst . artHolder ) ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2023-02-13 21:10:53 +02:00
bool redraw = true ;
// If a bulk transfer has arrived, then redrawing only the last art movement.
if ( numOfMovedArts ! = 0 )
{
numOfMovedArts - - ;
if ( numOfMovedArts ! = 0 )
redraw = false ;
}
2023-05-16 15:20:35 +02:00
for ( auto artWin : GH . windows ( ) . findWindows < CArtifactHolder > ( ) )
2023-05-16 15:07:03 +02:00
artWin - > artifactMoved ( src , dst , redraw ) ;
2022-12-27 14:24:42 +02:00
waitWhileDialog ( ) ;
2022-11-07 00:36:13 +02:00
}
2023-02-13 21:10:53 +02:00
void CPlayerInterface : : bulkArtMovementStart ( size_t numOfArts )
2022-11-07 00:36:13 +02:00
{
2023-02-13 21:10:53 +02:00
numOfMovedArts = numOfArts ;
2011-01-06 22:00:19 +02:00
}
2011-01-22 05:43:20 +02:00
void CPlayerInterface : : artifactAssembled ( const ArtifactLocation & al )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-15 03:33:00 +02:00
auto hero = std : : visit ( HeroObjectRetriever ( ) , al . artHolder ) ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2023-05-16 15:07:03 +02:00
2023-05-16 15:20:35 +02:00
for ( auto artWin : GH . windows ( ) . findWindows < CArtifactHolder > ( ) )
2023-05-16 15:07:03 +02:00
artWin - > artifactAssembled ( al ) ;
2011-01-22 05:43:20 +02:00
}
void CPlayerInterface : : artifactDisassembled ( const ArtifactLocation & al )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-04-15 03:33:00 +02:00
auto hero = std : : visit ( HeroObjectRetriever ( ) , al . artHolder ) ;
2023-04-20 21:03:28 +02:00
adventureInt - > onHeroChanged ( hero ) ;
2023-05-16 15:07:03 +02:00
2023-05-16 15:20:35 +02:00
for ( auto artWin : GH . windows ( ) . findWindows < CArtifactHolder > ( ) )
2023-05-16 15:07:03 +02:00
artWin - > artifactDisassembled ( al ) ;
2011-01-22 05:43:20 +02:00
}
2017-07-15 13:08:20 +02:00
void CPlayerInterface : : waitForAllDialogs ( bool unlockPim )
2012-02-22 16:41:27 +03:00
{
2013-11-03 15:51:25 +03:00
while ( ! dialogs . empty ( ) )
2012-02-22 16:41:27 +03:00
{
2012-04-06 18:02:15 +03:00
auto unlock = vstd : : makeUnlockGuardIf ( * pim , unlockPim ) ;
2023-08-20 22:45:41 +02:00
boost : : this_thread : : sleep_for ( boost : : chrono : : milliseconds ( 5 ) ) ;
2012-02-14 21:04:45 +03:00
}
2012-04-06 18:02:15 +03:00
waitWhileDialog ( unlockPim ) ;
2012-02-14 21:04:45 +03:00
}
2011-02-24 15:57:47 +02:00
2012-04-09 05:53:50 +03:00
void CPlayerInterface : : proposeLoadingGame ( )
{
2023-06-26 20:51:10 +02:00
showYesNoDialog (
CGI - > generaltexth - > allTexts [ 68 ] ,
[ ] ( )
{
GH . dispatchMainThread (
[ ] ( )
{
CSH - > endGameplay ( ) ;
GH . defActionsDef = 63 ;
CMM - > menu - > switchToTab ( " load " ) ;
}
) ;
} ,
nullptr
) ;
2012-04-09 05:53:50 +03:00
}
2014-06-18 13:31:11 +03:00
bool CPlayerInterface : : capturedAllEvents ( )
{
2023-04-20 21:03:28 +02:00
if ( duringMovement )
2014-06-21 09:49:27 +03:00
{
2014-06-21 10:35:08 +03:00
//just inform that we are capturing events. they will be processed by heroMoved() in client thread.
2023-03-22 19:49:57 +02:00
return true ;
}
2023-05-17 22:22:45 +02:00
bool needToLockAdventureMap = adventureInt & & adventureInt - > isActive ( ) & & CGI - > mh - > hasOngoingAnimations ( ) ;
2023-07-19 14:15:23 +02:00
bool quickCombatOngoing = isAutoFightOn & & ! battleInt ;
2023-03-22 19:49:57 +02:00
2023-07-19 14:15:23 +02:00
if ( ignoreEvents | | needToLockAdventureMap | | quickCombatOngoing )
2023-03-22 19:49:57 +02:00
{
2023-05-18 22:31:05 +02:00
GH . input ( ) . ignoreEventsUntilInput ( ) ;
2014-06-21 16:41:05 +03:00
return true ;
}
2015-01-13 21:57:41 +02:00
2014-06-18 13:31:11 +03:00
return false ;
}
2016-01-22 23:35:41 +02:00
void CPlayerInterface : : setMovementStatus ( bool value )
{
duringMovement = value ;
2016-10-30 11:00:25 +02:00
if ( value )
2016-01-22 23:35:41 +02:00
{
CCS - > curh - > hide ( ) ;
}
else
{
CCS - > curh - > show ( ) ;
}
}
2015-03-08 16:17:24 +02:00
void CPlayerInterface : : doMoveHero ( const CGHeroInstance * h , CGPath path )
2014-06-18 13:31:11 +03:00
{
2023-08-20 22:09:17 +02:00
setThreadName ( " doMoveHero " ) ;
2014-06-18 13:31:11 +03:00
int i = 1 ;
2015-03-10 11:06:45 +02:00
auto getObj = [ & ] ( int3 coord , bool ignoreHero )
2015-03-08 16:17:24 +02:00
{
2022-12-09 14:42:47 +02:00
return cb - > getTile ( h - > convertToVisitablePos ( coord ) ) - > topVisitableObj ( ignoreHero ) ;
2015-03-08 16:17:24 +02:00
} ;
2014-06-21 09:49:27 +03:00
2023-06-21 14:38:57 +02:00
auto isTeleportAction = [ & ] ( EPathNodeAction action ) - > bool
2015-12-11 08:42:30 +02:00
{
2023-06-21 14:38:57 +02:00
if ( action ! = EPathNodeAction : : TELEPORT_NORMAL & &
action ! = EPathNodeAction : : TELEPORT_BLOCKING_VISIT & &
action ! = EPathNodeAction : : TELEPORT_BATTLE )
2015-12-11 08:42:30 +02:00
{
return false ;
}
return true ;
} ;
auto getDestTeleportObj = [ & ] ( const CGObjectInstance * currentObject , const CGObjectInstance * nextObjectTop , const CGObjectInstance * nextObject ) - > const CGObjectInstance *
{
2016-10-30 11:00:25 +02:00
if ( CGTeleport : : isConnected ( currentObject , nextObjectTop ) )
2015-12-11 08:42:30 +02:00
return nextObjectTop ;
2016-10-30 11:00:25 +02:00
if ( nextObjectTop & & nextObjectTop - > ID = = Obj : : HERO & &
2015-12-11 08:42:30 +02:00
CGTeleport : : isConnected ( currentObject , nextObject ) )
{
return nextObject ;
}
return nullptr ;
} ;
2015-03-08 16:17:24 +02:00
boost : : unique_lock < boost : : mutex > un ( stillMoveHero . mx ) ;
stillMoveHero . data = CONTINUE_MOVE ;
2015-03-10 11:06:45 +02:00
auto doMovement = [ & ] ( int3 dst , bool transit )
2014-06-18 13:31:11 +03:00
{
2015-03-08 16:17:24 +02:00
stillMoveHero . data = WAITING_MOVE ;
cb - > moveHero ( h , dst , transit ) ;
while ( stillMoveHero . data ! = STOP_MOVE & & stillMoveHero . data ! = CONTINUE_MOVE )
stillMoveHero . cond . wait ( un ) ;
} ;
2014-06-18 13:31:11 +03:00
2015-03-08 16:17:24 +02:00
{
2022-12-07 22:34:08 +02:00
for ( auto & elem : path . nodes )
2022-12-09 14:42:47 +02:00
elem . coord = h - > convertFromVisitablePos ( elem . coord ) ;
2022-12-07 22:34:08 +02:00
2023-06-21 16:10:26 +02:00
int soundChannel = - 1 ;
2023-09-04 12:03:15 +02:00
AudioPath soundName ;
2023-06-21 16:10:26 +02:00
2023-09-04 12:03:15 +02:00
auto getMovementSoundFor = [ & ] ( const CGHeroInstance * hero , int3 posPrev , int3 posNext , EPathNodeAction moveType ) - > AudioPath
2023-06-21 16:10:26 +02:00
{
2023-07-13 19:44:45 +02:00
if ( moveType = = EPathNodeAction : : TELEPORT_BATTLE | | moveType = = EPathNodeAction : : TELEPORT_BLOCKING_VISIT | | moveType = = EPathNodeAction : : TELEPORT_NORMAL )
2023-09-04 12:03:15 +02:00
return { } ;
2023-07-13 19:44:45 +02:00
if ( moveType = = EPathNodeAction : : EMBARK | | moveType = = EPathNodeAction : : DISEMBARK )
2023-09-04 12:03:15 +02:00
return { } ;
2023-07-13 19:44:45 +02:00
if ( moveType = = EPathNodeAction : : BLOCKING_VISIT )
2023-09-04 12:03:15 +02:00
return { } ;
2023-07-13 19:44:45 +02:00
2023-06-21 16:10:26 +02:00
// flying movement sound
if ( hero - > hasBonusOfType ( BonusType : : FLYING_MOVEMENT ) )
2023-09-04 12:03:15 +02:00
return AudioPath : : builtin ( " HORSE10.wav " ) ;
2023-06-21 16:10:26 +02:00
auto prevTile = cb - > getTile ( h - > convertToVisitablePos ( posPrev ) ) ;
auto nextTile = cb - > getTile ( h - > convertToVisitablePos ( posNext ) ) ;
auto prevRoad = prevTile - > roadType ;
auto nextRoad = nextTile - > roadType ;
bool movingOnRoad = prevRoad - > getId ( ) ! = Road : : NO_ROAD & & nextRoad - > getId ( ) ! = Road : : NO_ROAD ;
if ( movingOnRoad )
return nextTile - > terType - > horseSound ;
else
return nextTile - > terType - > horseSoundPenalty ;
} ;
2014-06-18 13:31:11 +03:00
2015-11-17 16:46:26 +02:00
auto canStop = [ & ] ( CGPathNode * node ) - > bool
{
2023-08-31 00:38:16 +02:00
if ( node - > layer ! = EPathfindingLayer : : LAND & & node - > layer ! = EPathfindingLayer : : SAIL )
return false ;
2015-11-17 16:46:26 +02:00
2023-08-31 00:38:16 +02:00
if ( node - > accessible ! = EPathAccessibility : : ACCESSIBLE )
return false ;
2015-11-17 16:46:26 +02:00
2023-08-31 00:38:16 +02:00
return true ;
2015-11-17 16:46:26 +02:00
} ;
2020-10-01 10:38:06 +02:00
for ( i = ( int ) path . nodes . size ( ) - 1 ; i > 0 & & ( stillMoveHero . data = = CONTINUE_MOVE | | ! canStop ( & path . nodes [ i ] ) ) ; i - - )
2014-06-21 09:49:27 +03:00
{
2022-12-24 16:48:24 +02:00
int3 prevCoord = path . nodes [ i ] . coord ;
2015-03-08 16:17:24 +02:00
int3 nextCoord = path . nodes [ i - 1 ] . coord ;
2022-12-24 16:48:24 +02:00
auto prevObject = getObj ( prevCoord , prevCoord = = h - > pos ) ;
2015-12-11 08:42:30 +02:00
auto nextObjectTop = getObj ( nextCoord , false ) ;
auto nextObject = getObj ( nextCoord , true ) ;
2022-12-24 16:48:24 +02:00
auto destTeleportObj = getDestTeleportObj ( prevObject , nextObjectTop , nextObject ) ;
2016-10-30 11:00:25 +02:00
if ( isTeleportAction ( path . nodes [ i - 1 ] . action ) & & destTeleportObj ! = nullptr )
2015-03-08 16:17:24 +02:00
{
2023-06-21 16:10:26 +02:00
CCS - > soundh - > stopSound ( soundChannel ) ;
2015-12-11 08:42:30 +02:00
destinationTeleport = destTeleportObj - > id ;
2015-12-02 16:56:26 +02:00
destinationTeleportPos = nextCoord ;
2015-03-10 11:06:45 +02:00
doMovement ( h - > pos , false ) ;
2023-06-21 14:38:57 +02:00
if ( path . nodes [ i - 1 ] . action = = EPathNodeAction : : TELEPORT_BLOCKING_VISIT
| | path . nodes [ i - 1 ] . action = = EPathNodeAction : : TELEPORT_BATTLE )
2015-12-11 08:42:30 +02:00
{
destinationTeleport = ObjectInstanceID ( ) ;
destinationTeleportPos = int3 ( - 1 ) ;
}
2018-09-16 16:01:36 +02:00
if ( i ! = path . nodes . size ( ) - 1 )
2022-06-20 16:39:50 +02:00
{
2023-07-13 19:44:45 +02:00
soundName = getMovementSoundFor ( h , prevCoord , nextCoord , path . nodes [ i - 1 ] . action ) ;
if ( ! soundName . empty ( ) )
soundChannel = CCS - > soundh - > playSound ( soundName , - 1 ) ;
else
soundChannel = - 1 ;
2022-06-20 16:39:50 +02:00
}
2014-06-21 09:49:27 +03:00
continue ;
2015-03-08 16:17:24 +02:00
}
2014-06-18 13:31:11 +03:00
2016-10-30 11:00:25 +02:00
if ( path . nodes [ i - 1 ] . turns )
2015-03-08 16:17:24 +02:00
{ //stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points)
2014-06-21 09:49:27 +03:00
stillMoveHero . data = STOP_MOVE ;
break ;
}
2014-06-18 13:31:11 +03:00
2014-06-21 09:49:27 +03:00
{
2023-06-21 16:10:26 +02:00
// Start a new sound for the hero movement or let the existing one carry on.
2023-09-04 12:03:15 +02:00
AudioPath newSoundName = getMovementSoundFor ( h , prevCoord , nextCoord , path . nodes [ i - 1 ] . action ) ;
2023-06-21 16:10:26 +02:00
if ( newSoundName ! = soundName )
2014-06-18 13:31:11 +03:00
{
2023-06-21 16:10:26 +02:00
soundName = newSoundName ;
CCS - > soundh - > stopSound ( soundChannel ) ;
2023-07-13 19:44:45 +02:00
if ( ! soundName . empty ( ) )
soundChannel = CCS - > soundh - > playSound ( soundName , - 1 ) ;
else
soundChannel = - 1 ;
2014-06-18 13:31:11 +03:00
}
2014-06-21 09:49:27 +03:00
}
2014-06-18 13:31:11 +03:00
2015-03-08 16:17:24 +02:00
assert ( h - > pos . z = = nextCoord . z ) ; // Z should change only if it's movement via teleporter and in this case this code shouldn't be executed at all
int3 endpos ( nextCoord . x , nextCoord . y , h - > pos . z ) ;
2017-08-11 19:03:05 +02:00
logGlobal - > trace ( " Requesting hero movement to %s " , endpos . toString ( ) ) ;
2014-06-18 13:31:11 +03:00
2015-11-05 09:37:22 +02:00
bool useTransit = false ;
2016-10-30 11:00:25 +02:00
if ( ( i - 2 > = 0 ) // Check there is node after next one; otherwise transit is pointless
2015-12-11 08:42:30 +02:00
& & ( CGTeleport : : isConnected ( nextObjectTop , getObj ( path . nodes [ i - 2 ] . coord , false ) )
| | CGTeleport : : isTeleport ( nextObjectTop ) ) )
2015-03-08 16:17:24 +02:00
{ // Hero should be able to go through object if it's allow transit
2015-11-05 09:37:22 +02:00
useTransit = true ;
2015-03-08 16:17:24 +02:00
}
2016-10-30 11:00:25 +02:00
else if ( path . nodes [ i - 1 ] . layer = = EPathfindingLayer : : AIR )
2015-11-05 09:37:22 +02:00
useTransit = true ;
doMovement ( endpos , useTransit ) ;
2014-06-18 13:31:11 +03:00
2017-08-11 13:38:10 +02:00
logGlobal - > trace ( " Resuming %s " , __FUNCTION__ ) ;
2015-03-08 16:17:24 +02:00
bool guarded = cb - > isInTheMap ( cb - > getGuardingCreaturePosition ( endpos - int3 ( 1 , 0 , 0 ) ) ) ;
2016-10-30 11:00:25 +02:00
if ( ( ! useTransit & & guarded ) | | showingDialog - > get ( ) = = true ) // Abort movement if a guard was fought or there is a dialog to display (Mantis #1136)
2014-06-21 09:49:27 +03:00
break ;
2014-06-18 13:31:11 +03:00
}
2023-06-21 16:10:26 +02:00
CCS - > soundh - > stopSound ( soundChannel ) ;
2014-06-18 13:31:11 +03:00
}
2014-06-21 09:49:27 +03:00
//Update cursor so icon can change if needed when it reappears; doesn;'t apply if a dialog box pops up at the end of the movement
2016-10-30 11:00:25 +02:00
if ( ! showingDialog - > get ( ) )
2014-06-21 09:49:27 +03:00
GH . fakeMouseMove ( ) ;
2023-03-02 19:19:58 +02:00
CGI - > mh - > waitForOngoingAnimations ( ) ;
2016-01-22 23:35:41 +02:00
setMovementStatus ( false ) ;
2014-06-18 13:31:11 +03:00
}
2015-02-26 16:15:17 +02:00
2023-02-21 14:38:08 +02:00
void CPlayerInterface : : showWorldViewEx ( const std : : vector < ObjectPosInfo > & objectPositions , bool showTerrain )
2015-02-26 16:15:17 +02:00
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
2023-02-21 14:38:08 +02:00
adventureInt - > openWorldView ( objectPositions , showTerrain ) ;
2015-02-26 16:15:17 +02:00
}
2023-08-17 18:18:14 +02:00
2023-08-28 17:59:12 +02:00
std : : optional < BattleAction > CPlayerInterface : : makeSurrenderRetreatDecision ( const BattleID & battleID , const BattleStateInfoForRetreat & battleState )
2023-08-17 18:18:14 +02:00
{
return std : : nullopt ;
}