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"
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>
2014-07-13 20:53:37 +03:00
# include "windows/CAdvmapInterface.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"
# include "battle/BattleControlPanel.h"
2009-05-20 13:08:56 +03:00
# include "../CCallback.h"
2014-07-13 20:53:37 +03:00
# include "windows/CCastleInterface.h"
2013-04-07 14:52:07 +03:00
# include "gui/CCursorHandler.h"
2014-07-13 20:53:37 +03:00
# include "windows/CKingdomInterface.h"
2008-08-27 13:19:18 +03:00
# include "CGameInfo.h"
2014-07-13 20:53:37 +03:00
# include "windows/CHeroWindow.h"
# include "windows/CCreatureWindow.h"
# include "windows/CQuestLog.h"
2007-11-19 00:58:28 +02:00
# include "CMessage.h"
2008-08-27 13:19:18 +03:00
# include "CPlayerInterface.h"
2013-04-07 14:52:07 +03:00
# include "gui/SDL_Extensions.h"
2014-07-15 10:14:49 +03:00
# include "widgets/CComponent.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"
2009-05-20 13:08:56 +03:00
# include "Graphics.h"
2014-07-13 20:53:37 +03:00
# include "windows/GUIClasses.h"
2010-12-20 23:22:53 +02:00
# include "../lib/CArtHandler.h"
# include "../lib/CGeneralTextHandler.h"
# include "../lib/CHeroHandler.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"
2014-07-30 19:07:30 +03:00
# include "../lib/mapObjects/CObjectClassesHandler.h" // For displaying correct UI when interacting with objects
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
2013-04-07 13:48:07 +03:00
# include "../lib/mapping/CMap.h"
2009-10-10 08:47:59 +03:00
# include "../lib/VCMIDirs.h"
2010-04-06 16:19:54 +03:00
# include "mapHandler.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"
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"
2022-11-28 16:43:38 +02:00
# include "gui/CAnimation.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"
2018-01-05 19:21:07 +02:00
# include "../lib/CPathfinder.h"
2014-07-15 10:14:49 +03:00
# include <SDL.h>
2018-01-05 19:21:07 +02:00
# include "CServerHandler.h"
// FIXME: only needed for CGameState::mutex
# include "../lib/CGameState.h"
2020-03-22 15:57:13 +02:00
# include "gui/NotificationHandler.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
2007-11-19 00:58:28 +02:00
using namespace CSDL_Ext ;
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
extern std : : queue < SDL_Event > SDLEventsQueue ;
2008-07-27 20:07:37 +03:00
extern boost : : mutex eventsM ;
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-09 13:26:17 +02:00
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
2015-01-28 21:07:57 +02:00
static bool objectBlitOrderSorter ( const TerrainTileObject & a , const TerrainTileObject & b )
2007-11-19 00:58:28 +02:00
{
2015-01-28 21:07:57 +02:00
return CMapHandler : : compareObjectBlitOrder ( a . obj , b . obj ) ;
2014-06-24 02:26:36 +03:00
}
2008-11-02 00:32:56 +02:00
2016-11-03 20:49:55 +02:00
struct HeroObjectRetriever : boost : : static_visitor < const CGHeroInstance * >
{
const CGHeroInstance * operator ( ) ( const ConstTransitivePtr < CGHeroInstance > & h ) const
{
return h ;
}
const CGHeroInstance * operator ( ) ( const ConstTransitivePtr < CStackInstance > & s ) const
{
return nullptr ;
}
} ;
2013-03-03 20:06:03 +03:00
CPlayerInterface : : CPlayerInterface ( PlayerColor Player )
2008-01-26 21:36:31 +02:00
{
2017-08-12 13:36:04 +02:00
logGlobal - > trace ( " \t Human player interface for player %s being constructed " , Player . getStr ( ) ) ;
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 ;
2013-06-26 14:18:27 +03:00
curAction = nullptr ;
2009-05-19 21:23:04 +03:00
playerID = Player ;
human = true ;
2014-09-21 16:42:08 +03:00
currentSelection = nullptr ;
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 ( ) ;
2017-08-13 16:44:41 +02:00
GH . terminate_cond - > set ( false ) ;
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 ;
2015-01-13 21:57:41 +02:00
2014-06-21 09:49:27 +03:00
duringMovement = false ;
2016-01-22 22:30:24 +02:00
ignoreEvents = false ;
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
{
2021-01-30 14:13:45 +02:00
if ( CCS - > soundh ) CCS - > soundh - > ambientStopAllChannels ( ) ;
2017-08-12 13:36:04 +02:00
logGlobal - > trace ( " \t Human player interface for player %s being destructed " , playerID . getStr ( ) ) ;
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
CCS - > soundh - > loadHorseSounds ( ) ;
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
2018-07-25 00:36:48 +02:00
adventureInt . reset ( new CAdvMapInt ( ) ) ;
2008-01-26 21:36:31 +02:00
}
2009-05-19 21:23:04 +03:00
void CPlayerInterface : : yourTurn ( )
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
{
boost : : unique_lock < boost : : mutex > lock ( eventsM ) ; //block handling events until interface is ready
2010-02-13 06:47:31 +02:00
2010-02-20 15:24:38 +02:00
LOCPLINT = this ;
GH . curInt = this ;
2013-06-26 14:18:27 +03:00
adventureInt - > selection = nullptr ;
2009-04-14 15:47:09 +03:00
2020-03-22 15:57:13 +02:00
NotificationHandler : : notify ( " Your turn " ) ;
2017-06-04 07:49:23 +02:00
std : : string prefix = settings [ " session " ] [ " saveprefix " ] . String ( ) ;
2020-10-01 10:38:06 +02:00
int frequency = static_cast < int > ( settings [ " general " ] [ " saveFrequency " ] . Integer ( ) ) ;
2016-10-30 11:00:25 +02:00
if ( firstCall )
2009-05-19 21:23:04 +03:00
{
2018-01-05 19:21:07 +02:00
if ( CSH - > howManyPlayerInterfaces ( ) = = 1 )
2010-02-20 15:24:38 +02:00
adventureInt - > setPlayer ( playerID ) ;
2009-03-27 01:05:40 +02:00
2015-12-13 21:14:37 +02:00
autosaveCount = getLastIndex ( prefix + " Autosave_ " ) ;
2009-05-19 21:23:04 +03:00
2016-10-30 11:00:25 +02:00
if ( firstCall > 0 ) //new game, not loaded
2010-02-20 15:24:38 +02:00
{
2015-12-13 21:14:37 +02:00
int index = getLastIndex ( prefix + " Newgame_ " ) ;
2010-02-20 15:24:38 +02:00
index % = SAVES_COUNT ;
2015-12-13 21:14:37 +02:00
cb - > save ( " Saves/ " + prefix + " Newgame_Autosave_ " + boost : : lexical_cast < std : : string > ( index + 1 ) ) ;
2010-02-20 15:24:38 +02:00
}
firstCall = 0 ;
}
2018-04-03 03:37:09 +02:00
else if ( frequency > 0 & & cb - > getDate ( ) % frequency = = 0 )
2010-02-20 15:24:38 +02:00
{
2015-12-13 21:14:37 +02:00
LOCPLINT - > cb - > save ( " Saves/ " + prefix + " Autosave_ " + boost : : lexical_cast < std : : string > ( autosaveCount + + + 1 ) ) ;
2010-02-20 15:24:38 +02:00
autosaveCount % = 5 ;
}
2009-05-19 21:23:04 +03:00
2016-10-30 11:00:25 +02:00
if ( adventureInt - > player ! = playerID )
2010-07-24 14:46:04 +03:00
adventureInt - > setPlayer ( playerID ) ;
2018-01-05 19:21:07 +02:00
if ( CSH - > howManyPlayerInterfaces ( ) > 1 ) //hot seat message
2010-02-20 15:24:38 +02:00
{
adventureInt - > startHotSeatWait ( 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 ;
2010-02-20 15:24:38 +02:00
adventureInt - > startTurn ( ) ;
2010-02-24 15:03:36 +02:00
}
2010-02-20 15:24:38 +02:00
}
2009-11-01 03:15:16 +02:00
2010-02-20 15:24:38 +02:00
acceptTurn ( ) ;
2008-01-26 21:36:31 +02:00
}
2009-05-19 21:23:04 +03: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
2010-01-02 03:48:44 +02:00
int3 hp = details . start ;
2009-05-19 21:23:04 +03:00
2022-06-05 15:20:01 +02:00
if ( ! hero )
2011-07-17 21:49:05 +03:00
{
//AI hero left the visible area (we can't obtain info)
2014-03-23 15:59:03 +03:00
//TODO very evil workaround -> retrieve pointer to hero so we could animate it
2012-02-14 21:04:45 +03:00
// TODO -> we should not need full CGHeroInstance structure to display animation or it should not be handled by playerint (but by the client itself)
2022-09-18 16:39:10 +02:00
const TerrainTile2 & tile = CGI - > mh - > ttiles [ hp . z ] [ hp . x - 1 ] [ hp . y ] ;
2022-06-05 15:20:01 +02:00
for ( auto & elem : tile . objects )
if ( elem . obj & & elem . obj - > id = = details . id )
2015-01-28 21:07:57 +02:00
hero = dynamic_cast < const CGHeroInstance * > ( elem . obj ) ;
2012-02-14 21:04:45 +03:00
2022-06-05 15:20:01 +02:00
if ( ! hero ) //still nothing...
2012-02-14 21:04:45 +03:00
return ;
2011-07-17 21:49:05 +03:00
}
2010-05-06 15:13:31 +03:00
bool directlyAttackingCreature =
2013-04-20 14:34:01 +03:00
details . attackedFrom
2010-11-13 22:26:15 +02:00
& & adventureInt - > terrain . currentPath //in case if movement has been canceled in the meantime and path was already erased
& & adventureInt - > terrain . currentPath - > nodes . size ( ) = = 3 ; //FIXME should be 2 but works nevertheless...
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
{
2017-09-13 02:35:58 +02:00
updateAmbientSounds ( ) ;
2011-08-19 22:50:24 +03:00
//We may need to change music - select new track, music handler will change it if needed
2022-11-13 14:24:15 +02:00
CCS - > musich - > playMusicFromSet ( " terrain " , LOCPLINT - > cb - > getTile ( hero - > visitablePos ( ) ) - > terType - > name , true , false ) ;
2011-09-19 23:50:25 +03:00
2022-06-05 15:20:01 +02:00
if ( details . result = = TryMoveHero : : TELEPORTATION )
2010-01-02 03:48:44 +02:00
{
2022-06-05 15:20:01 +02:00
if ( adventureInt - > terrain . currentPath )
2014-03-07 16:21:09 +03:00
{
2012-02-18 17:59:37 +03:00
assert ( adventureInt - > terrain . currentPath - > nodes . size ( ) > = 2 ) ;
std : : vector < CGPathNode > : : const_iterator nodesIt = adventureInt - > terrain . currentPath - > nodes . end ( ) - 1 ;
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
2013-04-20 14:34:01 +03:00
removeLastNodeFromPath ( 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)
2013-04-20 14:34:01 +03:00
eraseCurrentPathOf ( hero ) ;
2012-02-18 17:59:37 +03:00
}
}
2015-01-31 00:37:28 +02:00
adventureInt - > centerOn ( hero , true ) ; //actualizing screen pos
2015-02-06 20:11:04 +02:00
adventureInt - > minimap . redraw ( ) ;
2013-04-20 14:34:01 +03:00
adventureInt - > heroList . update ( hero ) ;
2011-09-19 23:50:25 +03:00
return ; //teleport - no fancy moving animation
2010-01-02 03:48:44 +02:00
//TODO: smooth disappear / appear effect
}
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
{
2013-04-20 14:34:01 +03:00
eraseCurrentPathOf ( hero , false ) ;
2008-01-26 21:36:31 +02:00
}
2022-06-05 15:20:01 +02:00
else if ( adventureInt - > terrain . currentPath & & 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
2013-04-20 14:34:01 +03:00
removeLastNodeFromPath ( hero ) ;
2009-07-31 23:10:22 +03:00
}
}
2009-07-03 22:57:14 +03:00
2017-09-16 09:42:27 +02:00
if ( details . stopMovement ( ) ) //hero failed to move
2009-07-31 23:10:22 +03:00
{
2013-04-20 14:34:01 +03:00
hero - > isStanding = true ;
2009-05-19 21:23:04 +03:00
stillMoveHero . setn ( STOP_MOVE ) ;
2009-08-07 01:36:51 +03:00
GH . totalRedraw ( ) ;
2013-04-20 14:34:01 +03:00
adventureInt - > heroList . update ( hero ) ;
2009-05-19 21:23:04 +03:00
return ;
2008-01-26 21:36:31 +02:00
}
2009-05-19 21:23:04 +03:00
2017-07-04 13:24:46 +02:00
ui32 speed = 0 ;
2017-06-03 07:25:10 +02:00
if ( settings [ " session " ] [ " spectate " ] . Bool ( ) )
{
if ( ! settings [ " session " ] [ " spectate-hero-speed " ] . isNull ( ) )
2020-10-01 10:38:06 +02:00
speed = static_cast < ui32 > ( settings [ " session " ] [ " spectate-hero-speed " ] . Integer ( ) ) ;
2017-06-03 07:25:10 +02:00
}
2022-06-05 15:20:01 +02:00
else if ( makingTurn ) // our turn, our hero moves
2020-10-01 10:38:06 +02:00
speed = static_cast < ui32 > ( settings [ " adventure " ] [ " heroSpeed " ] . Float ( ) ) ;
2014-08-11 19:16:39 +03:00
else
2020-10-01 10:38:06 +02:00
speed = static_cast < ui32 > ( settings [ " adventure " ] [ " enemySpeed " ] . Float ( ) ) ;
2014-08-11 19:16:39 +03:00
2022-06-05 15:20:01 +02:00
if ( speed = = 0 )
2014-08-11 19:16:39 +03:00
{
//FIXME: is this a proper solution?
CGI - > mh - > hideObject ( hero ) ;
CGI - > mh - > printObject ( hero ) ;
return ; // no animation
}
adventureInt - > centerOn ( hero ) ; //actualizing screen pos
adventureInt - > minimap . redraw ( ) ;
adventureInt - > heroList . redraw ( ) ;
2013-04-20 14:34:01 +03:00
initMovement ( details , hero , hp ) ;
2009-05-19 21:23:04 +03:00
2022-06-05 15:20:01 +02:00
auto waitFrame = [ & ] ( )
{
int frameNumber = GH . mainFPSmng - > getFrameNumber ( ) ;
auto unlockPim = vstd : : makeUnlockGuard ( * pim ) ;
while ( frameNumber = = GH . mainFPSmng - > getFrameNumber ( ) )
SDL_Delay ( 5 ) ;
} ;
2010-01-02 03:48:44 +02:00
//first initializing done
2009-05-19 21:23:04 +03:00
2010-01-02 03:48:44 +02:00
//main moving
2022-06-05 15:20:01 +02:00
for ( int i = 1 ; i < 32 ; i + = 2 * speed )
2010-01-02 03:48:44 +02:00
{
2013-04-20 14:34:01 +03:00
movementPxStep ( details , i , hp , hero ) ;
2017-05-25 19:57:20 +02:00
# ifndef VCMI_ANDROID
// currently android doesn't seem to be able to handle all these full redraws here, so let's disable it so at least it looks less choppy;
// most likely this is connected with the way that this manual animation+framerate handling is solved
2010-01-02 03:48:44 +02:00
adventureInt - > updateScreen = true ;
2017-05-25 19:57:20 +02:00
# endif
2014-08-11 19:16:39 +03:00
2019-05-03 16:50:36 +02:00
//evil returns here ...
//todo: get rid of it
2022-06-05 15:20:01 +02:00
waitFrame ( ) ; //for animation purposes
2018-04-07 13:34:11 +02:00
}
2010-01-02 03:48:44 +02:00
//main moving done
2009-05-19 21:23:04 +03:00
2010-01-02 03:48:44 +02:00
//finishing move
2013-04-20 14:34:01 +03:00
finishMovement ( details , hp , hero ) ;
hero - > isStanding = true ;
2009-05-19 21:23:04 +03:00
2010-01-02 03:48:44 +02:00
//move finished
2012-06-13 16:04:06 +03:00
adventureInt - > minimap . redraw ( ) ;
2013-04-20 14:34:01 +03:00
adventureInt - > heroList . update ( 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
{
2010-01-02 03:48:44 +02:00
boost : : unique_lock < boost : : mutex > un ( eventsM ) ;
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
while ( ! SDLEventsQueue . empty ( ) )
2010-01-02 03:48:44 +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
SDL_Event ev = SDLEventsQueue . front ( ) ;
SDLEventsQueue . pop ( ) ;
2012-09-11 17:25:19 +03:00
switch ( ev . type )
2010-01-02 03:48:44 +02:00
{
case SDL_MOUSEBUTTONDOWN :
stillMoveHero . setn ( STOP_MOVE ) ;
break ;
case SDL_KEYDOWN :
2016-10-30 11:00:25 +02:00
if ( ev . key . keysym . sym < SDLK_F1 | | ev . key . keysym . sym > SDLK_F15 )
2010-01-02 03:48:44 +02:00
stillMoveHero . setn ( STOP_MOVE ) ;
break ;
}
}
}
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
}
void CPlayerInterface : : heroKilled ( const CGHeroInstance * hero )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-09-02 01:55:57 +03:00
LOG_TRACE_PARAMS ( logGlobal , " Hero %s killed handler for player %s " , hero - > name % playerID ) ;
2012-06-22 14:40:16 +03:00
const CArmedInstance * newSelection = nullptr ;
if ( makingTurn )
{
//find new object for selection: either hero
int next = adventureInt - > getNextHeroIndex ( vstd : : find_pos ( wanderingHeroes , hero ) ) ;
if ( next > = 0 )
newSelection = wanderingHeroes [ next ] ;
//or town
if ( ! newSelection | | newSelection = = hero )
{
if ( towns . empty ( ) )
newSelection = nullptr ;
else
newSelection = towns . front ( ) ;
}
}
2010-01-02 03:48:44 +02:00
wanderingHeroes - = hero ;
2011-01-24 02:09:08 +02:00
2012-06-13 16:04:06 +03:00
adventureInt - > heroList . update ( hero ) ;
2013-09-02 01:55:57 +03:00
if ( makingTurn & & newSelection )
2012-06-22 14:40:16 +03:00
adventureInt - > select ( newSelection , true ) ;
2016-10-30 11:00:25 +02:00
else if ( adventureInt - > selection = = hero )
2012-06-22 14:40:16 +03:00
adventureInt - > selection = nullptr ;
2022-07-21 03:01:50 +02:00
if ( vstd : : contains ( paths , hero ) )
paths . erase ( 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 ( ) )
CCS - > soundh - > playSound ( visitedObj - > getVisitSound ( ) . get ( ) ) ;
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 ;
2010-01-02 03:48:44 +02:00
wanderingHeroes . push_back ( hero ) ;
2012-06-13 16:04:06 +03:00
adventureInt - > heroList . update ( 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
2018-07-25 00:36:48 +02:00
GH . pushInt ( newCastleInt ) ;
2010-01-02 03:48:44 +02:00
}
2009-05-19 21:23:04 +03:00
2010-01-02 03:48:44 +02:00
int3 CPlayerInterface : : repairScreenPos ( int3 pos )
{
2016-10-30 11:00:25 +02:00
if ( pos . x < - CGI - > mh - > frameW )
2010-01-02 03:48:44 +02:00
pos . x = - CGI - > mh - > frameW ;
2016-10-30 11:00:25 +02:00
if ( pos . y < - CGI - > mh - > frameH )
2010-01-02 03:48:44 +02:00
pos . y = - CGI - > mh - > frameH ;
2016-10-30 11:00:25 +02:00
if ( pos . x > CGI - > mh - > sizes . x - adventureInt - > terrain . tilesw + CGI - > mh - > frameW )
2010-08-16 16:51:31 +03:00
pos . x = CGI - > mh - > sizes . x - adventureInt - > terrain . tilesw + CGI - > mh - > frameW ;
2016-10-30 11:00:25 +02:00
if ( pos . y > CGI - > mh - > sizes . y - adventureInt - > terrain . tilesh + CGI - > mh - > frameH )
2010-08-16 16:51:31 +03:00
pos . y = CGI - > mh - > sizes . y - adventureInt - > terrain . tilesh + CGI - > mh - > frameH ;
2010-01-02 03:48:44 +02:00
return pos ;
}
2017-06-03 07:25:10 +02:00
void CPlayerInterface : : activateForSpectator ( )
{
adventureInt - > state = CAdvMapInt : : INGAME ;
adventureInt - > activate ( ) ;
adventureInt - > minimap . activate ( ) ;
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : heroPrimarySkillChanged ( const CGHeroInstance * hero , int which , si64 val )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2016-10-30 11:00:25 +02:00
if ( which = = 4 )
2010-07-20 09:05:45 +03:00
{
2018-07-25 00:36:48 +02:00
if ( CAltarWindow * ctw = dynamic_cast < CAltarWindow * > ( GH . topInt ( ) . get ( ) ) )
2010-07-20 09:05:45 +03:00
ctw - > setExpToLevel ( ) ;
}
2016-10-30 11:00:25 +02:00
else if ( which < GameConstants : : PRIMARY_SKILLS ) //no need to redraw infowin if this is experience (exp is treated as prim skill with id==4)
2010-07-20 12:16:48 +03:00
updateInfo ( 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 ;
2018-07-25 00:36:48 +02:00
CUniversityWindow * cuw = dynamic_cast < CUniversityWindow * > ( GH . topInt ( ) . get ( ) ) ;
2016-10-30 11:00:25 +02:00
if ( cuw ) //university window is open
2010-07-20 17:08:13 +03:00
{
GH . totalRedraw ( ) ;
}
}
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 ;
2010-03-21 00:17:19 +02:00
updateInfo ( hero ) ;
2016-10-30 11:00:25 +02:00
if ( makingTurn & & hero - > tempOwner = = playerID )
2012-06-17 01:40:28 +03:00
adventureInt - > heroList . update ( 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 )
2012-06-17 01:40:28 +03:00
adventureInt - > heroList . update ( 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 ;
2018-07-25 00:36:48 +02:00
if ( CMarketplaceWindow * mw = dynamic_cast < CMarketplaceWindow * > ( GH . topInt ( ) . get ( ) ) )
2016-11-26 14:14:43 +02:00
mw - > resourceChanged ( ) ;
2012-02-16 20:10:58 +03:00
2010-01-02 03:48:44 +02:00
GH . totalRedraw ( ) ;
}
2008-01-26 21:36:31 +02:00
2013-05-27 13:53:28 +03:00
void CPlayerInterface : : heroGotLevel ( const CGHeroInstance * hero , PrimarySkill : : 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 ) ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < CLevelWindow > ( hero , pskill , skills , [ = ] ( ui32 selection )
{
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 ) ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < 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 ;
2010-03-21 00:17:19 +02:00
updateInfo ( town ) ;
2009-05-19 21:23:04 +03:00
2016-10-30 11:00:25 +02:00
if ( town - > garrisonHero ) //wandering hero moved to the garrison
2008-01-26 21:36:31 +02:00
{
2010-01-02 03:48:44 +02:00
CGI - > mh - > hideObject ( town - > garrisonHero ) ;
2016-10-30 11:00:25 +02:00
if ( town - > garrisonHero - > tempOwner = = playerID & & vstd : : contains ( wanderingHeroes , town - > garrisonHero ) ) // our hero
2013-02-23 15:22:23 +03:00
wanderingHeroes - = town - > garrisonHero ;
2010-01-02 03:48:44 +02:00
}
2008-01-26 21:36:31 +02:00
2016-10-30 11:00:25 +02:00
if ( town - > visitingHero ) //hero leaves garrison
2010-01-02 03:48:44 +02:00
{
CGI - > mh - > printObject ( town - > visitingHero ) ;
2016-10-30 11:00:25 +02:00
if ( town - > visitingHero - > tempOwner = = playerID & & ! vstd : : contains ( wanderingHeroes , town - > visitingHero ) ) // our hero
2013-02-23 15:22:23 +03:00
wanderingHeroes . push_back ( town - > visitingHero ) ;
2010-01-02 03:48:44 +02:00
}
2012-06-13 16:04:06 +03:00
adventureInt - > heroList . update ( ) ;
2013-06-26 14:18:27 +03:00
adventureInt - > updateNextHero ( nullptr ) ;
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 ) ;
castleInt - > garr - > setArmy ( town - > getUpperArmy ( ) , 0 ) ;
castleInt - > garr - > setArmy ( town - > visitingHero , 1 ) ;
castleInt - > garr - > recreateSlots ( ) ;
castleInt - > heroes - > update ( ) ;
2008-08-15 15:11:42 +03:00
}
2018-07-25 00:36:48 +02:00
for ( auto isa : GH . listInt )
2011-07-22 19:22:22 +03:00
{
2018-07-25 00:36:48 +02:00
CKingdomInterface * ki = dynamic_cast < CKingdomInterface * > ( isa . get ( ) ) ;
2011-07-22 19:22:22 +03:00
if ( ki )
{
ki - > townChanged ( town ) ;
ki - > updateGarrisons ( ) ;
}
}
2010-01-02 03:48:44 +02:00
GH . totalRedraw ( ) ;
}
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 )
2012-12-11 15:12:46 +03:00
updateInfo ( object ) ;
2009-04-14 15:47:09 +03:00
2016-10-30 11:00:25 +02:00
for ( auto & elem : GH . listInt )
2008-01-31 23:35:30 +02:00
{
2018-07-25 00:36:48 +02:00
CGarrisonHolder * cgh = dynamic_cast < CGarrisonHolder * > ( elem . get ( ) ) ;
2012-06-13 16:04:06 +03:00
if ( cgh )
2010-07-22 03:32:45 +03:00
cgh - > updateGarrisons ( ) ;
2012-06-13 16:04:06 +03:00
2018-07-25 00:36:48 +02:00
if ( CTradeWindow * cmw = dynamic_cast < CTradeWindow * > ( elem . get ( ) ) )
2010-05-26 12:47:53 +03:00
{
2016-10-30 11:00:25 +02:00
if ( vstd : : contains ( objs , cmw - > hero ) )
2010-05-26 12:47:53 +03:00
cmw - > garrisonChanged ( ) ;
2010-02-06 15:27:58 +02:00
}
2008-01-31 23:35:30 +02:00
}
2008-05-18 20:33:39 +03:00
2010-01-02 03:48:44 +02:00
GH . totalRedraw ( ) ;
2008-05-18 20:33:39 +03:00
}
2010-01-02 03:48:44 +02:00
2012-12-11 15:12:46 +03:00
void CPlayerInterface : : garrisonChanged ( const CGObjectInstance * obj )
{
garrisonsChanged ( std : : vector < const CGObjectInstance * > ( 1 , obj ) ) ;
}
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 ;
2010-01-02 03:48:44 +02:00
switch ( buildingID )
{
2013-02-11 22:11:34 +03:00
case BuildingID : : FORT : case BuildingID : : CITADEL : case BuildingID : : CASTLE :
case BuildingID : : VILLAGE_HALL : case BuildingID : : TOWN_HALL : case BuildingID : : CITY_HALL : case BuildingID : : CAPITOL :
case BuildingID : : RESOURCE_SILO :
2010-03-21 00:17:19 +02:00
updateInfo ( town ) ;
break ;
2010-01-02 03:48:44 +02:00
}
2010-03-21 00:17:19 +02:00
2016-10-30 11:00:25 +02:00
if ( castleInt )
2010-01-02 03:48:44 +02:00
{
2016-08-24 05:19:25 +02:00
castleInt - > townlist - > update ( town ) ;
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 ;
}
}
2010-01-02 03:48:44 +02:00
}
2012-06-13 16:04:06 +03:00
adventureInt - > townList . update ( town ) ;
2008-05-18 20:33:39 +03:00
}
2010-01-02 03:48:44 +02:00
2013-09-14 22:09:35 +03:00
void CPlayerInterface : : battleStartBefore ( const CCreatureSet * army1 , const CCreatureSet * army2 , int3 tile , const CGHeroInstance * hero1 , const CGHeroInstance * hero2 )
{
//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 ( ) ;
}
2010-12-04 21:15:20 +02:00
void CPlayerInterface : : battleStart ( const CCreatureSet * army1 , const CCreatureSet * army2 , int3 tile , const CGHeroInstance * hero1 , const CGHeroInstance * hero2 , bool side )
2008-05-18 20:33:39 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2016-10-30 11:00:25 +02:00
if ( settings [ " adventure " ] [ " quickCombat " ] . Bool ( ) )
2013-06-23 14:25:48 +03:00
{
2017-01-17 13:17:37 +02:00
autofightingAI = CDynLibHandler : : getNewBattleAI ( settings [ " server " ] [ " friendlyAI " ] . String ( ) ) ;
2022-12-07 21:50:45 +02:00
autofightingAI - > initBattleInterface ( env , cb ) ;
2013-06-23 14:25:48 +03:00
autofightingAI - > battleStart ( army1 , army2 , int3 ( 0 , 0 , 0 ) , hero1 , hero2 , side ) ;
isAutoFightOn = true ;
cb - > registerBattleInterface ( autofightingAI ) ;
2016-01-10 17:00:24 +02:00
// Player shouldn't be able to move on adventure map if quick combat is going
adventureInt - > quickCombatLock ( ) ;
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
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
void CPlayerInterface : : battleUnitsChanged ( const std : : vector < UnitChanges > & units , const std : : vector < CustomEffectInfo > & customEffects )
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 :
{
2022-11-20 19:11:34 +02:00
const CStack * stack = cb - > 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 :
{
const CStack * unit = cb - > battleGetStackByID ( info . id ) ;
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
}
2017-07-20 06:08:49 +02:00
2022-11-24 16:30:04 +02:00
battleInt - > effectsController - > displayCustomEffects ( customEffects ) ;
2010-01-02 03:48:44 +02:00
}
2017-07-20 06:08:49 +02:00
void CPlayerInterface : : battleObstaclesChanged ( 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 ;
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 )
{
auto instance = cb - > battleGetObstacleByID ( change . id ) ;
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 ) ;
}
}
2010-02-19 18:02:34 +02:00
2022-12-01 22:06:42 +02:00
if ( ! newObstacles . empty ( ) )
battleInt - > obstaclePlaced ( newObstacles ) ;
battleInt - > fieldController - > redrawBackgroundWithHexes ( ) ;
2008-05-18 20:33:39 +03:00
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : battleCatapultAttacked ( 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
2011-09-24 04:15:36 +03:00
void CPlayerInterface : : battleNewRound ( int round ) //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
2010-01-02 03:48:44 +02:00
battleInt - > newRound ( round ) ;
2008-05-31 23:37:54 +03:00
}
2010-01-02 03:48:44 +02:00
2013-05-09 14:09:23 +03:00
void CPlayerInterface : : actionStarted ( 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
2013-05-09 14:09:23 +03:00
curAction = new BattleAction ( action ) ;
battleInt - > startAction ( curAction ) ;
2008-05-18 20:33:39 +03:00
}
2010-01-02 03:48:44 +02:00
2013-05-09 14:09:23 +03:00
void CPlayerInterface : : actionFinished ( 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
2013-05-09 14:09:23 +03:00
battleInt - > endAction ( curAction ) ;
2010-01-02 03:48:44 +02:00
delete curAction ;
2013-06-26 14:18:27 +03:00
curAction = nullptr ;
2008-05-27 16:16:35 +03:00
}
2008-08-13 03:44:31 +03:00
2010-12-23 22:18:10 +02:00
BattleAction CPlayerInterface : : activeStack ( const CStack * stack ) //called when it's turn of that stack
2008-08-13 03:44:31 +03:00
{
2012-04-06 18:02:15 +03:00
THREAD_CREATED_BY_CLIENT ;
2017-08-11 13:38:10 +02:00
logGlobal - > trace ( " Awaiting command for %s " , stack - > nodeName ( ) ) ;
2015-10-06 02:46:35 +02:00
auto stackId = stack - > ID ;
auto stackName = stack - > nodeName ( ) ;
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
{
auto ret = autofightingAI - > activeStack ( stack ) ;
2022-05-15 11:59:56 +02:00
if ( cb - > battleIsFinished ( ) )
{
return BattleAction : : makeDefend ( stack ) ; // battle finished with spellcast
}
2016-10-30 11:00:25 +02:00
if ( isAutoFightOn )
2013-06-23 14:25:48 +03:00
{
return ret ;
}
}
cb - > unregisterBattleInterface ( autofightingAI ) ;
2013-06-23 22:35:54 +03:00
autofightingAI . reset ( ) ;
2013-06-23 14:25:48 +03:00
}
2022-12-09 13:26:17 +02:00
BattleInterface * b = battleInt ;
2012-04-03 02:23:14 +03:00
2022-05-15 11:59:56 +02:00
if ( ! b )
{
return BattleAction : : makeDefend ( stack ) ; // probably battle is finished already
}
2022-12-09 13:26:17 +02:00
if ( BattleInterface : : givenCommand . get ( ) )
2014-01-14 17:34:08 +03:00
{
2017-08-10 18:39:27 +02:00
logGlobal - > error ( " Command buffer must be clean! (we don't want to use old command) " ) ;
2022-12-09 13:26:17 +02:00
vstd : : clear_pointer ( BattleInterface : : givenCommand . data ) ;
2014-01-14 17:34:08 +03:00
}
2010-01-02 03:48:44 +02:00
{
boost : : unique_lock < boost : : recursive_mutex > un ( * pim ) ;
2010-12-23 22:18:10 +02:00
b - > stackActivated ( stack ) ;
2011-07-06 20:00:45 +03:00
//Regeneration & mana drain go there
2010-01-02 03:48:44 +02:00
}
//wait till BattleInterface sets its command
2022-12-09 13:26:17 +02:00
boost : : unique_lock < boost : : mutex > lock ( BattleInterface : : givenCommand . mx ) ;
while ( ! BattleInterface : : givenCommand . data )
2012-02-20 00:03:43 +03:00
{
2022-12-09 13:26:17 +02:00
BattleInterface : : givenCommand . cond . wait ( lock ) ;
2016-10-30 11:00:25 +02:00
if ( ! battleInt ) //battle ended while we were waiting for movement (eg. because of spell)
2012-02-20 00:03:43 +03:00
throw boost : : thread_interrupted ( ) ; //will shut the thread peacefully
}
2010-01-02 03:48:44 +02:00
//tidy up
2022-12-09 13:26:17 +02:00
BattleAction ret = * ( BattleInterface : : givenCommand . data ) ;
vstd : : clear_pointer ( BattleInterface : : givenCommand . data ) ;
2016-01-22 22:30:24 +02:00
2017-07-20 06:08:49 +02:00
if ( ret . actionType = = EActionType : : CANCEL )
2015-10-06 02:46:35 +02:00
{
2017-07-20 06:08:49 +02:00
if ( stackId ! = ret . stackNumber )
2015-10-08 07:15:29 +02:00
logGlobal - > error ( " Not current active stack action canceled " ) ;
2017-08-11 13:38:10 +02:00
logGlobal - > trace ( " Canceled command for %s " , stackName ) ;
2015-10-06 02:46:35 +02:00
}
2015-10-08 07:15:29 +02:00
else
2017-08-11 13:38:10 +02:00
logGlobal - > trace ( " Giving command for %s " , stackName ) ;
2016-01-22 22:30:24 +02:00
2010-01-02 03:48:44 +02:00
return ret ;
}
2009-05-19 21:23:04 +03:00
2010-12-04 21:44:23 +02:00
void CPlayerInterface : : battleEnd ( const BattleResult * br )
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
{
2022-12-09 13:26:17 +02:00
GH . pushIntT < BattleResultWindow > ( * br , * this ) ;
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 ( ) ;
2022-09-30 14:06:50 +02:00
adventureInt - > quickCombatUnlock ( ) ;
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 ;
2010-01-02 03:48:44 +02:00
battleInt - > battleFinished ( * br ) ;
2022-09-30 14:06:50 +02:00
adventureInt - > quickCombatUnlock ( ) ;
2009-05-19 21:23:04 +03:00
}
2010-01-02 03:48:44 +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
void CPlayerInterface : : battleLogMessage ( const std : : vector < MetaString > & lines )
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
BATTLE_EVENT_POSSIBLE_RETURN ;
battleInt - > displayBattleLog ( lines ) ;
}
2011-12-22 16:05:19 +03:00
void CPlayerInterface : : battleStackMoved ( const CStack * stack , std : : vector < BattleHex > dest , int distance )
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
2011-08-01 20:36:18 +03:00
battleInt - > stackMoved ( stack , dest , distance ) ;
2010-01-02 03:48:44 +02:00
}
2010-12-04 21:44:23 +02:00
void CPlayerInterface : : battleSpellCast ( 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 ) ;
}
2010-12-04 21:44:23 +02:00
void CPlayerInterface : : battleStacksEffectsSet ( 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 ) ;
}
2011-10-08 16:02:58 +03:00
void CPlayerInterface : : battleTriggerEffect ( const BattleTriggerEffect & bte )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-06-23 14:25:48 +03:00
//TODO why is this different (no return on LOPLINT != this) ?
RETURN_IF_QUICK_COMBAT ;
2022-11-24 16:30:04 +02:00
battleInt - > effectsController - > battleTriggerEffect ( bte ) ;
2011-10-08 16:02:58 +03: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
void CPlayerInterface : : battleStacksAttacked ( const std : : vector < BattleStackAttacked > & bsa )
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
{
2017-07-20 06:08:49 +02:00
const CStack * defender = cb - > battleGetStackByID ( elem . stackAttacked , false ) ;
const CStack * attacker = cb - > battleGetStackByID ( elem . attackerID , false ) ;
if ( elem . isEffect ( ) )
2010-01-02 03:48:44 +02:00
{
2017-07-20 06:08:49 +02:00
if ( defender & & ! elem . isSecondary ( ) )
2022-11-24 16:30:04 +02:00
battleInt - > effectsController - > displayEffect ( EBattleEffect : : EBattleEffect ( elem . effect ) , defender - > getPosition ( ) ) ;
2010-01-02 03:48:44 +02:00
}
2017-07-20 06:08:49 +02:00
if ( elem . isSpell ( ) )
2014-11-27 23:36:14 +02:00
{
2017-07-20 06:08:49 +02:00
if ( defender )
battleInt - > displaySpellEffect ( elem . spellID , defender - > getPosition ( ) ) ;
2014-11-27 23:36:14 +02:00
}
2013-08-09 20:37:41 +03:00
//FIXME: why action is deleted during enchanter cast?
bool remoteAttack = false ;
2017-07-20 06:08:49 +02:00
if ( LOCPLINT - > curAction )
remoteAttack | = LOCPLINT - > curAction - > actionType ! = EActionType : : WALK_AND_ATTACK ;
2013-08-09 20:37:41 +03:00
StackAttackedInfo to_put = { defender , elem . damageAmount , elem . killedAmount , attacker , remoteAttack , elem . killed ( ) , elem . willRebirth ( ) , elem . cloneKilled ( ) } ;
2010-01-02 03:48:44 +02:00
arg . push_back ( to_put ) ;
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
}
2017-07-04 13:24:46 +02:00
void CPlayerInterface : : battleAttack ( 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
2010-01-02 03:48:44 +02:00
assert ( curAction ) ;
2017-07-04 13:24:46 +02:00
const CStack * attacker = cb - > battleGetStackByID ( ba - > stackAttacking ) ;
if ( ! attacker )
{
logGlobal - > error ( " Attacking stack not found " ) ;
return ;
}
if ( ba - > lucky ( ) ) //lucky hit
2010-01-02 03:48:44 +02:00
{
2022-11-18 17:54:10 +02:00
battleInt - > controlPanel - > console - > addText ( attacker - > formatGeneralMessage ( - 45 ) ) ;
2022-11-24 16:30:04 +02:00
battleInt - > effectsController - > displayEffect ( EBattleEffect : : GOOD_LUCK , soundBase : : GOODLUCK , attacker - > getPosition ( ) ) ;
2013-05-04 16:14:23 +03:00
}
2017-07-04 13:24:46 +02:00
if ( ba - > unlucky ( ) ) //unlucky hit
2013-05-04 16:14:23 +03:00
{
2022-11-18 17:54:10 +02:00
battleInt - > controlPanel - > console - > addText ( attacker - > formatGeneralMessage ( - 44 ) ) ;
2022-11-24 16:30:04 +02:00
battleInt - > effectsController - > displayEffect ( EBattleEffect : : BAD_LUCK , soundBase : : BADLUCK , attacker - > getPosition ( ) ) ;
2010-01-02 03:48:44 +02:00
}
2017-07-04 13:24:46 +02:00
if ( ba - > deathBlow ( ) )
2011-07-06 20:00:45 +03:00
{
2022-11-18 17:54:10 +02:00
battleInt - > controlPanel - > console - > addText ( attacker - > formatGeneralMessage ( 365 ) ) ;
2017-07-04 13:24:46 +02:00
for ( auto & elem : ba - > bsa )
2011-07-06 20:00:45 +03:00
{
2013-06-29 16:05:48 +03:00
const CStack * attacked = cb - > battleGetStackByID ( elem . stackAttacked ) ;
2022-11-24 16:30:04 +02:00
battleInt - > effectsController - > displayEffect ( EBattleEffect : : DEATH_BLOW , attacked - > getPosition ( ) ) ;
2011-07-06 20:00:45 +03:00
}
2013-05-04 16:14:23 +03:00
CCS - > soundh - > playSound ( soundBase : : deathBlow ) ;
2011-07-06 20:00:45 +03:00
}
2017-07-20 06:08:49 +02:00
2022-11-24 16:30:04 +02:00
battleInt - > effectsController - > displayCustomEffects ( ba - > customEffects ) ;
2017-07-20 06:08:49 +02:00
2014-09-23 17:30:36 +03:00
battleInt - > waitForAnims ( ) ;
2009-02-14 16:37:13 +02:00
2017-07-20 06:08:49 +02:00
auto actionTarget = curAction - > getTarget ( cb . get ( ) ) ;
if ( actionTarget . empty ( ) | | ( actionTarget . size ( ) < 2 & & ! ba - > shot ( ) ) )
{
logNetwork - > error ( " Invalid current action: no destination. " ) ;
return ;
}
2017-07-04 13:24:46 +02:00
if ( ba - > shot ( ) )
2009-02-14 16:37:13 +02:00
{
2017-07-04 13:24:46 +02:00
for ( auto & elem : ba - > bsa )
2010-12-23 22:18:10 +02:00
{
2017-07-04 13:24:46 +02:00
if ( ! elem . isSecondary ( ) ) //display projectile only for primary target
2011-07-02 20:40:33 +03:00
{
2013-06-29 16:05:48 +03:00
const CStack * attacked = cb - > battleGetStackByID ( elem . stackAttacked ) ;
2017-07-20 06:08:49 +02:00
battleInt - > stackAttacking ( attacker , attacked - > getPosition ( ) , attacked , true ) ;
2011-07-02 20:40:33 +03:00
}
2010-12-23 22:18:10 +02:00
}
2010-01-02 03:48:44 +02:00
}
else
2011-07-06 20:00:45 +03:00
{
2017-07-20 06:08:49 +02:00
auto attackTarget = actionTarget . at ( 1 ) . hexValue ;
//TODO: use information from BattleAttack but not curAction
2010-01-02 03:48:44 +02:00
int shift = 0 ;
2017-07-20 06:08:49 +02:00
if ( ba - > counter ( ) & & BattleHex : : mutualPosition ( attackTarget , attacker - > getPosition ( ) ) < 0 )
2010-01-02 03:48:44 +02:00
{
2017-07-20 06:08:49 +02:00
int distp = BattleHex : : getDistance ( attackTarget + 1 , attacker - > getPosition ( ) ) ;
int distm = BattleHex : : getDistance ( attackTarget - 1 , attacker - > getPosition ( ) ) ;
2010-07-29 15:54:30 +03:00
2017-07-04 13:24:46 +02:00
if ( distp < distm )
2010-01-02 03:48:44 +02:00
shift = 1 ;
else
shift = - 1 ;
}
2022-05-14 16:38:06 +02:00
if ( ! ba - > bsa . empty ( ) )
{
const CStack * attacked = cb - > battleGetStackByID ( ba - > bsa . begin ( ) - > stackAttacked ) ;
battleInt - > stackAttacking ( attacker , ba - > counter ( ) ? BattleHex ( attackTarget + shift ) : attackTarget , attacked , false ) ;
}
2009-02-14 16:37:13 +02:00
}
2015-01-13 21:57:41 +02:00
2014-11-28 01:47:14 +02:00
//battleInt->waitForAnims(); //FIXME: freeze
2015-01-13 21:57:41 +02:00
2017-07-04 13:24:46 +02:00
if ( ba - > spellLike ( ) )
2014-11-27 23:36:14 +02:00
{
2017-07-20 06:08:49 +02:00
//TODO: use information from BattleAttack but not curAction
auto destination = actionTarget . at ( 0 ) . hexValue ;
2015-01-13 21:57:41 +02:00
//display hit animation
SpellID spellID = ba - > spellID ;
2017-07-20 06:08:49 +02:00
battleInt - > displaySpellHit ( spellID , destination ) ;
2014-11-27 23:36:14 +02:00
}
2008-08-13 03:44:31 +03:00
}
2012-05-05 00:16:39 +03:00
2016-02-13 16:40:31 +02:00
void CPlayerInterface : : battleGateStateChanged ( 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
}
2011-08-25 18:24:37 +03:00
void CPlayerInterface : : yourTacticPhase ( int distance )
{
2012-04-06 18:02:15 +03:00
THREAD_CREATED_BY_CLIENT ;
2011-08-26 00:08:53 +03:00
while ( battleInt & & battleInt - > tacticsMode )
2011-08-25 18:24:37 +03:00
boost : : this_thread : : sleep ( boost : : posix_time : : millisec ( 1 ) ) ;
}
2012-06-13 16:04:06 +03:00
void CPlayerInterface : : showComp ( const Component & comp , std : : string message )
2008-08-13 03:44:31 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
waitWhileDialog ( ) ; //Fix for mantis #98
2010-01-02 03:48:44 +02:00
2010-12-19 16:39:56 +02:00
CCS - > soundh - > playSoundFromSet ( CCS - > soundh - > pickupSounds ) ;
2012-06-13 16:04:06 +03:00
adventureInt - > infoBar . showComponent ( comp , message ) ;
2008-08-17 12:11:16 +03:00
}
2010-01-02 03:48:44 +02:00
2018-04-07 13:34:11 +02:00
void CPlayerInterface : : showInfoDialog ( const std : : string & text , const std : : vector < Component > & components , int soundID )
2010-01-02 03:48:44 +02:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2012-09-28 18:46:09 +03:00
if ( settings [ " session " ] [ " autoSkip " ] . Bool ( ) & & ! LOCPLINT - > shiftPressed ( ) )
{
return ;
}
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 ) ) ;
2010-01-02 03:48:44 +02:00
showInfoDialog ( text , intComps , soundID ) ;
2012-09-28 18:46:09 +03:00
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
2012-09-28 18:46:09 +03:00
if ( settings [ " session " ] [ " autoSkip " ] . Bool ( ) & & ! LOCPLINT - > shiftPressed ( ) )
{
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
2016-10-30 11:00:25 +02:00
if ( makingTurn & & GH . listInt . size ( ) & & 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
2010-01-02 03:48:44 +02:00
GH . pushInt ( 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
2013-11-30 12:43:31 +03:00
std : : string str ;
text . toString ( str ) ;
2018-04-07 13:34:11 +02:00
showInfoDialog ( 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
std : : vector < std : : pair < std : : string , CFunctionList < void ( ) > > > pom ;
pom . push_back ( std : : pair < std : : string , CFunctionList < void ( ) > > ( " IOKAY.DEF " , 0 ) ) ;
2016-10-30 11:00:25 +02:00
if ( cancel )
2009-05-19 21:23:04 +03:00
{
2010-01-02 03:48:44 +02:00
pom . push_back ( std : : pair < std : : string , CFunctionList < void ( ) > > ( " 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 ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < CSelWindow > ( text , playerID , charperline , intComps , pom , askID ) ;
2010-01-02 03:48:44 +02:00
intComps [ 0 ] - > clickLeft ( true , false ) ;
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 ;
GH . pushInt ( wnd ) ;
}
2013-06-29 16:05:48 +03:00
void CPlayerInterface : : tileRevealed ( const std : : unordered_set < int3 , ShashInt3 > & pos )
2008-08-27 13:19:18 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-12-04 06:54:02 +03:00
//FIXME: wait for dialog? Magi hut/eye would benefit from this but may break other areas
2016-10-30 11:00:25 +02:00
for ( auto & po : pos )
2013-06-29 16:05:48 +03:00
adventureInt - > minimap . showTile ( po ) ;
2016-10-30 11:00:25 +02:00
if ( ! pos . empty ( ) )
2010-01-02 03:48:44 +02:00
GH . totalRedraw ( ) ;
2008-08-27 13:19:18 +03:00
}
2013-06-29 16:05:48 +03:00
void CPlayerInterface : : tileHidden ( const std : : unordered_set < int3 , ShashInt3 > & pos )
2008-08-27 13:19:18 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2016-10-30 11:00:25 +02:00
for ( auto & po : pos )
2013-06-29 16:05:48 +03:00
adventureInt - > minimap . hideTile ( po ) ;
2016-10-30 11:00:25 +02:00
if ( ! pos . empty ( ) )
2010-06-06 09:05:39 +03:00
GH . totalRedraw ( ) ;
2008-09-07 06:38:37 +03:00
}
2010-01-02 03:48:44 +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 ) ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < 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
{
2018-07-25 00:36:48 +02:00
CFortScreen * fs = dynamic_cast < CFortScreen * > ( GH . topInt ( ) . get ( ) ) ;
2016-10-30 11:00:25 +02:00
if ( fs )
2011-04-07 20:54:08 +03:00
fs - > creaturesChanged ( ) ;
2011-07-22 19:22:22 +03:00
2018-07-25 00:36:48 +02:00
for ( auto isa : GH . listInt )
2011-07-22 19:22:22 +03:00
{
2018-07-25 00:36:48 +02:00
CKingdomInterface * ki = dynamic_cast < CKingdomInterface * > ( isa . get ( ) ) ;
2011-07-22 19:22:22 +03:00
if ( ki & & townObj )
ki - > townChanged ( townObj ) ;
}
2010-01-02 03:48:44 +02:00
}
2022-09-24 16:29:29 +02:00
else if ( town & & GH . listInt . size ( ) & & ( 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
{
2018-07-25 00:36:48 +02:00
CRecruitmentWindow * crw = dynamic_cast < CRecruitmentWindow * > ( GH . topInt ( ) . get ( ) ) ;
2016-10-30 11:00:25 +02:00
if ( crw & & crw - > dwelling = = town )
2012-06-22 14:40:16 +03:00
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 ;
2016-10-30 11:00:25 +02:00
if ( bonus . type = = Bonus : : NONE )
2012-04-06 18:02:15 +03:00
return ;
2010-03-21 00:17:19 +02:00
updateInfo ( hero ) ;
2011-03-22 15:19:07 +02:00
if ( ( bonus . type = = Bonus : : FLYING_MOVEMENT | | bonus . type = = Bonus : : WATER_WALKING ) & & ! gain )
2010-05-15 18:00:19 +03:00
{
//recalculate paths because hero has lost bonus influencing pathfinding
2010-05-16 16:42:19 +03:00
eraseCurrentPathOf ( hero , false ) ;
2010-05-15 18:00:19 +03:00
}
2008-09-07 06:38:37 +03:00
}
2010-01-02 03:48:44 +02:00
template < typename Handler > void CPlayerInterface : : serializeTempl ( Handler & h , const int version )
2008-09-07 06:38:37 +03:00
{
2017-07-31 15:35:42 +02:00
h & wanderingHeroes ;
h & towns ;
h & sleepingHeroes ;
2013-05-09 14:09:23 +03:00
std : : map < const CGHeroInstance * , int3 > pathsMap ; //hero -> dest
2016-10-30 11:00:25 +02:00
if ( h . saving )
2011-09-24 19:46:23 +03:00
{
2016-10-30 11:00:25 +02:00
for ( auto & p : paths )
2013-08-18 18:46:28 +03:00
{
2016-10-30 11:00:25 +02:00
if ( p . second . nodes . size ( ) )
2013-08-18 18:46:28 +03:00
pathsMap [ p . first ] = p . second . endPos ( ) ;
else
2017-08-11 13:38:10 +02:00
logGlobal - > debug ( " %s has assigned an empty path! Ignoring it... " , p . first - > name ) ;
2013-08-18 18:46:28 +03:00
}
2013-05-09 14:09:23 +03:00
h & pathsMap ;
2011-09-24 19:46:23 +03:00
}
2011-10-10 23:37:11 +03:00
else
{
2013-05-09 14:09:23 +03:00
h & pathsMap ;
2016-10-30 11:00:25 +02:00
if ( cb )
for ( auto & p : pathsMap )
2015-01-13 21:57:41 +02:00
{
CGPath path ;
2015-11-02 10:14:32 +02:00
cb - > getPathsInfo ( p . first ) - > getPath ( path , p . second ) ;
2015-01-13 21:57:41 +02:00
paths [ p . first ] = path ;
2017-08-11 19:03:05 +02:00
logGlobal - > trace ( " Restored path for hero %s leading to %s with %d nodes " , p . first - > nodeName ( ) , p . second . toString ( ) , path . nodes . size ( ) ) ;
2015-01-13 21:57:41 +02:00
}
2011-10-10 23:37:11 +03:00
}
2013-05-09 14:09:23 +03:00
h & spellbookSettings ;
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 ;
2010-01-02 03:48:44 +02:00
serializeTempl ( 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 ;
2010-01-02 03:48:44 +02:00
serializeTempl ( h , version ) ;
firstCall = - 1 ;
}
2014-06-18 13:31:11 +03:00
void CPlayerInterface : : moveHero ( const CGHeroInstance * h , 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
2011-09-24 19:46:23 +03:00
if ( adventureInt & & adventureInt - > isHeroSleeping ( h ) )
{
2014-08-03 14:16:19 +03:00
adventureInt - > sleepWake - > clickLeft ( true , false ) ;
adventureInt - > sleepWake - > clickLeft ( false , true ) ;
2012-02-16 20:10:58 +03:00
//could've just called
2011-09-24 19:46:23 +03:00
//adventureInt->fsleepWake();
//but no authentic button click/sound ;-)
}
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
bool CPlayerInterface : : shiftPressed ( ) const
2008-09-07 06:38:37 +03:00
{
2014-05-21 22:14:05 +03:00
return isShiftKeyDown ( ) ;
2010-01-02 03:48:44 +02:00
}
2008-09-07 06:38:37 +03:00
2010-07-21 13:09:29 +03:00
bool CPlayerInterface : : altPressed ( ) const
{
2014-05-21 22:14:05 +03:00
return isAltKeyDown ( ) ;
2010-07-21 13:09:29 +03: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
2016-10-30 11:00:25 +02:00
if ( stillMoveHero . get ( ) = = DURING_MOVE & & adventureInt - > terrain . currentPath & & adventureInt - > terrain . currentPath - > 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 ) ;
2010-01-02 03:48:44 +02:00
GH . pushInt ( cgw ) ;
}
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 .
*/
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
void CPlayerInterface : : showArtifactAssemblyDialog ( const Artifact * artifact , const Artifact * assembledArtifact , CFunctionList < bool ( ) > onYes )
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
std : : string text = artifact - > getDescription ( ) ;
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...
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
text + = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 732 ] ) % assembledArtifact - > getName ( ) ) ;
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
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 ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < CExchangeWindow > ( hero1 , hero2 , query ) ;
2010-01-02 03:48:44 +02:00
}
2009-05-06 13:14:48 +03:00
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 ;
2010-01-02 03:48:44 +02:00
//redraw minimap if owner changed
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 ) ;
2010-01-02 03:48:44 +02:00
std : : set < int3 > pos = obj - > getBlockedPos ( ) ;
2021-03-23 16:47:07 +02:00
for ( auto & po : pos )
2009-08-18 14:49:34 +03:00
{
2021-03-23 16:47:07 +02:00
if ( cb - > isVisible ( po ) )
2013-06-29 16:05:48 +03:00
adventureInt - > minimap . showTile ( po ) ;
2009-08-18 14:49:34 +03:00
}
2021-03-23 16:47:07 +02:00
if ( obj - > ID = = Obj : : TOWN )
2011-03-22 15:19:07 +02:00
{
2021-03-23 16:47:07 +02:00
if ( obj - > tempOwner = = playerID )
2010-02-26 13:18:09 +02:00
towns . push_back ( static_cast < const CGTownInstance * > ( obj ) ) ;
else
towns - = obj ;
2021-03-23 16:47:07 +02:00
2012-06-13 16:04:06 +03:00
adventureInt - > townList . update ( ) ;
2021-10-27 00:37:45 +02:00
adventureInt - > minimap . update ( ) ;
2011-03-22 15:19:07 +02:00
}
2010-02-26 13:18:09 +02:00
assert ( cb - > getTownsInfo ( ) . size ( ) = = towns . 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
{
2018-01-05 19:21:07 +02:00
if ( ! wanderingHeroes . size ( ) )
{
std : : vector < const CGHeroInstance * > heroes = cb - > getHeroesInfo ( ) ;
for ( auto & hero : heroes )
2011-10-10 23:37:11 +03:00
{
2018-01-05 19:21:07 +02:00
if ( ! hero - > inTownGarrison )
wanderingHeroes . push_back ( 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
2018-01-05 19:21:07 +02:00
if ( ! towns . size ( ) )
towns = cb - > getTownsInfo ( ) ;
2011-10-04 22:43:49 +03:00
2018-01-05 19:21:07 +02:00
if ( adventureInt )
2013-06-26 14:18:27 +03:00
adventureInt - > updateNextHero ( 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 ) ;
} ;
GH . pushIntT < 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 ( ) ;
2010-01-02 03:48:44 +02:00
std : : vector < si32 > cost ;
obj - > getBoatCost ( cost ) ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < 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 ( ) ;
2010-02-20 15:24:38 +02:00
adventureInt - > centerOn ( pos ) ;
2016-10-30 11:00:25 +02:00
if ( focusTime )
2009-02-09 18:18:48 +02:00
{
2013-12-04 06:54:02 +03:00
GH . totalRedraw ( ) ;
2014-06-21 16:41:05 +03:00
{
auto unlockPim = vstd : : makeUnlockGuard ( * pim ) ;
IgnoreEvents ignore ( * this ) ;
SDL_Delay ( focusTime ) ;
}
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 ;
2017-09-13 02:35:58 +02:00
if ( LOCPLINT - > cb - > getCurrentPlayer ( ) = = playerID & & obj - > getRemovalSound ( ) )
{
waitWhileDialog ( ) ;
CCS - > soundh - > playSound ( obj - > getRemovalSound ( ) . get ( ) ) ;
2014-07-30 20:49:19 +03:00
}
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 ) ;
}
2008-10-26 22:58:34 +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)
boost : : unique_lock < boost : : mutex > lock ( eventsM ) ; //TODO: copied from yourTurn, no idea if it's needed
LOCPLINT = this ;
GH . curInt = this ;
adventureInt - > selection = nullptr ;
adventureInt - > setPlayer ( playerID ) ;
std : : string msg = CGI - > generaltexth - > localizedTexts [ " adventureMap " ] [ " playerAttacked " ] . String ( ) ;
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
bool CPlayerInterface : : ctrlPressed ( ) const
2008-10-26 22:58:34 +02:00
{
2014-05-21 22:14:05 +03:00
return isCtrlKeyDown ( ) ;
2008-10-26 22:58:34 +02:00
}
2014-09-21 16:42:08 +03:00
const CArmedInstance * CPlayerInterface : : getSelection ( )
{
return currentSelection ;
}
void CPlayerInterface : : setSelection ( const CArmedInstance * obj )
{
currentSelection = obj ;
2017-09-13 02:35:58 +02:00
updateAmbientSounds ( true ) ;
2014-09-21 16:42:08 +03:00
}
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 ) ;
GH . pushInt ( dialogs . front ( ) ) ;
dialogs . pop_front ( ) ;
2009-02-09 18:18:48 +02:00
}
2010-01-02 03:48:44 +02:00
2010-06-01 02:53:35 +03:00
//in some conditions we may receive calls before selection is initialized - we must ignore them
2017-06-03 07:25:10 +02:00
if ( adventureInt & & GH . topInt ( ) = = adventureInt
& & ( ! adventureInt - > selection & & ! settings [ " session " ] [ " spectate " ] . Bool ( ) ) )
2011-05-29 17:06:52 +03:00
{
2010-06-01 02:53:35 +03:00
return ;
2011-05-29 17:06:52 +03:00
}
2010-06-01 02:53:35 +03:00
2011-04-13 22:52:56 +03:00
// Handles mouse and key input
GH . updateTime ( ) ;
GH . handleEvents ( ) ;
2017-09-18 07:27:03 +02:00
if ( ! adventureInt | | adventureInt - > isActive ( ) )
GH . simpleRedraw ( ) ;
else if ( ( adventureInt - > swipeEnabled & & adventureInt - > swipeMovementRequested ) | | adventureInt - > scrollingDir )
GH . totalRedraw ( ) ; //player forces map scrolling though interface is disabled
2010-01-02 03:48:44 +02:00
else
GH . simpleRedraw ( ) ;
}
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
{
2016-10-30 11:00:25 +02:00
if ( is_regular ( 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
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : initMovement ( const TryMoveHero & details , const CGHeroInstance * ho , const int3 & hp )
2008-10-26 22:58:34 +02:00
{
2022-09-18 16:39:10 +02:00
auto subArr = ( CGI - > mh - > ttiles ) [ hp . z ] ;
2022-11-14 12:08:46 +02:00
ho - > isStanding = false ;
2010-01-02 03:48:44 +02:00
2022-11-14 12:08:46 +02:00
int heroWidth = ho - > appearance - > getWidth ( ) ;
int heroHeight = ho - > appearance - > getHeight ( ) ;
2010-01-02 03:48:44 +02:00
2022-11-14 12:08:46 +02:00
int tileMinX = std : : min ( details . start . x , details . end . x ) - heroWidth ;
int tileMaxX = std : : max ( details . start . x , details . end . x ) ;
int tileMinY = std : : min ( details . start . y , details . end . y ) - heroHeight ;
int tileMaxY = std : : max ( details . start . y , details . end . y ) ;
2009-07-30 15:49:45 +03:00
2022-11-14 12:08:46 +02:00
// determine tiles on which hero will be visible during movement and add hero as visible object on these tiles where necessary
for ( int tileX = tileMinX ; tileX < = tileMaxX ; + + tileX )
2010-01-02 03:48:44 +02:00
{
2022-11-14 12:08:46 +02:00
for ( int tileY = tileMinY ; tileY < = tileMaxY ; + + tileY )
{
bool heroVisibleHere = false ;
auto & tile = subArr [ tileX ] [ tileY ] ;
2009-08-13 04:03:11 +03:00
2022-11-14 12:08:46 +02:00
for ( auto const & obj : tile . objects )
{
if ( obj . obj = = ho )
{
heroVisibleHere = true ;
break ;
}
}
2010-01-02 03:48:44 +02:00
2022-11-14 12:08:46 +02:00
if ( ! heroVisibleHere )
{
tile . objects . push_back ( TerrainTileObject ( ho , { 0 , 0 , 32 , 32 } ) ) ;
std : : stable_sort ( tile . objects . begin ( ) , tile . objects . end ( ) , objectBlitOrderSorter ) ;
}
}
2009-08-13 04:03:11 +03:00
}
2009-08-11 10:50:29 +03:00
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : movementPxStep ( const TryMoveHero & details , int i , const int3 & hp , const CGHeroInstance * ho )
2009-08-04 02:53:18 +03:00
{
2022-11-14 12:08:46 +02:00
auto subArr = ( CGI - > mh - > ttiles ) [ hp . z ] ;
2010-01-02 03:48:44 +02:00
2022-11-14 12:08:46 +02:00
int heroWidth = ho - > appearance - > getWidth ( ) ;
int heroHeight = ho - > appearance - > getHeight ( ) ;
2010-01-02 03:48:44 +02:00
2022-11-14 12:08:46 +02:00
int tileMinX = std : : min ( details . start . x , details . end . x ) - heroWidth ;
int tileMaxX = std : : max ( details . start . x , details . end . x ) ;
int tileMinY = std : : min ( details . start . y , details . end . y ) - heroHeight ;
int tileMaxY = std : : max ( details . start . y , details . end . y ) ;
2010-01-02 03:48:44 +02:00
2022-11-14 12:08:46 +02:00
std : : shared_ptr < CAnimation > animation = graphics - > getAnimation ( ho ) ;
2010-01-02 03:48:44 +02:00
2022-11-14 12:08:46 +02:00
assert ( animation ) ;
assert ( animation - > size ( 0 ) ! = 0 ) ;
auto image = animation - > getImage ( 0 , 0 ) ;
2009-12-28 06:08:24 +02:00
2022-11-14 12:08:46 +02:00
int heroImageOldX = details . start . x * 32 ;
int heroImageOldY = details . start . y * 32 ;
2009-12-28 06:08:24 +02:00
2022-11-14 12:08:46 +02:00
int heroImageNewX = details . end . x * 32 ;
int heroImageNewY = details . end . y * 32 ;
2009-12-28 06:08:24 +02:00
2022-11-14 12:08:46 +02:00
int heroImageCurrX = heroImageOldX + i * ( heroImageNewX - heroImageOldX ) / 32 ;
int heroImageCurrY = heroImageOldY + i * ( heroImageNewY - heroImageOldY ) / 32 ;
2009-12-28 06:08:24 +02:00
2022-11-14 12:08:46 +02:00
// recompute which part of hero sprite will be visible on each tile at this point of movement animation
for ( int tileX = tileMinX ; tileX < = tileMaxX ; + + tileX )
2010-01-02 03:48:44 +02:00
{
2022-11-14 12:08:46 +02:00
for ( int tileY = tileMinY ; tileY < = tileMaxY ; + + tileY )
{
auto & tile = subArr [ tileX ] [ tileY ] ;
for ( auto & obj : tile . objects )
{
if ( obj . obj = = ho )
{
int tilePosX = tileX * 32 ;
int tilePosY = tileY * 32 ;
2010-01-02 03:48:44 +02:00
2022-11-14 12:08:46 +02:00
obj . rect . x = tilePosX - heroImageCurrX + image - > width ( ) - 32 ;
obj . rect . y = tilePosY - heroImageCurrY + image - > height ( ) - 32 ;
}
}
}
2010-01-02 03:48:44 +02:00
}
2022-11-14 12:08:46 +02:00
adventureInt - > terrain . moveX = ( 32 - i ) * ( heroImageNewX - heroImageOldX ) / 32 ;
adventureInt - > terrain . moveY = ( 32 - i ) * ( heroImageNewY - heroImageOldY ) / 32 ;
2009-12-28 06:08:24 +02:00
}
2010-01-02 03:48:44 +02:00
void CPlayerInterface : : finishMovement ( const TryMoveHero & details , const int3 & hp , const CGHeroInstance * ho )
2009-12-28 06:08:24 +02:00
{
2022-11-14 12:08:46 +02:00
auto subArr = ( CGI - > mh - > ttiles ) [ hp . z ] ;
2009-12-28 06:08:24 +02:00
2022-11-14 12:08:46 +02:00
int heroWidth = ho - > appearance - > getWidth ( ) ;
int heroHeight = ho - > appearance - > getHeight ( ) ;
int tileMinX = std : : min ( details . start . x , details . end . x ) - heroWidth ;
int tileMaxX = std : : max ( details . start . x , details . end . x ) ;
int tileMinY = std : : min ( details . start . y , details . end . y ) - heroHeight ;
int tileMaxY = std : : max ( details . start . y , details . end . y ) ;
// erase hero from all tiles on which he is currently visible
for ( int tileX = tileMinX ; tileX < = tileMaxX ; + + tileX )
2010-01-02 03:48:44 +02:00
{
2022-11-14 12:08:46 +02:00
for ( int tileY = tileMinY ; tileY < = tileMaxY ; + + tileY )
{
auto & tile = subArr [ tileX ] [ tileY ] ;
for ( size_t i = 0 ; i < tile . objects . size ( ) ; + + i )
{
if ( tile . objects [ i ] . obj = = ho )
{
tile . objects . erase ( tile . objects . begin ( ) + i ) ;
break ;
}
}
}
2010-01-02 03:48:44 +02:00
}
2022-11-14 12:08:46 +02:00
// re-add hero to all tiles on which he will still be visible after animation is over
for ( int tileX = details . end . x - heroWidth + 1 ; tileX < = details . end . x ; + + tileX )
2010-01-02 03:48:44 +02:00
{
2022-11-14 12:08:46 +02:00
for ( int tileY = details . end . y - heroHeight + 1 ; tileY < = details . end . y ; + + tileY )
{
auto & tile = subArr [ tileX ] [ tileY ] ;
tile . objects . push_back ( TerrainTileObject ( ho , { 0 , 0 , 32 , 32 } ) ) ;
}
2010-01-02 03:48:44 +02:00
}
2022-11-14 12:08:46 +02:00
// update object list on all tiles that were affected during previous operations
for ( int tileX = tileMinX ; tileX < = tileMaxX ; + + tileX )
2010-01-02 03:48:44 +02:00
{
2022-11-14 12:08:46 +02:00
for ( int tileY = tileMinY ; tileY < = tileMaxY ; + + tileY )
{
auto & tile = subArr [ tileX ] [ tileY ] ;
std : : stable_sort ( tile . objects . begin ( ) , tile . objects . end ( ) , objectBlitOrderSorter ) ;
}
2009-12-28 06:08:24 +02:00
}
2022-11-14 12:08:46 +02:00
//recompute hero sprite positioning using hero's final position
movementPxStep ( details , 32 , hp , ho ) ;
2010-01-02 03:48:44 +02:00
}
2010-01-29 22:52:45 +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
2019-06-01 19:28:21 +02:00
//we assume GH.curInt == LOCPLINT
auto previousInterface = LOCPLINT ; //without multiple player interfaces some of lines below are useless, but for hotseat we wanna swap player interface temporarily
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
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
{
2017-08-13 16:44:41 +02:00
GH . terminate_cond - > setn ( true ) ;
2013-12-20 18:00:48 +03:00
adventureInt - > deactivate ( ) ;
2016-10-30 11:00:25 +02:00
if ( GH . topInt ( ) = = adventureInt )
2013-12-20 18:00:48 +03:00
GH . popInt ( adventureInt ) ;
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
2016-10-30 11:00:25 +02:00
if ( GH . curInt = = this ) GH . curInt = nullptr ;
2010-01-29 22:52:45 +02:00
}
2010-02-01 19:07:46 +02:00
else
{
2016-10-30 11:00:25 +02:00
if ( victoryLossCheckResult . loss ( ) & & cb - > getPlayerStatus ( playerID ) = = EPlayerStatus : : INGAME ) //enemy has lost
2012-04-06 18:02:15 +03:00
{
2013-12-29 14:27:38 +03:00
std : : string str = victoryLossCheckResult . messageToSelf ;
boost : : algorithm : : replace_first ( str , " %s " , CGI - > generaltexth - > capColors [ player . getNum ( ) ] ) ;
2018-04-07 13:34:11 +02:00
showInfoDialog ( str , std : : vector < std : : shared_ptr < CComponent > > ( 1 , std : : make_shared < CComponent > ( CComponent : : flag , player . getNum ( ) , 0 ) ) ) ;
2010-02-01 19:07:46 +02:00
}
}
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
2018-07-25 00:36:48 +02:00
GH . pushIntT < CPuzzleWindow > ( grailPos , ratio ) ;
2010-02-10 04:56:00 +02:00
}
2015-01-13 21:57:41 +02:00
void CPlayerInterface : : viewWorldMap ( )
{
adventureInt - > changeMode ( EAdvMapMode : : WORLD_VIEW ) ;
}
2010-05-16 16:42:19 +03:00
void CPlayerInterface : : advmapSpellCast ( const CGHeroInstance * caster , int spellID )
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2017-07-03 20:38:00 +02:00
2018-07-25 00:36:48 +02:00
if ( dynamic_cast < CSpellWindow * > ( GH . topInt ( ) . get ( ) ) )
GH . popInts ( 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 )
2010-05-16 16:42:19 +03:00
eraseCurrentPathOf ( caster , false ) ;
2017-07-04 00:32:40 +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
const spells : : Spell * spell = CGI - > spells ( ) - > getByIndex ( spellID ) ;
2015-01-13 21:57:41 +02:00
2017-07-04 00:32:40 +02:00
if ( spellID = = SpellID : : VIEW_EARTH )
2015-02-02 14:02:27 +02:00
{
2015-02-26 16:15:17 +02:00
//TODO: implement on server side
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
const auto level = caster - > getSpellSchoolLevel ( spell ) ;
adventureInt - > worldViewOptions . showAllTerrain = ( level > 2 ) ;
2015-02-02 14:02:27 +02:00
}
2016-01-22 22:30:24 +02:00
2014-03-10 19:00:58 +03:00
auto castSoundPath = spell - > 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
}
2017-07-15 13:08:20 +02:00
void CPlayerInterface : : eraseCurrentPathOf ( const CGHeroInstance * ho , bool checkForExistanceOfPath )
2010-02-20 15:24:38 +02:00
{
2016-10-30 11:00:25 +02:00
if ( checkForExistanceOfPath )
2010-05-16 16:42:19 +03:00
{
assert ( vstd : : contains ( paths , ho ) ) ;
}
else if ( ! vstd : : contains ( paths , ho ) )
{
return ;
}
2010-02-20 15:24:38 +02:00
assert ( ho = = adventureInt - > selection ) ;
paths . erase ( ho ) ;
2013-06-26 14:18:27 +03:00
adventureInt - > terrain . currentPath = nullptr ;
2012-02-18 17:59:37 +03:00
adventureInt - > updateMoveHero ( ho , false ) ;
2010-02-20 15:24:38 +02:00
}
2011-09-19 23:50:25 +03:00
void CPlayerInterface : : removeLastNodeFromPath ( const CGHeroInstance * ho )
{
adventureInt - > terrain . currentPath - > nodes . erase ( adventureInt - > terrain . currentPath - > nodes . end ( ) - 1 ) ;
2016-10-30 11:00:25 +02:00
if ( adventureInt - > terrain . currentPath - > nodes . size ( ) < 2 ) //if it was the last one, remove entire path and path with only one tile is not a real path
2011-09-19 23:50:25 +03:00
eraseCurrentPathOf ( ho ) ;
}
2010-02-20 15:24:38 +02:00
CGPath * CPlayerInterface : : getAndVerifyPath ( const CGHeroInstance * h )
{
2016-10-30 11:00:25 +02:00
if ( vstd : : contains ( paths , h ) ) //hero has assigned path
2010-02-20 15:24:38 +02:00
{
CGPath & path = paths [ h ] ;
2016-10-30 11:00:25 +02:00
if ( ! path . nodes . size ( ) )
2010-02-20 15:24:38 +02:00
{
2017-08-10 18:39:27 +02:00
logGlobal - > warn ( " Warning: empty path found... " ) ;
2010-02-20 15:24:38 +02:00
paths . erase ( h ) ;
}
else
{
2022-12-07 22:10:08 +02:00
assert ( h - > visitablePos ( ) = = path . startPos ( ) ) ;
2010-02-20 15:24:38 +02:00
//update the hero path in case of something has changed on map
2016-10-30 11:00:25 +02:00
if ( LOCPLINT - > cb - > getPathsInfo ( h ) - > getPath ( path , path . endPos ( ) ) )
2010-02-20 15:24:38 +02:00
return & path ;
else
paths . erase ( h ) ;
}
}
2013-06-26 14:18:27 +03:00
return nullptr ;
2010-02-20 15:24:38 +02:00
}
void CPlayerInterface : : acceptTurn ( )
{
2015-12-11 10:21:35 +02:00
bool centerView = true ;
2016-10-30 11:00:25 +02:00
if ( settings [ " session " ] [ " autoSkip " ] . Bool ( ) )
2012-01-03 04:55:26 +03:00
{
2015-12-11 10:21:35 +02:00
centerView = false ;
2018-07-25 00:36:48 +02:00
while ( CInfoWindow * iw = dynamic_cast < CInfoWindow * > ( GH . topInt ( ) . get ( ) ) )
2012-01-03 04:55:26 +03:00
iw - > close ( ) ;
}
2010-02-20 15:24:38 +02:00
2018-01-05 19:21:07 +02:00
if ( CSH - > howManyPlayerInterfaces ( ) > 1 )
2022-11-14 15:53:07 +02:00
{
waitWhileDialog ( ) ; // wait for player to accept turn in hot-seat mode
2010-02-20 15:24:38 +02:00
adventureInt - > startTurn ( ) ;
2022-11-14 15:53:07 +02:00
}
2010-02-20 15:24:38 +02:00
2012-09-21 20:59:54 +03:00
adventureInt - > heroList . update ( ) ;
adventureInt - > townList . update ( ) ;
2013-07-21 21:27:33 +03:00
const CGHeroInstance * heroToSelect = nullptr ;
// find first non-sleeping hero
for ( auto hero : wanderingHeroes )
{
if ( boost : : range : : find ( sleepingHeroes , hero ) = = sleepingHeroes . end ( ) )
{
heroToSelect = hero ;
break ;
}
}
//select first hero if available.
2016-10-30 11:00:25 +02:00
if ( heroToSelect ! = nullptr )
2013-07-21 21:27:33 +03:00
{
2015-12-11 10:21:35 +02:00
adventureInt - > select ( heroToSelect , centerView ) ;
2013-07-21 21:27:33 +03:00
}
2016-10-30 11:00:25 +02:00
else if ( towns . size ( ) )
2015-12-11 10:21:35 +02:00
adventureInt - > select ( towns . front ( ) , centerView ) ;
2016-09-01 06:04:43 +02:00
else
adventureInt - > select ( wanderingHeroes . front ( ) ) ;
2010-02-20 15:24:38 +02:00
2012-06-13 16:04:06 +03:00
//show new day animation and sound on infobar
adventureInt - > infoBar . showDate ( ) ;
2013-06-26 14:18:27 +03:00
adventureInt - > updateNextHero ( nullptr ) ;
2010-02-20 15:24:38 +02:00
adventureInt - > showAll ( screen ) ;
2012-01-03 04:55:26 +03:00
2017-09-13 02:35:58 +02:00
if ( settings [ " session " ] [ " autoSkip " ] . Bool ( ) & & ! LOCPLINT - > shiftPressed ( ) )
2012-01-03 04:55:26 +03:00
{
2018-07-25 00:36:48 +02:00
if ( CInfoWindow * iw = dynamic_cast < CInfoWindow * > ( GH . topInt ( ) . get ( ) ) )
2012-01-03 04:55:26 +03:00
iw - > close ( ) ;
2014-08-03 14:16:19 +03:00
adventureInt - > fendTurn ( ) ;
2012-01-03 04:55:26 +03:00
}
2013-11-30 12:43:31 +03:00
// warn player if he has no town
2016-10-30 11:00:25 +02:00
if ( cb - > howManyTowns ( ) = = 0 )
2013-11-30 12:43:31 +03:00
{
auto playerColor = * cb - > getPlayerID ( ) ;
std : : vector < Component > components ;
components . push_back ( Component ( Component : : FLAG , playerColor . getNum ( ) , 0 , 0 ) ) ;
MetaString text ;
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
const auto & optDaysWithoutCastle = cb - > getPlayerState ( playerColor ) - > daysWithoutCastle ;
2016-11-03 18:15:16 +02:00
if ( optDaysWithoutCastle )
2013-11-30 12:43:31 +03:00
{
2016-11-03 18:15:16 +02:00
auto daysWithoutCastle = optDaysWithoutCastle . get ( ) ;
if ( daysWithoutCastle < 6 )
{
text . addTxt ( MetaString : : ARRAY_TXT , 128 ) ; //%s, you only have %d days left to capture a town or you will be banished from this land.
text . addReplacement ( MetaString : : COLOR , playerColor . getNum ( ) ) ;
text . addReplacement ( 7 - daysWithoutCastle ) ;
}
else if ( daysWithoutCastle = = 6 )
{
text . addTxt ( MetaString : : ARRAY_TXT , 129 ) ; //%s, this is your last day to capture a town or you will be banished from this land.
text . addReplacement ( MetaString : : COLOR , playerColor . getNum ( ) ) ;
}
2013-11-30 12:43:31 +03:00
2016-11-03 18:15:16 +02:00
showInfoDialogAndWait ( components , text ) ;
}
else
logGlobal - > warn ( " Player has no towns, but daysWithoutCastle is not set " ) ;
2013-11-30 12:43:31 +03:00
}
2010-02-24 15:03:36 +02:00
}
2021-11-05 23:08:48 +02:00
void CPlayerInterface : : tryDiggging ( const CGHeroInstance * h )
2010-02-24 15:03:36 +02:00
{
2015-11-29 11:32:06 +02:00
int msgToShow = - 1 ;
2022-12-07 22:10:08 +02:00
const bool isBlocked = CGI - > mh - > hasObjectHole ( h - > visitablePos ( ) ) ; // Don't dig in the pit.
2021-11-05 23:08:48 +02:00
const auto diggingStatus = isBlocked
? EDiggingStatus : : TILE_OCCUPIED
: h - > diggingStatus ( ) . num ;
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 ;
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
void CPlayerInterface : : updateInfo ( const CGObjectInstance * specific )
{
2012-06-22 14:40:16 +03:00
adventureInt - > infoBar . showSelection ( ) ;
2010-05-07 15:29:41 +03:00
}
void CPlayerInterface : : battleNewRoundFirst ( int round )
{
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
battleInt - > newRoundFirst ( round ) ;
}
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 ;
2016-10-30 11:00:25 +02:00
if ( market - > o - > ID = = Obj : : ALTAR_OF_SACRIFICE )
2010-07-20 09:05:45 +03:00
{
2011-12-14 00:23:17 +03:00
//EEMarketMode mode = market->availableModes().front();
2016-10-30 11:00:25 +02:00
if ( market - > allowsTrade ( EMarketMode : : ARTIFACT_EXP ) & & visitor - > getAlignment ( ) ! = EAlignment : : EVIL )
2018-07-25 00:36:48 +02:00
GH . pushIntT < CAltarWindow > ( market , visitor , EMarketMode : : ARTIFACT_EXP ) ;
2016-10-30 11:00:25 +02:00
else if ( market - > allowsTrade ( EMarketMode : : CREATURE_EXP ) & & visitor - > getAlignment ( ) ! = EAlignment : : GOOD )
2018-07-25 00:36:48 +02:00
GH . pushIntT < CAltarWindow > ( market , visitor , EMarketMode : : CREATURE_EXP ) ;
2010-07-20 09:05:45 +03:00
}
else
2018-07-25 00:36:48 +02:00
{
GH . pushIntT < 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 ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < 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 ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < 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 ;
2018-07-25 00:36:48 +02:00
if ( CMarketplaceWindow * cmw = dynamic_cast < CMarketplaceWindow * > ( GH . topInt ( ) . get ( ) ) )
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 ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < 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 ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < 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 ;
2018-07-25 00:36:48 +02:00
GH . pushIntT < 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
{
2017-09-13 02:35:58 +02:00
CCS - > soundh - > ambientStopAllChannels ( ) ;
2018-01-05 19:21:07 +02:00
if ( won & & cb - > getStartInfo ( ) - > campState )
CSH - > startCampaignScenario ( cb - > getStartInfo ( ) - > campState ) ;
else
sendCustomEvent ( EUserEvent : : RETURN_TO_MAIN_MENU ) ;
2010-08-20 16:34:39 +03:00
}
void CPlayerInterface : : sendCustomEvent ( int code )
2010-08-18 12:50:25 +03:00
{
2012-09-11 17:25:19 +03:00
CGuiHandler : : pushSDLEvent ( SDL_USEREVENT , code ) ;
2010-08-18 12:50:25 +03:00
}
2016-01-23 14:20:51 +02:00
void CPlayerInterface : : askToAssembleArtifact ( const ArtifactLocation & al )
{
2016-11-03 20:49:55 +02:00
auto hero = boost : : apply_visitor ( HeroObjectRetriever ( ) , al . artHolder ) ;
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 ;
}
2016-11-04 18:54:09 +02:00
CHeroArtPlace : : askToAssemble ( art , al . slot , hero ) ;
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 ;
2016-01-22 22:30:24 +02:00
adventureInt - > infoBar . showSelection ( ) ;
2016-01-23 14:20:51 +02:00
askToAssembleArtifact ( al ) ;
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 ;
2013-12-28 21:57:08 +03:00
adventureInt - > infoBar . showSelection ( ) ;
2018-07-25 00:36:48 +02:00
for ( auto isa : GH . listInt )
2011-04-23 00:51:10 +03:00
{
2018-07-25 00:36:48 +02:00
auto artWin = dynamic_cast < CArtifactHolder * > ( isa . get ( ) ) ;
2016-10-30 11:00:25 +02:00
if ( artWin )
2012-06-15 20:08:19 +03:00
artWin - > artifactRemoved ( al ) ;
2011-04-23 00:51:10 +03:00
}
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 ;
2013-12-28 21:57:08 +03:00
adventureInt - > infoBar . showSelection ( ) ;
2018-07-25 00:36:48 +02:00
for ( auto isa : GH . listInt )
2011-03-22 15:19:07 +02:00
{
2018-07-25 00:36:48 +02:00
auto artWin = dynamic_cast < CArtifactHolder * > ( isa . get ( ) ) ;
2016-10-30 11:00:25 +02:00
if ( artWin )
2012-06-15 20:08:19 +03:00
artWin - > artifactMoved ( src , dst ) ;
2011-03-22 15:19:07 +02:00
}
2022-11-10 20:48:19 +02:00
if ( ! GH . objsToBlit . empty ( ) )
GH . objsToBlit . back ( ) - > redraw ( ) ;
2022-12-27 14:24:42 +02:00
waitWhileDialog ( ) ;
2022-11-07 00:36:13 +02:00
}
void CPlayerInterface : : artifactPossibleAssembling ( const ArtifactLocation & dst )
{
2016-01-23 14:20:51 +02:00
askToAssembleArtifact ( dst ) ;
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 ;
2013-12-28 21:57:08 +03:00
adventureInt - > infoBar . showSelection ( ) ;
2018-07-25 00:36:48 +02:00
for ( auto isa : GH . listInt )
2011-03-22 15:19:07 +02:00
{
2018-07-25 00:36:48 +02:00
auto artWin = dynamic_cast < CArtifactHolder * > ( isa . get ( ) ) ;
2016-10-30 11:00:25 +02:00
if ( artWin )
2012-06-15 20:08:19 +03:00
artWin - > artifactAssembled ( al ) ;
2011-03-22 15:19:07 +02:00
}
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 ;
2013-12-28 21:57:08 +03:00
adventureInt - > infoBar . showSelection ( ) ;
2018-07-25 00:36:48 +02:00
for ( auto isa : GH . listInt )
2011-03-22 15:19:07 +02:00
{
2018-07-25 00:36:48 +02:00
auto artWin = dynamic_cast < CArtifactHolder * > ( isa . get ( ) ) ;
2016-10-30 11:00:25 +02:00
if ( artWin )
2012-06-15 20:08:19 +03:00
artWin - > artifactDisassembled ( al ) ;
2011-03-22 15:19:07 +02:00
}
2011-01-22 05:43:20 +02:00
}
2013-03-03 20:06:03 +03:00
void CPlayerInterface : : playerStartsTurn ( PlayerColor player )
2012-02-14 21:04:45 +03:00
{
2012-04-06 18:02:15 +03:00
EVENT_HANDLER_CALLED_BY_CLIENT ;
2013-01-25 17:39:28 +03:00
if ( ! vstd : : contains ( GH . listInt , adventureInt ) )
2012-02-17 00:19:07 +03:00
{
2020-10-01 10:38:06 +02:00
GH . popInts ( ( int ) GH . listInt . size ( ) ) ; //after map load - remove everything else
2013-01-25 17:39:28 +03:00
GH . pushInt ( adventureInt ) ;
2012-04-06 18:02:15 +03:00
}
2013-01-25 17:39:28 +03:00
else
{
2016-10-02 22:06:33 +02:00
adventureInt - > infoBar . showSelection ( ) ;
2018-07-25 00:36:48 +02:00
while ( GH . listInt . front ( ) ! = adventureInt & & ! dynamic_cast < CInfoWindow * > ( GH . listInt . front ( ) . get ( ) ) ) //don't remove dialogs that expect query answer
2013-01-25 17:39:28 +03:00
GH . popInts ( 1 ) ;
}
2018-01-05 19:21:07 +02:00
if ( CSH - > howManyPlayerInterfaces ( ) = = 1 )
2012-04-06 18:02:15 +03:00
{
GH . curInt = this ;
adventureInt - > startTurn ( ) ;
2012-02-17 00:19:07 +03:00
}
2016-10-30 11:00:25 +02:00
if ( player ! = playerID & & this = = LOCPLINT )
2012-02-14 21:04:45 +03:00
{
2012-02-17 00:19:07 +03:00
waitWhileDialog ( ) ;
2012-02-22 16:41:27 +03:00
adventureInt - > aiTurnStarted ( ) ;
}
}
2012-02-17 00:19:07 +03: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 ) ;
SDL_Delay ( 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 ( )
{
2018-04-07 13:34:11 +02:00
showYesNoDialog ( CGI - > generaltexth - > allTexts [ 68 ] , [ this ] ( ) { sendCustomEvent ( EUserEvent : : RETURN_TO_MENU_LOAD ) ; } , nullptr ) ;
2012-04-09 05:53:50 +03:00
}
2010-07-21 13:09:29 +03:00
CPlayerInterface : : SpellbookLastSetting : : SpellbookLastSetting ( )
{
spellbookLastPageBattle = spellbokLastPageAdvmap = 0 ;
spellbookLastTabBattle = spellbookLastTabAdvmap = 4 ;
2010-10-31 00:53:41 +03:00
}
2014-06-18 13:31:11 +03:00
bool CPlayerInterface : : capturedAllEvents ( )
{
2016-10-30 11:00:25 +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.
2014-06-21 09:49:27 +03:00
return true ;
}
2015-01-13 21:57:41 +02:00
2016-10-30 11:00:25 +02:00
if ( ignoreEvents )
2014-06-21 16:41:05 +03:00
{
boost : : unique_lock < boost : : mutex > un ( eventsM ) ;
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
while ( ! SDLEventsQueue . empty ( ) )
2014-06-21 16:41:05 +03: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
SDLEventsQueue . pop ( ) ;
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
{
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
2015-12-11 08:42:30 +02:00
auto isTeleportAction = [ & ] ( CGPathNode : : ENodeAction action ) - > bool
{
2016-10-30 11:00:25 +02:00
if ( action ! = CGPathNode : : TELEPORT_NORMAL & &
2015-12-11 08:42:30 +02:00
action ! = CGPathNode : : TELEPORT_BLOCKING_VISIT & &
action ! = CGPathNode : : TELEPORT_BATTLE )
{
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
2022-09-29 11:44:46 +02:00
TerrainId currentTerrain = Terrain : : BORDER ; // not init yet
TerrainId newTerrain ;
2014-06-21 09:49:27 +03:00
int sh = - 1 ;
2014-06-18 13:31:11 +03:00
2015-11-17 16:46:26 +02:00
auto canStop = [ & ] ( CGPathNode * node ) - > bool
{
2016-10-30 11:00:25 +02:00
if ( node - > layer = = EPathfindingLayer : : LAND | | node - > layer = = EPathfindingLayer : : SAIL )
2015-11-17 16:46:26 +02:00
return true ;
2016-10-30 11:00:25 +02:00
if ( node - > accessible = = CGPathNode : : ACCESSIBLE )
2015-11-17 16:46:26 +02:00
return true ;
return false ;
} ;
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
{
2015-03-08 16:17:24 +02:00
int3 currentCoord = path . nodes [ i ] . coord ;
int3 nextCoord = path . nodes [ i - 1 ] . coord ;
2015-12-11 08:42:30 +02:00
auto currentObject = getObj ( currentCoord , currentCoord = = h - > pos ) ;
auto nextObjectTop = getObj ( nextCoord , false ) ;
auto nextObject = getObj ( nextCoord , true ) ;
auto destTeleportObj = getDestTeleportObj ( currentObject , 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
{
CCS - > soundh - > stopSound ( sh ) ;
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 ) ;
2022-06-19 09:33:24 +02:00
if ( path . nodes [ i - 1 ] . action = = CGPathNode : : TELEPORT_BLOCKING_VISIT
| | path . nodes [ i - 1 ] . action = = CGPathNode : : 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
{
2018-09-16 16:01:36 +02:00
sh = CCS - > soundh - > playSound ( CCS - > soundh - > horseSounds [ currentTerrain ] , - 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
// Start a new sound for the hero movement or let the existing one carry on.
2014-06-18 13:31:11 +03:00
#if 0
2014-06-21 09:49:27 +03:00
// TODO
2016-10-30 11:00:25 +02:00
if ( hero is flying & & sh = = - 1 )
2014-06-21 09:49:27 +03:00
sh = CCS - > soundh - > playSound ( soundBase : : horseFlying , - 1 ) ;
2014-06-18 13:31:11 +03:00
# endif
2014-06-21 09:49:27 +03:00
{
2022-12-09 14:42:47 +02:00
newTerrain = cb - > getTile ( h - > convertToVisitablePos ( currentCoord ) ) - > terType - > id ;
2022-06-20 16:39:50 +02:00
if ( newTerrain ! = currentTerrain )
2014-06-18 13:31:11 +03:00
{
2014-06-21 09:49:27 +03:00
CCS - > soundh - > stopSound ( sh ) ;
sh = CCS - > soundh - > playSound ( CCS - > soundh - > horseSounds [ newTerrain ] , - 1 ) ;
currentTerrain = newTerrain ;
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
}
2014-06-21 09:49:27 +03:00
CCS - > soundh - > stopSound ( sh ) ;
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 ( ) ;
2014-06-18 13:31:11 +03:00
//todo: this should be in main thread
2016-10-30 11:00:25 +02:00
if ( adventureInt )
2014-06-18 13:31:11 +03:00
{
// (i == 0) means hero went through all the path
adventureInt - > updateMoveHero ( h , ( i ! = 0 ) ) ;
adventureInt - > updateNextHero ( h ) ;
2015-01-13 21:57:41 +02:00
}
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
void CPlayerInterface : : showWorldViewEx ( const std : : vector < ObjectPosInfo > & objectPositions )
{
EVENT_HANDLER_CALLED_BY_CLIENT ;
//TODO: showWorldViewEx
2016-01-22 22:30:24 +02:00
2015-02-26 16:15:17 +02:00
std : : copy ( objectPositions . begin ( ) , objectPositions . end ( ) , std : : back_inserter ( adventureInt - > worldViewOptions . iconPositions ) ) ;
2016-01-22 22:30:24 +02:00
2015-02-26 16:15:17 +02:00
viewWorldMap ( ) ;
}
2017-09-13 02:35:58 +02:00
void CPlayerInterface : : updateAmbientSounds ( bool resetAll )
{
if ( castleInt | | battleInt | | ! makingTurn | | ! currentSelection )
{
CCS - > soundh - > ambientStopAllChannels ( ) ;
return ;
}
2018-07-25 00:36:48 +02:00
else if ( ! dynamic_cast < CAdvMapInt * > ( GH . topInt ( ) . get ( ) ) )
2017-09-13 02:35:58 +02:00
{
return ;
}
if ( resetAll )
CCS - > soundh - > ambientStopAllChannels ( ) ;
std : : map < std : : string , int > currentSounds ;
auto updateSounds = [ & ] ( std : : string soundId , int distance ) - > void
{
if ( vstd : : contains ( currentSounds , soundId ) )
currentSounds [ soundId ] = std : : max ( currentSounds [ soundId ] , distance ) ;
else
currentSounds . insert ( std : : make_pair ( soundId , distance ) ) ;
} ;
int3 pos = currentSelection - > getSightCenter ( ) ;
std : : unordered_set < int3 , ShashInt3 > tiles ;
cb - > getVisibleTilesInRange ( tiles , pos , CCS - > soundh - > ambientGetRange ( ) , int3 : : DIST_CHEBYSHEV ) ;
for ( int3 tile : tiles )
{
int dist = pos . dist ( tile , int3 : : DIST_CHEBYSHEV ) ;
// We want sound for every special terrain on tile and not just one on top
2022-09-18 16:39:10 +02:00
for ( auto & ttObj : CGI - > mh - > ttiles [ tile . z ] [ tile . x ] [ tile . y ] . objects )
2017-09-13 02:35:58 +02:00
{
2017-09-14 19:46:38 +02:00
if ( ttObj . ambientSound )
updateSounds ( ttObj . ambientSound . get ( ) , dist ) ;
2017-09-13 02:35:58 +02:00
}
if ( CGI - > mh - > map - > isCoastalTile ( tile ) )
updateSounds ( " LOOPOCEA " , dist ) ;
}
CCS - > soundh - > ambientUpdateChannels ( currentSounds ) ;
}