2017-07-13 10:26:03 +02:00
/*
* CBattleInterface . 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:35:28 +03:00
# include "StdInc.h"
2009-01-15 19:01:08 +02:00
# include "CBattleInterface.h"
2011-12-14 00:35:28 +03:00
2014-07-13 20:53:37 +03:00
# include "CBattleAnimations.h"
# include "CBattleInterfaceClasses.h"
# include "CCreatureAnimation.h"
2022-11-17 13:21:03 +02:00
# include "CBattleProjectileController.h"
2022-11-17 19:36:25 +02:00
# include "CBattleObstacleController.h"
2022-11-17 18:50:12 +02:00
# include "CBattleSiegeController.h"
2022-11-17 23:57:51 +02:00
# include "CBattleFieldController.h"
2022-11-18 17:54:10 +02:00
# include "CBattleControlPanel.h"
2022-11-20 19:11:34 +02:00
# include "CBattleStacksController.h"
2014-07-13 20:53:37 +03:00
2013-07-06 19:10:20 +03:00
# include "../CBitmapHandler.h"
2014-07-13 20:53:37 +03:00
# include "../CGameInfo.h"
2011-12-14 00:35:28 +03:00
# include "../CMessage.h"
2014-07-13 20:53:37 +03:00
# include "../CMT.h"
# include "../CMusicHandler.h"
# include "../CPlayerInterface.h"
# include "../CVideoHandler.h"
# include "../Graphics.h"
2017-09-05 15:44:27 +02:00
# include "../gui/CAnimation.h"
2014-07-13 20:53:37 +03:00
# include "../gui/CCursorHandler.h"
# include "../gui/CGuiHandler.h"
# include "../gui/SDL_Extensions.h"
# include "../windows/CAdvmapInterface.h"
# include "../windows/CCreatureWindow.h"
# include "../windows/CSpellWindow.h"
2011-12-14 00:35:28 +03:00
# include "../../CCallback.h"
2017-03-17 17:48:44 +02:00
# include "../../lib/CStack.h"
2012-09-29 13:59:43 +03:00
# include "../../lib/CConfigHandler.h"
2014-07-13 20:53:37 +03:00
# include "../../lib/CGeneralTextHandler.h"
# include "../../lib/CHeroHandler.h"
2011-12-14 00:35:28 +03:00
# include "../../lib/CondSh.h"
2014-07-13 20:53:37 +03:00
# include "../../lib/CRandomGenerator.h"
2015-02-02 10:25:26 +02:00
# include "../../lib/spells/CSpellHandler.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 "../../lib/spells/ISpellMechanics.h"
# include "../../lib/spells/Problem.h"
2011-12-14 00:35:28 +03:00
# include "../../lib/CTownHandler.h"
2022-06-28 10:05:30 +02:00
# include "../../lib/BattleFieldHandler.h"
2022-09-15 10:06:54 +02:00
# include "../../lib/ObstacleHandler.h"
2014-07-15 10:14:49 +03:00
# include "../../lib/CGameState.h"
2013-04-07 13:48:07 +03:00
# include "../../lib/mapping/CMap.h"
2014-07-13 20:53:37 +03:00
# include "../../lib/NetPacks.h"
2012-02-20 00:03:43 +03:00
# include "../../lib/UnlockGuard.h"
2009-01-15 19:01:08 +02:00
2016-11-27 18:13:40 +02:00
CondSh < bool > CBattleInterface : : animsAreDisplayed ( false ) ;
2017-06-14 03:53:26 +02:00
CondSh < BattleAction * > CBattleInterface : : givenCommand ( nullptr ) ;
2009-03-28 20:46:20 +02:00
2016-10-28 23:37:20 +02:00
CBattleInterface : : CBattleInterface ( const CCreatureSet * army1 , const CCreatureSet * army2 ,
2018-07-25 00:36:48 +02:00
const CGHeroInstance * hero1 , const CGHeroInstance * hero2 ,
const SDL_Rect & myRect ,
std : : shared_ptr < CPlayerInterface > att , std : : shared_ptr < CPlayerInterface > defen , std : : shared_ptr < CPlayerInterface > spectatorInt )
2022-11-17 23:57:51 +02:00
: attackingHeroInstance ( hero1 ) , defendingHeroInstance ( hero2 ) , animCount ( 0 ) ,
2022-11-20 19:11:34 +02:00
creatureCasting ( false ) , spellDestSelectMode ( false ) , spellToCast ( nullptr ) , sp ( nullptr ) ,
attackerInt ( att ) , defenderInt ( defen ) , curInt ( att ) ,
2019-03-22 22:39:53 +02:00
myTurn ( false ) , moveStarted ( false ) , moveSoundHander ( - 1 ) , bresult ( nullptr ) , battleActionsStarted ( false )
2009-09-10 14:28:34 +03:00
{
2011-12-17 21:59:59 +03:00
OBJ_CONSTRUCTION ;
2009-09-10 14:28:34 +03:00
2022-11-17 13:21:03 +02:00
projectilesController . reset ( new CBattleProjectileController ( this ) ) ;
2017-06-03 07:25:10 +02:00
if ( spectatorInt )
2018-07-25 00:36:48 +02:00
{
2017-06-03 07:25:10 +02:00
curInt = spectatorInt ;
2018-07-25 00:36:48 +02:00
}
2017-06-03 07:25:10 +02:00
else if ( ! curInt )
2013-06-23 00:47:51 +03:00
{
//May happen when we are defending during network MP game -> attacker interface is just not present
2014-03-07 16:21:09 +03:00
curInt = defenderInt ;
2013-06-23 00:47:51 +03:00
}
2010-01-28 19:23:01 +02:00
2011-12-14 00:35:28 +03:00
animsAreDisplayed . setn ( false ) ;
pos = myRect ;
strongInterest = true ;
2017-06-14 03:53:26 +02:00
givenCommand . setn ( nullptr ) ;
2010-01-28 19:23:01 +02:00
2013-11-09 19:25:20 +03:00
//hot-seat -> check tactics for both players (defender may be local human)
2018-07-25 00:36:48 +02:00
if ( attackerInt & & attackerInt - > cb - > battleGetTacticDist ( ) )
2011-12-14 00:35:28 +03:00
tacticianInterface = attackerInt ;
2018-07-25 00:36:48 +02:00
else if ( defenderInt & & defenderInt - > cb - > battleGetTacticDist ( ) )
2011-12-14 00:35:28 +03:00
tacticianInterface = defenderInt ;
2010-01-28 19:23:01 +02:00
2013-11-09 19:25:20 +03:00
//if we found interface of player with tactics, then enter tactics mode
tacticsMode = static_cast < bool > ( tacticianInterface ) ;
2010-01-28 19:23:01 +02:00
2011-12-14 00:35:28 +03:00
//create stack queue
2017-07-20 06:08:49 +02:00
bool embedQueue ;
std : : string queueSize = settings [ " battle " ] [ " queueSize " ] . String ( ) ;
if ( queueSize = = " auto " )
embedQueue = screen - > h < 700 ;
else
embedQueue = screen - > h < 700 | | queueSize = = " small " ;
2018-07-25 00:36:48 +02:00
queue = std : : make_shared < CStackQueue > ( embedQueue , this ) ;
2017-07-20 06:08:49 +02:00
if ( ! embedQueue )
2011-12-14 00:35:28 +03:00
{
2016-10-28 23:37:20 +02:00
if ( settings [ " battle " ] [ " showQueue " ] . Bool ( ) )
2011-12-14 00:35:28 +03:00
pos . y + = queue - > pos . h / 2 ; //center whole window
2010-01-28 19:23:01 +02:00
2011-12-22 16:05:19 +03:00
queue - > moveTo ( Point ( pos . x , pos . y - queue - > pos . h ) ) ;
2011-12-14 00:35:28 +03:00
}
queue - > update ( ) ;
2010-01-28 19:23:01 +02:00
2011-12-14 00:35:28 +03:00
//preparing siege info
2016-10-28 23:37:20 +02:00
const CGTownInstance * town = curInt - > cb - > battleGetDefendedTown ( ) ;
2018-07-25 00:36:48 +02:00
if ( town & & town - > hasFort ( ) )
2022-11-17 18:50:12 +02:00
siegeController . reset ( new CBattleSiegeController ( this , town ) ) ;
2010-01-28 19:23:01 +02:00
2018-07-25 00:36:48 +02:00
CPlayerInterface : : battleInt = this ;
2010-01-28 19:23:01 +02:00
2011-12-14 00:35:28 +03:00
//initializing armies
this - > army1 = army1 ;
this - > army2 = army2 ;
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
//preparing menu background and terrain
2022-11-17 23:57:51 +02:00
fieldController . reset ( new CBattleFieldController ( this ) ) ;
2022-11-20 19:11:34 +02:00
stacksController . reset ( new CBattleStacksController ( this ) ) ;
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
//loading hero animations
2018-07-25 00:36:48 +02:00
if ( hero1 ) // attacking hero
2009-09-16 13:09:08 +03:00
{
2012-12-16 16:47:53 +03:00
std : : string battleImage ;
2018-08-27 08:42:36 +02:00
if ( ! hero1 - > type - > battleImage . empty ( ) )
{
battleImage = hero1 - > type - > battleImage ;
}
2012-12-16 16:47:53 +03:00
else
2018-08-27 08:42:36 +02:00
{
if ( hero1 - > sex )
battleImage = hero1 - > type - > heroClass - > imageBattleFemale ;
else
battleImage = hero1 - > type - > heroClass - > imageBattleMale ;
}
2012-12-16 16:47:53 +03:00
2018-07-25 00:36:48 +02:00
attackingHero = std : : make_shared < CBattleHero > ( battleImage , false , hero1 - > tempOwner , hero1 - > tempOwner = = curInt - > playerID ? hero1 : nullptr , this ) ;
2017-09-05 15:44:27 +02:00
2018-03-30 13:02:04 +02:00
auto img = attackingHero - > animation - > getImage ( 0 , 0 , true ) ;
2017-09-05 15:44:27 +02:00
if ( img )
attackingHero - > pos = genRect ( img - > height ( ) , img - > width ( ) , pos . x - 43 , pos . y - 19 ) ;
2009-09-16 13:09:08 +03:00
}
2018-07-25 00:36:48 +02:00
if ( hero2 ) // defending hero
2009-09-16 13:09:08 +03:00
{
2012-12-16 16:47:53 +03:00
std : : string battleImage ;
2018-08-27 08:42:36 +02:00
if ( ! hero2 - > type - > battleImage . empty ( ) )
{
battleImage = hero2 - > type - > battleImage ;
}
2012-12-16 16:47:53 +03:00
else
2018-08-27 08:42:36 +02:00
{
if ( hero2 - > sex )
battleImage = hero2 - > type - > heroClass - > imageBattleFemale ;
else
battleImage = hero2 - > type - > heroClass - > imageBattleMale ;
}
2012-12-16 16:47:53 +03:00
2018-07-25 00:36:48 +02:00
defendingHero = std : : make_shared < CBattleHero > ( battleImage , true , hero2 - > tempOwner , hero2 - > tempOwner = = curInt - > playerID ? hero2 : nullptr , this ) ;
2017-09-05 15:44:27 +02:00
2018-03-30 13:02:04 +02:00
auto img = defendingHero - > animation - > getImage ( 0 , 0 , true ) ;
2017-09-05 15:44:27 +02:00
if ( img )
defendingHero - > pos = genRect ( img - > height ( ) , img - > width ( ) , pos . x + 693 , pos . y - 19 ) ;
2009-09-16 13:09:08 +03:00
}
2018-07-25 00:36:48 +02:00
2022-11-17 19:36:25 +02:00
obstacleController . reset ( new CBattleObstacleController ( this ) ) ;
2009-09-10 14:28:34 +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
if ( tacticsMode )
2022-11-18 17:54:10 +02:00
tacticNextStack ( nullptr ) ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
CCS - > musich - > stopMusic ( ) ;
2019-03-22 22:39:53 +02:00
battleIntroSoundChannel = CCS - > soundh - > playSoundFromSet ( CCS - > soundh - > battleIntroSounds ) ;
auto onIntroPlayed = [ & ] ( )
2012-08-06 10:34:37 +03:00
{
2019-03-22 22:39:53 +02:00
if ( LOCPLINT - > battleInt )
{
2022-11-13 14:24:15 +02:00
CCS - > musich - > playMusicFromSet ( " battle " , true , true ) ;
2019-03-22 22:39:53 +02:00
battleActionsStarted = true ;
2022-11-18 17:54:10 +02:00
controlPanel - > blockUI ( settings [ " session " ] [ " spectate " ] . Bool ( ) ) ;
2019-03-22 22:39:53 +02:00
battleIntroSoundChannel = - 1 ;
}
2012-08-06 10:34:37 +03:00
} ;
2019-03-22 22:39:53 +02:00
CCS - > soundh - > setCallback ( battleIntroSoundChannel , onIntroPlayed ) ;
2012-04-04 11:03:52 +03:00
2019-05-04 05:42:55 +02:00
currentAction = PossiblePlayerBattleAction : : INVALID ;
selectedAction = PossiblePlayerBattleAction : : INVALID ;
2012-06-02 18:16:54 +03:00
addUsedEvents ( RCLICK | MOVE | KEYBOARD ) ;
2022-11-18 17:54:10 +02:00
controlPanel - > blockUI ( true ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
CBattleInterface : : ~ CBattleInterface ( )
2009-09-10 14:28:34 +03:00
{
2018-07-25 00:36:48 +02:00
CPlayerInterface : : battleInt = nullptr ;
2022-11-20 19:11:34 +02:00
givenCommand . cond . notify_all ( ) ; //that two lines should make any stacksController->getActiveStack() waiting thread to finish
2012-02-20 00:03:43 +03:00
2011-12-14 00:35:28 +03:00
if ( active ) //dirty fix for #485
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
deactivate ( ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
//TODO: play AI tracks if battle was during AI turn
2012-02-16 20:10:58 +03:00
//if (!curInt->makingTurn)
2011-12-14 00:35:28 +03:00
//CCS->musich->playMusicFromSet(CCS->musich->aiMusics, -1);
2012-02-16 20:10:58 +03:00
2016-10-28 23:37:20 +02:00
if ( adventureInt & & adventureInt - > selection )
2011-12-14 00:35:28 +03:00
{
2022-09-25 21:47:44 +02:00
const auto & terrain = * ( LOCPLINT - > cb - > getTile ( adventureInt - > selection - > visitablePos ( ) ) - > terType ) ;
2022-11-13 14:24:15 +02:00
CCS - > musich - > playMusicFromSet ( " terrain " , terrain . name , true , false ) ;
2011-12-14 00:35:28 +03:00
}
2017-06-05 17:43:02 +02:00
animsAreDisplayed . setn ( false ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : setPrintCellBorders ( bool set )
2009-09-10 14:28:34 +03:00
{
2012-01-12 18:23:00 +03:00
Settings cellBorders = settings . write [ " battle " ] [ " cellBorders " ] ;
cellBorders - > Bool ( ) = set ;
2022-11-20 19:11:34 +02:00
fieldController - > redrawBackgroundWithHexes ( ) ;
2011-12-14 00:35:28 +03:00
GH . totalRedraw ( ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : setPrintStackRange ( bool set )
{
2012-01-12 18:23:00 +03:00
Settings stackRange = settings . write [ " battle " ] [ " stackRange " ] ;
stackRange - > Bool ( ) = set ;
2022-11-20 19:11:34 +02:00
fieldController - > redrawBackgroundWithHexes ( ) ;
2011-12-14 00:35:28 +03:00
GH . totalRedraw ( ) ;
}
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : setPrintMouseShadow ( bool set )
2009-09-10 14:28:34 +03:00
{
2012-01-12 18:23:00 +03:00
Settings shadow = settings . write [ " battle " ] [ " mouseShadow " ] ;
shadow - > Bool ( ) = set ;
2011-12-14 00:35:28 +03:00
}
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : activate ( )
{
2022-11-18 17:54:10 +02:00
controlPanel - > activate ( ) ;
2016-10-28 23:37:20 +02:00
if ( curInt - > isAutoFightOn )
2013-06-23 00:47:51 +03:00
return ;
2012-06-02 18:16:54 +03:00
CIntObject : : activate ( ) ;
2012-06-09 19:45:45 +03:00
2016-10-28 23:37:20 +02:00
if ( attackingHero )
2011-12-14 00:35:28 +03:00
attackingHero - > activate ( ) ;
2016-10-28 23:37:20 +02:00
if ( defendingHero )
2011-12-14 00:35:28 +03:00
defendingHero - > activate ( ) ;
2022-09-11 10:31:24 +02:00
2022-11-17 23:57:51 +02:00
fieldController - > activate ( ) ;
2022-09-11 10:31:24 +02:00
2016-10-28 23:37:20 +02:00
if ( settings [ " battle " ] [ " showQueue " ] . Bool ( ) )
2011-12-14 00:35:28 +03:00
queue - > activate ( ) ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
LOCPLINT - > cingconsole - > activate ( ) ;
}
void CBattleInterface : : deactivate ( )
{
2022-11-18 17:54:10 +02:00
controlPanel - > deactivate ( ) ;
2012-06-02 18:16:54 +03:00
CIntObject : : deactivate ( ) ;
2022-11-17 23:57:51 +02:00
fieldController - > deactivate ( ) ;
2012-06-09 19:45:45 +03:00
2016-10-28 23:37:20 +02:00
if ( attackingHero )
2011-12-14 00:35:28 +03:00
attackingHero - > deactivate ( ) ;
2016-10-28 23:37:20 +02:00
if ( defendingHero )
2011-12-14 00:35:28 +03:00
defendingHero - > deactivate ( ) ;
2016-10-28 23:37:20 +02:00
if ( settings [ " battle " ] [ " showQueue " ] . Bool ( ) )
2011-12-14 00:35:28 +03:00
queue - > deactivate ( ) ;
2009-09-11 15:46:26 +03:00
2011-12-14 00:35:28 +03:00
LOCPLINT - > cingconsole - > deactivate ( ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : keyPressed ( const SDL_KeyboardEvent & key )
{
2021-02-20 03:57:50 +02:00
if ( key . keysym . sym = = SDLK_q & & key . state = = SDL_PRESSED )
2009-09-04 17:11:42 +03:00
{
2021-02-20 03:57:50 +02:00
if ( settings [ " battle " ] [ " showQueue " ] . Bool ( ) ) //hide queue
2011-12-14 00:35:28 +03:00
hideQueue ( ) ;
else
showQueue ( ) ;
2009-09-21 11:29:41 +03:00
2011-12-14 00:35:28 +03:00
}
2021-02-20 03:57:50 +02:00
else if ( key . keysym . sym = = SDLK_f & & key . state = = SDL_PRESSED )
2016-10-15 22:50:12 +02:00
{
enterCreatureCastingMode ( ) ;
}
2021-02-20 03:57:50 +02:00
else if ( key . keysym . sym = = SDLK_ESCAPE )
2009-01-15 19:01:08 +02:00
{
2019-03-22 22:39:53 +02:00
if ( ! battleActionsStarted )
CCS - > soundh - > stopSound ( battleIntroSoundChannel ) ;
else
2021-02-20 03:57:50 +02:00
endCastingSpell ( ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
}
void CBattleInterface : : mouseMoved ( const SDL_MouseMotionEvent & sEvent )
{
2022-11-17 23:57:51 +02:00
BattleHex selectedHex = fieldController - > getHoveredHex ( ) ;
2011-12-14 00:35:28 +03:00
2022-11-17 23:57:51 +02:00
handleHex ( selectedHex , MOVE ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : clickRight ( tribool down , bool previousState )
2009-09-24 16:44:55 +03:00
{
2016-10-28 23:37:20 +02:00
if ( ! down )
2009-09-24 16:44:55 +03:00
{
2011-12-14 00:35:28 +03:00
endCastingSpell ( ) ;
}
}
2009-09-24 16:44:55 +03:00
2022-11-20 19:11:34 +02:00
void CBattleInterface : : stackReset ( const CStack * stack )
2011-12-14 00:35:28 +03:00
{
2022-11-20 19:11:34 +02:00
stacksController - > stackReset ( stack ) ;
}
2014-03-07 16:21:09 +03:00
2022-11-20 19:11:34 +02:00
void CBattleInterface : : stackAdded ( const CStack * stack )
{
stacksController - > stackAdded ( stack ) ;
2011-12-14 00:35:28 +03:00
}
2017-07-20 06:08:49 +02:00
void CBattleInterface : : stackRemoved ( uint32_t stackID )
2011-12-14 00:35:28 +03:00
{
2022-11-20 19:11:34 +02:00
stacksController - > stackRemoved ( stackID ) ;
fieldController - > redrawBackgroundWithHexes ( ) ;
2012-02-22 20:43:59 +03:00
queue - > update ( ) ;
2011-12-14 00:35:28 +03:00
}
2016-10-28 23:37:20 +02:00
void CBattleInterface : : stackActivated ( const CStack * stack ) //TODO: check it all before game state is changed due to abilities
2011-12-14 00:35:28 +03:00
{
2022-11-20 19:11:34 +02:00
stacksController - > stackActivated ( stack ) ;
2011-12-14 00:35:28 +03:00
}
2016-10-28 23:37:20 +02:00
void CBattleInterface : : stackMoved ( const CStack * stack , std : : vector < BattleHex > destHex , int distance )
2011-12-14 00:35:28 +03:00
{
2022-11-20 19:11:34 +02:00
stacksController - > stackMoved ( stack , destHex , distance ) ;
2011-12-14 00:35:28 +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 CBattleInterface : : stacksAreAttacked ( std : : vector < StackAttackedInfo > attackedInfos )
2011-12-14 00:35:28 +03:00
{
2022-11-20 19:11:34 +02:00
stacksController - > stacksAreAttacked ( attackedInfos ) ;
2017-09-27 16:35:30 +02:00
std : : array < int , 2 > killedBySide = { 0 , 0 } ;
2022-09-22 10:49:55 +02:00
int targets = 0 ;
2017-09-27 16:35:30 +02:00
for ( const StackAttackedInfo & attackedInfo : attackedInfos )
2009-11-08 16:44:58 +02:00
{
2011-12-14 00:35:28 +03:00
+ + targets ;
2017-09-27 16:35:30 +02:00
ui8 side = attackedInfo . defender - > side ;
killedBySide . at ( side ) + = attackedInfo . amountKilled ;
}
for ( ui8 side = 0 ; side < 2 ; side + + )
{
if ( killedBySide . at ( side ) > killedBySide . at ( 1 - side ) )
setHeroAnimation ( side , 2 ) ;
else if ( killedBySide . at ( side ) < killedBySide . at ( 1 - side ) )
setHeroAnimation ( side , 3 ) ;
2009-11-08 16:44:58 +02:00
}
2009-03-21 18:03:07 +02:00
}
2016-10-28 23:37:20 +02:00
void CBattleInterface : : stackAttacking ( const CStack * attacker , BattleHex dest , const CStack * attacked , bool shooting )
2009-03-21 18:03:07 +02:00
{
2022-11-20 19:11:34 +02:00
stacksController - > stackAttacking ( attacker , dest , attacked , shooting ) ;
2011-12-14 00:35:28 +03:00
}
2011-01-18 19:23:31 +02:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : newRoundFirst ( int round )
{
waitForAnims ( ) ;
}
2011-01-18 19:23:31 +02:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : newRound ( int number )
{
2022-11-18 17:54:10 +02:00
controlPanel - > console - > addText ( CGI - > generaltexth - > allTexts [ 412 ] ) ;
2011-12-14 00:35:28 +03:00
}
2017-07-20 06:08:49 +02:00
void CBattleInterface : : giveCommand ( EActionType action , BattleHex tile , si32 additional )
2011-12-14 00:35:28 +03:00
{
2017-07-20 06:08:49 +02:00
const CStack * actor = nullptr ;
if ( action ! = EActionType : : HERO_SPELL & & action ! = EActionType : : RETREAT & & action ! = EActionType : : SURRENDER )
2011-12-14 00:35:28 +03:00
{
2022-11-20 19:11:34 +02:00
actor = stacksController - > getActiveStack ( ) ;
2009-01-15 19:01:08 +02:00
}
2012-04-03 02:23:14 +03:00
2017-07-20 06:08:49 +02:00
auto side = curInt - > cb - > playerToSide ( curInt - > playerID ) ;
if ( ! side )
{
logGlobal - > error ( " Player %s is not in battle " , curInt - > playerID . getStr ( ) ) ;
return ;
}
2012-04-03 02:23:14 +03:00
2022-11-20 19:11:34 +02:00
auto ba = new BattleAction ( ) ; //is deleted in CPlayerInterface::stacksController->getActiveStack()()
2017-07-20 06:08:49 +02:00
ba - > side = side . get ( ) ;
2011-12-14 00:35:28 +03:00
ba - > actionType = action ;
2017-07-20 06:08:49 +02:00
ba - > aimToHex ( tile ) ;
ba - > actionSubtype = additional ;
sendCommand ( ba , actor ) ;
}
2011-01-18 19:23:31 +02:00
2017-07-20 06:08:49 +02:00
void CBattleInterface : : sendCommand ( BattleAction * & command , const CStack * actor )
{
command - > stackNumber = actor ? actor - > unitId ( ) : ( ( command - > side = = BattleSide : : ATTACKER ) ? - 1 : - 2 ) ;
if ( ! tacticsMode )
2010-03-02 13:40:29 +02:00
{
2017-07-20 06:08:49 +02:00
logGlobal - > trace ( " Setting command for %s " , ( actor ? actor - > nodeName ( ) : " hero " ) ) ;
2011-12-14 00:35:28 +03:00
myTurn = false ;
2022-11-20 19:11:34 +02:00
stacksController - > setActiveStack ( nullptr ) ;
2017-07-20 06:08:49 +02:00
givenCommand . setn ( command ) ;
2011-12-14 00:35:28 +03:00
}
else
{
2017-07-20 06:08:49 +02:00
curInt - > cb - > battleMakeTacticAction ( command ) ;
vstd : : clear_pointer ( command ) ;
2022-11-20 19:11:34 +02:00
stacksController - > setActiveStack ( nullptr ) ;
2012-04-03 02:23:14 +03:00
//next stack will be activated when action ends
2010-03-02 13:40:29 +02:00
}
2009-01-15 19:01:08 +02:00
}
2017-07-01 10:34:00 +02:00
const CGHeroInstance * CBattleInterface : : getActiveHero ( )
2011-12-14 00:35:28 +03:00
{
2022-11-20 19:11:34 +02:00
const CStack * attacker = stacksController - > getActiveStack ( ) ;
2017-07-01 10:34:00 +02:00
if ( ! attacker )
2009-01-15 19:01:08 +02:00
{
2013-06-26 14:18:27 +03:00
return nullptr ;
2011-12-14 00:35:28 +03:00
}
2017-07-01 10:34:00 +02:00
if ( attacker - > side = = BattleSide : : ATTACKER )
2011-12-14 00:35:28 +03:00
{
return attackingHeroInstance ;
}
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
return defendingHeroInstance ;
}
void CBattleInterface : : hexLclicked ( int whichOne )
{
2012-03-31 00:36:07 +03:00
handleHex ( whichOne , LCLICK ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : stackIsCatapulting ( const CatapultAttack & ca )
2009-09-15 15:20:11 +03:00
{
2022-11-17 18:50:12 +02:00
if ( siegeController )
siegeController - > stackIsCatapulting ( ca ) ;
}
2016-02-10 06:10:32 +02:00
2022-11-17 18:50:12 +02:00
void CBattleInterface : : gateStateChanged ( const EGateState state )
{
if ( siegeController )
siegeController - > gateStateChanged ( state ) ;
2009-09-15 15:20:11 +03:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : battleFinished ( const BattleResult & br )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
bresult = & br ;
2012-02-20 00:03:43 +03:00
{
2016-11-25 21:12:22 +02:00
auto unlockPim = vstd : : makeUnlockGuard ( * CPlayerInterface : : pim ) ;
2012-02-20 00:03:43 +03:00
animsAreDisplayed . waitUntil ( false ) ;
}
2022-11-20 19:11:34 +02:00
stacksController - > setActiveStack ( nullptr ) ;
2017-05-13 09:54:03 +02:00
displayBattleFinished ( ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : displayBattleFinished ( )
2009-04-16 17:01:27 +03:00
{
2012-12-14 18:32:53 +03:00
CCS - > curh - > changeGraphic ( ECursor : : ADVENTURE , 0 ) ;
2017-06-03 07:25:10 +02:00
if ( settings [ " session " ] [ " spectate " ] . Bool ( ) & & settings [ " session " ] [ " spectate-skip-battle-result " ] . Bool ( ) )
{
2018-07-25 00:36:48 +02:00
close ( ) ;
2017-06-03 07:25:10 +02:00
return ;
}
2012-02-16 20:10:58 +03:00
2018-07-25 00:36:48 +02:00
GH . pushInt ( std : : make_shared < CBattleResultWindow > ( * bresult , * ( this - > curInt ) ) ) ;
2017-03-19 04:15:31 +02:00
curInt - > waitWhileDialog ( ) ; // Avoid freeze when AI end turn after battle. Check bug #1897
2018-07-25 00:36:48 +02:00
CPlayerInterface : : battleInt = nullptr ;
2009-04-16 17:01:27 +03:00
}
2017-09-08 13:25:12 +02:00
void CBattleInterface : : spellCast ( const BattleSpellCast * sc )
2009-08-26 17:09:55 +03:00
{
2017-07-20 06:08:49 +02:00
const SpellID spellID = sc - > spellID ;
const CSpell * spell = spellID . toSpell ( ) ;
if ( ! spell )
return ;
2009-08-26 17:09:55 +03:00
2017-07-20 06:08:49 +02:00
const std : : string & castSoundPath = spell - > getCastSound ( ) ;
2015-02-07 10:13:24 +02:00
2016-10-28 23:37:20 +02:00
if ( ! castSoundPath . empty ( ) )
2014-03-10 19:00:58 +03:00
CCS - > soundh - > playSound ( castSoundPath ) ;
2014-11-27 20:06:11 +02:00
2017-09-08 13:25:12 +02:00
const auto casterStackID = sc - > casterStack ;
const CStack * casterStack = nullptr ;
if ( casterStackID > = 0 )
2014-11-27 20:06:11 +02:00
{
2017-09-08 13:25:12 +02:00
casterStack = curInt - > cb - > battleGetStackByID ( casterStackID ) ;
}
2014-11-27 20:33:57 +02:00
2017-09-08 13:25:12 +02:00
Point srccoord = ( sc - > side ? Point ( 770 , 60 ) : Point ( 30 , 60 ) ) + pos ; //hero position by default
{
if ( casterStack ! = nullptr )
2014-11-27 20:06:11 +02:00
{
2017-07-20 06:08:49 +02:00
srccoord = CClickableHex : : getXYUnitAnim ( casterStack - > getPosition ( ) , casterStack , this ) ;
2017-09-08 13:25:12 +02:00
srccoord . x + = 250 ;
srccoord . y + = 240 ;
2014-11-27 20:06:11 +02:00
}
2014-11-27 20:33:57 +02:00
}
2017-09-08 13:25:12 +02:00
if ( casterStack ! = nullptr & & sc - > activeCast )
{
//todo: custom cast animation for hero
2017-07-20 06:08:49 +02:00
displaySpellCast ( spellID , casterStack - > getPosition ( ) ) ;
2017-09-08 13:25:12 +02:00
2022-11-20 19:11:34 +02:00
stacksController - > addNewAnim ( new CCastAnimation ( this , casterStack , sc - > tile , curInt - > cb - > battleGetStackByPos ( sc - > tile ) ) ) ;
2017-09-08 13:25:12 +02:00
}
waitForAnims ( ) ; //wait for cast animation
2015-03-22 11:18:58 +02:00
2014-11-26 23:27:38 +02:00
//playing projectile animation
2016-10-28 23:37:20 +02:00
if ( sc - > tile . isValid ( ) )
2014-11-27 20:06:11 +02:00
{
2014-11-26 23:27:38 +02:00
Point destcoord = CClickableHex : : getXYUnitAnim ( sc - > tile , curInt - > cb - > battleGetStackByPos ( sc - > tile ) , this ) ; //position attacked by projectile
destcoord . x + = 250 ; destcoord . y + = 240 ;
//animation angle
double angle = atan2 ( static_cast < double > ( destcoord . x - srccoord . x ) , static_cast < double > ( destcoord . y - srccoord . y ) ) ;
bool Vflip = ( angle < 0 ) ;
2016-10-28 23:37:20 +02:00
if ( Vflip )
2014-11-26 23:27:38 +02:00
angle = - angle ;
2015-02-07 10:13:24 +02:00
2017-07-20 06:08:49 +02:00
std : : string animToDisplay = spell - > animationInfo . selectProjectile ( angle ) ;
2015-02-07 10:13:24 +02:00
2017-09-05 19:45:29 +02:00
if ( ! animToDisplay . empty ( ) )
2009-08-26 17:09:55 +03:00
{
2017-09-05 19:45:29 +02:00
//TODO: calculate inside CEffectAnimation
std : : shared_ptr < CAnimation > tmp = std : : make_shared < CAnimation > ( animToDisplay ) ;
tmp - > load ( 0 , 0 ) ;
2018-03-30 13:02:04 +02:00
auto first = tmp - > getImage ( 0 , 0 ) ;
2017-09-05 19:45:29 +02:00
2011-12-14 00:35:28 +03:00
//displaying animation
2013-07-16 21:12:47 +03:00
double diffX = ( destcoord . x - srccoord . x ) * ( destcoord . x - srccoord . x ) ;
double diffY = ( destcoord . y - srccoord . y ) * ( destcoord . y - srccoord . y ) ;
double distance = sqrt ( diffX + diffY ) ;
2012-02-16 20:10:58 +03:00
2020-10-01 10:38:06 +02:00
int steps = static_cast < int > ( distance / AnimationControls : : getSpellEffectSpeed ( ) + 1 ) ;
2017-09-05 19:45:29 +02:00
int dx = ( destcoord . x - srccoord . x - first - > width ( ) ) / steps ;
int dy = ( destcoord . y - srccoord . y - first - > height ( ) ) / steps ;
2011-12-14 00:35:28 +03:00
2022-11-20 19:11:34 +02:00
stacksController - > addNewAnim ( new CEffectAnimation ( this , animToDisplay , srccoord . x , srccoord . y , dx , dy , Vflip ) ) ;
2014-11-26 23:27:38 +02:00
}
2015-03-22 11:18:58 +02:00
}
2017-09-08 13:25:12 +02:00
waitForAnims ( ) ; //wait for projectile animation
2015-03-22 11:18:58 +02:00
2014-11-27 23:36:14 +02:00
displaySpellHit ( spellID , sc - > tile ) ;
2015-03-22 11:18:58 +02:00
2015-09-14 04:45:05 +02:00
//queuing affect animation
2017-07-20 06:08:49 +02:00
for ( auto & elem : sc - > affectedCres )
2014-11-26 23:27:38 +02:00
{
2017-07-20 06:08:49 +02:00
auto stack = curInt - > cb - > battleGetStackByID ( elem , false ) ;
if ( stack )
displaySpellEffect ( spellID , stack - > getPosition ( ) ) ;
2015-09-14 04:45:05 +02:00
}
2015-02-07 10:13:24 +02:00
2015-09-14 05:21:49 +02:00
//queuing additional animation
2017-07-20 06:08:49 +02:00
for ( auto & elem : sc - > customEffects )
2015-09-14 04:45:05 +02:00
{
2017-07-20 06:08:49 +02:00
auto stack = curInt - > cb - > battleGetStackByID ( elem . stack , false ) ;
if ( stack )
displayEffect ( elem . effect , stack - > getPosition ( ) ) ;
2014-11-26 23:27:38 +02:00
}
2011-12-14 00:35:28 +03:00
waitForAnims ( ) ;
//mana absorption
2016-10-28 23:37:20 +02:00
if ( sc - > manaGained > 0 )
2011-12-14 00:35:28 +03:00
{
2011-12-22 16:05:19 +03:00
Point leftHero = Point ( 15 , 30 ) + pos ;
Point rightHero = Point ( 755 , 30 ) + pos ;
2022-11-20 19:11:34 +02:00
stacksController - > addNewAnim ( new CEffectAnimation ( this , sc - > side ? " SP07_A.DEF " : " SP07_B.DEF " , leftHero . x , leftHero . y , 0 , 0 , false ) ) ;
stacksController - > addNewAnim ( new CEffectAnimation ( this , sc - > side ? " SP07_B.DEF " : " SP07_A.DEF " , rightHero . x , rightHero . y , 0 , 0 , false ) ) ;
2009-01-15 19:01:08 +02:00
}
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : battleStacksEffectsSet ( const SetStackEffect & sse )
2009-01-15 19:01:08 +02:00
{
2022-11-20 19:11:34 +02:00
if ( stacksController - > getActiveStack ( ) ! = nullptr )
fieldController - > redrawBackgroundWithHexes ( ) ;
2011-12-14 00:35:28 +03:00
}
2011-05-26 17:47:45 +03:00
2017-09-27 16:35:30 +02:00
void CBattleInterface : : setHeroAnimation ( ui8 side , int phase )
{
if ( side = = BattleSide : : ATTACKER )
2017-10-07 23:04:43 +02:00
{
if ( attackingHero )
attackingHero - > setPhase ( phase ) ;
}
2017-09-27 16:35:30 +02:00
else
2017-10-07 23:04:43 +02:00
{
if ( defendingHero )
defendingHero - > setPhase ( phase ) ;
}
2017-09-27 16:35:30 +02:00
}
2016-09-10 18:26:55 +02:00
void CBattleInterface : : castThisSpell ( SpellID spellID )
{
2018-07-25 00:36:48 +02:00
spellToCast = std : : make_shared < BattleAction > ( ) ;
2017-07-20 06:08:49 +02:00
spellToCast - > actionType = EActionType : : HERO_SPELL ;
spellToCast - > actionSubtype = spellID ; //spell number
spellToCast - > stackNumber = ( attackingHeroInstance - > tempOwner = = curInt - > playerID ) ? - 1 : - 2 ;
spellToCast - > side = defendingHeroInstance ? ( curInt - > playerID = = defendingHeroInstance - > tempOwner ) : false ;
2016-09-10 18:26:55 +02:00
spellDestSelectMode = true ;
creatureCasting = false ;
2016-09-10 18:38:14 +02:00
//choosing possible targets
2016-10-28 23:37:20 +02:00
const CGHeroInstance * castingHero = ( attackingHeroInstance - > tempOwner = = curInt - > playerID ) ? attackingHeroInstance : defendingHeroInstance ;
2016-09-10 18:26:55 +02:00
assert ( castingHero ) ; // code below assumes non-null hero
sp = spellID . toSpell ( ) ;
2019-05-04 05:42:55 +02:00
PossiblePlayerBattleAction spellSelMode = curInt - > cb - > getCasterAction ( sp , castingHero , spells : : Mode : : HERO ) ;
2016-09-10 18:26:55 +02:00
2019-05-04 05:42:55 +02:00
if ( spellSelMode = = PossiblePlayerBattleAction : : NO_LOCATION ) //user does not have to select location
2011-12-14 00:35:28 +03:00
{
2017-07-20 06:08:49 +02:00
spellToCast - > aimToHex ( BattleHex : : INVALID ) ;
2018-07-25 00:36:48 +02:00
curInt - > cb - > battleMakeAction ( spellToCast . get ( ) ) ;
2011-12-14 00:35:28 +03:00
endCastingSpell ( ) ;
}
else
2009-09-24 16:44:55 +03:00
{
2012-04-16 20:12:39 +03:00
possibleActions . clear ( ) ;
2012-05-07 19:04:43 +03:00
possibleActions . push_back ( spellSelMode ) ; //only this one action can be performed at the moment
2012-02-16 20:10:58 +03:00
GH . fakeMouseMove ( ) ; //update cursor
2009-09-24 16:44:55 +03:00
}
2011-12-14 00:35:28 +03:00
}
2011-01-14 20:08:01 +02:00
2017-07-20 06:08:49 +02:00
void CBattleInterface : : displayBattleLog ( const std : : vector < MetaString > & battleLog )
{
for ( const auto & line : battleLog )
{
std : : string formatted = line . toString ( ) ;
boost : : algorithm : : trim ( formatted ) ;
2022-11-18 17:54:10 +02:00
if ( ! controlPanel - > console - > addText ( formatted ) )
2017-07-20 06:08:49 +02:00
logGlobal - > warn ( " Too long battle log line " ) ;
}
}
void CBattleInterface : : displayCustomEffects ( const std : : vector < CustomEffectInfo > & customEffects )
{
for ( const CustomEffectInfo & one : customEffects )
{
if ( one . sound ! = 0 )
CCS - > soundh - > playSound ( soundBase : : soundID ( one . sound ) ) ;
const CStack * s = curInt - > cb - > battleGetStackByID ( one . stack , false ) ;
if ( s & & one . effect ! = 0 )
displayEffect ( one . effect , s - > getPosition ( ) ) ;
}
}
2017-09-05 19:45:29 +02:00
void CBattleInterface : : displayEffect ( ui32 effect , BattleHex destTile )
2011-12-14 00:35:28 +03:00
{
2017-09-05 19:45:29 +02:00
std : : string customAnim = graphics - > battleACToDef [ effect ] [ 0 ] ;
2022-11-20 19:11:34 +02:00
stacksController - > addNewAnim ( new CEffectAnimation ( this , customAnim , destTile ) ) ;
2011-12-14 00:35:28 +03:00
}
2011-01-14 20:08:01 +02:00
2021-02-20 03:57:50 +02:00
void CBattleInterface : : displaySpellAnimationQueue ( const CSpell : : TAnimationQueue & q , BattleHex destinationTile )
2015-03-22 15:47:20 +02:00
{
2021-02-20 03:57:50 +02:00
for ( const CSpell : : TAnimation & animation : q )
2015-03-22 15:47:20 +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 ( animation . pause > 0 )
2022-11-20 19:11:34 +02:00
stacksController - > addNewAnim ( new CDummyAnimation ( this , animation . pause ) ) ;
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
else
2022-11-20 19:11:34 +02:00
stacksController - > addNewAnim ( new CEffectAnimation ( this , animation . resourceName , destinationTile , false , animation . verticalPosition = = VerticalPosition : : BOTTOM ) ) ;
2016-01-26 08:37:55 +02:00
}
2015-03-22 15:47:20 +02:00
}
2021-02-20 03:57:50 +02:00
void CBattleInterface : : displaySpellCast ( SpellID spellID , BattleHex destinationTile )
2014-11-27 23:36:14 +02:00
{
2021-02-20 03:57:50 +02:00
const CSpell * spell = spellID . toSpell ( ) ;
2015-03-22 11:18:58 +02:00
2021-02-20 03:57:50 +02:00
if ( spell )
displaySpellAnimationQueue ( spell - > animationInfo . cast , destinationTile ) ;
}
2014-11-27 23:36:14 +02:00
2021-02-20 03:57:50 +02:00
void CBattleInterface : : displaySpellEffect ( SpellID spellID , BattleHex destinationTile )
{
const CSpell * spell = spellID . toSpell ( ) ;
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
2021-02-20 03:57:50 +02:00
if ( spell )
displaySpellAnimationQueue ( spell - > animationInfo . affect , destinationTile ) ;
2014-11-27 23:36:14 +02:00
}
2016-09-10 17:56:38 +02:00
void CBattleInterface : : displaySpellHit ( SpellID spellID , BattleHex destinationTile )
2014-11-27 23:36:14 +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 CSpell * spell = spellID . toSpell ( ) ;
2015-03-22 11:18:58 +02:00
2021-02-20 03:57:50 +02:00
if ( spell )
displaySpellAnimationQueue ( spell - > animationInfo . hit , destinationTile ) ;
2014-11-27 23:36:14 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : battleTriggerEffect ( const BattleTriggerEffect & bte )
{
2017-07-20 06:08:49 +02:00
const CStack * stack = curInt - > cb - > battleGetStackByID ( bte . stackID ) ;
if ( ! stack )
{
logGlobal - > error ( " Invalid stack ID %d " , bte . stackID ) ;
return ;
}
2011-12-14 00:35:28 +03:00
//don't show animation when no HP is regenerated
2017-07-20 06:08:49 +02:00
switch ( bte . effect )
2011-01-14 20:08:01 +02:00
{
2017-07-20 06:08:49 +02:00
//TODO: move to bonus type handler
2011-12-14 00:35:28 +03:00
case Bonus : : HP_REGENERATION :
2017-07-20 06:08:49 +02:00
displayEffect ( 74 , stack - > getPosition ( ) ) ;
2012-04-17 11:46:09 +03:00
CCS - > soundh - > playSound ( soundBase : : REGENER ) ;
2011-12-14 00:35:28 +03:00
break ;
case Bonus : : MANA_DRAIN :
2017-07-20 06:08:49 +02:00
displayEffect ( 77 , stack - > getPosition ( ) ) ;
2011-12-14 00:35:28 +03:00
CCS - > soundh - > playSound ( soundBase : : MANADRAI ) ;
break ;
case Bonus : : POISON :
2017-07-20 06:08:49 +02:00
displayEffect ( 67 , stack - > getPosition ( ) ) ;
2011-12-14 00:35:28 +03:00
CCS - > soundh - > playSound ( soundBase : : POISON ) ;
break ;
case Bonus : : FEAR :
2017-07-20 06:08:49 +02:00
displayEffect ( 15 , stack - > getPosition ( ) ) ;
2011-12-14 00:35:28 +03:00
CCS - > soundh - > playSound ( soundBase : : FEAR ) ;
break ;
2012-01-26 19:48:53 +03:00
case Bonus : : MORALE :
{
std : : string hlp = CGI - > generaltexth - > allTexts [ 33 ] ;
boost : : algorithm : : replace_first ( hlp , " %s " , ( stack - > getName ( ) ) ) ;
2017-07-20 06:08:49 +02:00
displayEffect ( 20 , stack - > getPosition ( ) ) ;
2013-05-04 16:14:23 +03:00
CCS - > soundh - > playSound ( soundBase : : GOODMRLE ) ;
2022-11-18 17:54:10 +02:00
controlPanel - > console - > addText ( hlp ) ;
2012-01-26 19:48:53 +03:00
break ;
}
2011-12-14 00:35:28 +03:00
default :
return ;
2011-01-14 20:08:01 +02:00
}
2011-12-14 00:35:28 +03:00
//waitForAnims(); //fixme: freezes game :?
}
2011-01-14 20:08:01 +02:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : setAnimSpeed ( int set )
{
2012-01-12 18:23:00 +03:00
Settings speed = settings . write [ " battle " ] [ " animationSpeed " ] ;
2013-07-06 19:10:20 +03:00
speed - > Float ( ) = float ( set ) / 100 ;
2011-12-14 00:35:28 +03:00
}
2011-02-23 05:57:45 +02:00
2011-12-14 00:35:28 +03:00
int CBattleInterface : : getAnimSpeed ( ) const
{
2017-06-03 07:25:10 +02:00
if ( settings [ " session " ] [ " spectate " ] . Bool ( ) & & ! settings [ " session " ] [ " spectate-battle-speed " ] . isNull ( ) )
2020-10-01 10:38:06 +02:00
return static_cast < int > ( vstd : : round ( settings [ " session " ] [ " spectate-battle-speed " ] . Float ( ) * 100 ) ) ;
2017-06-03 07:25:10 +02:00
2020-10-01 10:38:06 +02:00
return static_cast < int > ( vstd : : round ( settings [ " battle " ] [ " animationSpeed " ] . Float ( ) * 100 ) ) ;
2013-07-06 19:10:20 +03:00
}
2016-10-28 23:37:20 +02:00
CPlayerInterface * CBattleInterface : : getCurrentPlayerInterface ( ) const
2013-11-03 19:44:47 +03:00
{
return curInt . get ( ) ;
}
2022-11-20 19:11:34 +02:00
void CBattleInterface : : trySetActivePlayer ( PlayerColor player )
2013-07-06 19:10:20 +03:00
{
2022-11-20 19:11:34 +02:00
if ( attackerInt & & attackerInt - > playerID = = player )
curInt = attackerInt ;
2013-07-21 13:10:38 +03:00
2022-11-20 19:11:34 +02:00
if ( defenderInt & & defenderInt - > playerID = = player )
curInt = defenderInt ;
2009-09-20 15:47:40 +03:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : activateStack ( )
2009-09-20 15:47:40 +03:00
{
2019-03-22 22:39:53 +02:00
if ( ! battleActionsStarted )
return ; //"show" function should re-call this function
2022-11-20 19:11:34 +02:00
stacksController - > activateStack ( ) ;
2013-11-02 23:07:45 +03:00
2022-11-20 19:11:34 +02:00
const CStack * s = stacksController - > getActiveStack ( ) ;
2022-11-03 20:01:20 +02:00
if ( ! s )
return ;
2009-09-24 16:23:52 +03:00
2022-11-20 19:11:34 +02:00
myTurn = true ;
2011-12-14 00:35:28 +03:00
queue - > update ( ) ;
2022-11-20 19:11:34 +02:00
fieldController - > redrawBackgroundWithHexes ( ) ;
2019-05-04 05:42:55 +02:00
possibleActions = getPossibleActionsForStack ( s ) ;
2012-05-05 00:16:39 +03:00
GH . fakeMouseMove ( ) ;
2011-12-14 00:35:28 +03:00
}
2009-09-20 15:47:40 +03:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : endCastingSpell ( )
2009-09-24 16:23:52 +03:00
{
2018-07-25 00:36:48 +02:00
if ( spellDestSelectMode )
2016-10-15 22:50:12 +02:00
{
2018-07-25 00:36:48 +02:00
spellToCast . reset ( ) ;
2016-10-15 22:50:12 +02:00
sp = nullptr ;
spellDestSelectMode = false ;
CCS - > curh - > changeGraphic ( ECursor : : COMBAT , ECursor : : COMBAT_POINTER ) ;
2022-11-20 19:11:34 +02:00
if ( stacksController - > getActiveStack ( ) )
2016-10-15 22:50:12 +02:00
{
2022-11-20 19:11:34 +02:00
possibleActions = getPossibleActionsForStack ( stacksController - > getActiveStack ( ) ) ; //restore actions after they were cleared
2016-10-15 22:50:12 +02:00
myTurn = true ;
}
}
else
{
2022-11-20 19:11:34 +02:00
if ( stacksController - > getActiveStack ( ) )
2016-10-15 22:50:12 +02:00
{
2022-11-20 19:11:34 +02:00
possibleActions = getPossibleActionsForStack ( stacksController - > getActiveStack ( ) ) ;
2016-10-15 22:50:12 +02:00
GH . fakeMouseMove ( ) ;
}
}
}
void CBattleInterface : : enterCreatureCastingMode ( )
{
//silently check for possible errors
2016-10-28 23:37:20 +02:00
if ( ! myTurn )
2016-10-15 22:50:12 +02:00
return ;
2016-10-28 23:37:20 +02:00
if ( tacticsMode )
2016-10-15 22:50:12 +02:00
return ;
2011-08-25 18:24:37 +03:00
2020-10-06 01:27:04 +02:00
//hero is casting a spell
2016-10-28 23:37:20 +02:00
if ( spellDestSelectMode )
2016-10-15 22:50:12 +02:00
return ;
2022-11-20 19:11:34 +02:00
if ( ! stacksController - > getActiveStack ( ) )
2016-10-15 22:50:12 +02:00
return ;
2015-03-18 23:10:41 +02:00
2022-11-20 19:11:34 +02:00
if ( ! stacksController - > activeStackSpellcaster ( ) )
2016-10-15 22:50:12 +02:00
return ;
//random spellcaster
2022-11-20 19:11:34 +02:00
if ( stacksController - > activeStackSpellToCast ( ) = = SpellID : : NONE )
2016-10-15 22:50:12 +02:00
return ;
2012-04-04 11:03:52 +03:00
2019-05-04 05:42:55 +02:00
if ( vstd : : contains ( possibleActions , PossiblePlayerBattleAction : : NO_LOCATION ) )
2012-05-07 19:04:43 +03:00
{
2022-11-20 19:11:34 +02:00
const spells : : Caster * caster = stacksController - > getActiveStack ( ) ;
const CSpell * spell = stacksController - > activeStackSpellToCast ( ) . toSpell ( ) ;
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
spells : : Target target ;
target . emplace_back ( ) ;
spells : : BattleCast cast ( curInt - > cb . get ( ) , caster , spells : : Mode : : CREATURE_ACTIVE , spell ) ;
auto m = spell - > battleMechanics ( & cast ) ;
spells : : detail : : ProblemImpl ignored ;
2021-02-20 03:57:50 +02:00
const bool isCastingPossible = m - > canBeCastAt ( target , ignored ) ;
2016-10-15 22:50:12 +02:00
2016-10-28 23:37:20 +02:00
if ( isCastingPossible )
2016-10-15 22:50:12 +02:00
{
myTurn = false ;
2022-11-20 19:11:34 +02:00
giveCommand ( EActionType : : MONSTER_SPELL , BattleHex : : INVALID , stacksController - > activeStackSpellToCast ( ) ) ;
stacksController - > setSelectedStack ( nullptr ) ;
2016-10-15 22:50:12 +02:00
CCS - > curh - > changeGraphic ( ECursor : : COMBAT , ECursor : : COMBAT_POINTER ) ;
}
}
else
{
2022-11-20 19:11:34 +02:00
possibleActions = getPossibleActionsForStack ( stacksController - > getActiveStack ( ) ) ;
2019-05-04 05:42:55 +02:00
auto actionFilterPredicate = [ ] ( const PossiblePlayerBattleAction x )
{
return ( x ! = PossiblePlayerBattleAction : : ANY_LOCATION ) & & ( x ! = PossiblePlayerBattleAction : : NO_LOCATION ) & &
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
( x ! = PossiblePlayerBattleAction : : FREE_LOCATION ) & & ( x ! = PossiblePlayerBattleAction : : AIMED_SPELL_CREATURE ) & &
2019-05-04 05:42:55 +02:00
( x ! = PossiblePlayerBattleAction : : OBSTACLE ) ;
} ;
vstd : : erase_if ( possibleActions , actionFilterPredicate ) ;
2016-10-15 22:50:12 +02:00
GH . fakeMouseMove ( ) ;
2012-05-07 19:04:43 +03:00
}
2012-04-04 11:03:52 +03:00
}
2019-05-04 05:42:55 +02:00
std : : vector < PossiblePlayerBattleAction > CBattleInterface : : getPossibleActionsForStack ( const CStack * stack )
2012-04-04 11:03:52 +03:00
{
2019-05-04 05:42:55 +02:00
BattleClientInterfaceData data ; //hard to get rid of these things so for now they're required data to pass
2022-11-20 19:11:34 +02:00
data . creatureSpellToCast = stacksController - > activeStackSpellToCast ( ) ;
2019-05-04 05:42:55 +02:00
data . tacticsMode = tacticsMode ;
auto allActions = curInt - > cb - > getClientActionsForStack ( stack , data ) ;
2016-10-15 22:50:12 +02:00
2019-05-04 05:42:55 +02:00
return std : : vector < PossiblePlayerBattleAction > ( allActions ) ;
}
2012-04-04 11:03:52 +03:00
2019-05-04 05:42:55 +02:00
void CBattleInterface : : reorderPossibleActionsPriority ( const CStack * stack , MouseHoveredHexContext context )
{
if ( tacticsMode | | possibleActions . empty ( ) ) return ; //this function is not supposed to be called in tactics mode or before getPossibleActionsForStack
2012-04-04 11:03:52 +03:00
2019-05-04 05:42:55 +02:00
auto assignPriority = [ & ] ( PossiblePlayerBattleAction const & item ) - > uint8_t //large lambda assigning priority which would have to be part of possibleActions without it
{
switch ( item )
{
case PossiblePlayerBattleAction : : AIMED_SPELL_CREATURE :
case PossiblePlayerBattleAction : : ANY_LOCATION :
case PossiblePlayerBattleAction : : NO_LOCATION :
case PossiblePlayerBattleAction : : FREE_LOCATION :
case PossiblePlayerBattleAction : : OBSTACLE :
if ( ! stack - > hasBonusOfType ( Bonus : : NO_SPELLCAST_BY_DEFAULT ) & & context = = MouseHoveredHexContext : : OCCUPIED_HEX )
return 1 ;
2021-02-20 03:57:50 +02:00
else
2019-05-04 05:42:55 +02:00
return 100 ; //bottom priority
break ;
case PossiblePlayerBattleAction : : RANDOM_GENIE_SPELL :
return 2 ; break ;
case PossiblePlayerBattleAction : : RISE_DEMONS :
return 3 ; break ;
case PossiblePlayerBattleAction : : SHOOT :
return 4 ; break ;
case PossiblePlayerBattleAction : : ATTACK_AND_RETURN :
return 5 ; break ;
case PossiblePlayerBattleAction : : ATTACK :
return 6 ; break ;
case PossiblePlayerBattleAction : : WALK_AND_ATTACK :
return 7 ; break ;
case PossiblePlayerBattleAction : : MOVE_STACK :
return 8 ; break ;
case PossiblePlayerBattleAction : : CATAPULT :
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
return 9 ; break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : HEAL :
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
return 10 ; break ;
2019-05-04 05:42:55 +02:00
default :
return 200 ; break ;
}
} ;
2012-04-04 11:03:52 +03:00
2019-05-04 05:42:55 +02:00
auto comparer = [ & ] ( PossiblePlayerBattleAction const & lhs , PossiblePlayerBattleAction const & rhs )
{
return assignPriority ( lhs ) > assignPriority ( rhs ) ;
} ;
2016-10-15 22:50:12 +02:00
2019-05-04 05:42:55 +02:00
std : : make_heap ( possibleActions . begin ( ) , possibleActions . end ( ) , comparer ) ;
2011-12-14 00:35:28 +03:00
}
2011-02-14 22:31:53 +02:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : endAction ( const BattleAction * action )
2012-02-16 20:10:58 +03:00
{
2016-10-28 23:37:20 +02:00
const CStack * stack = curInt - > cb - > battleGetStackByID ( action - > stackNumber ) ;
2013-05-19 18:14:23 +03:00
2017-07-20 06:08:49 +02:00
if ( action - > actionType = = EActionType : : HERO_SPELL )
2017-09-27 16:35:30 +02:00
setHeroAnimation ( action - > side , 0 ) ;
2012-05-05 00:16:39 +03:00
2022-11-20 19:11:34 +02:00
stacksController - > endAction ( action ) ;
2009-01-15 19:01:08 +02:00
2011-12-14 00:35:28 +03:00
queue - > update ( ) ;
2016-10-28 23:37:20 +02:00
if ( tacticsMode ) //stack ended movement in tactics phase -> select the next one
2022-11-18 17:54:10 +02:00
tacticNextStack ( stack ) ;
2012-04-03 02:23:14 +03:00
2017-07-20 06:08:49 +02:00
if ( action - > actionType = = EActionType : : HERO_SPELL ) //we have activated next stack after sending request that has been just realized -> blockmap due to movement has changed
2022-11-20 19:11:34 +02:00
fieldController - > redrawBackgroundWithHexes ( ) ;
// if (stacksController->getActiveStack() && !animsAreDisplayed.get() && pendingAnims.empty() && !active)
// {
// logGlobal->warn("Something wrong... interface was deactivated but there is no animation. Reactivating...");
// controlPanel->blockUI(false);
// }
// else
// {
2013-07-21 13:10:38 +03:00
// block UI if no active stack (e.g. enemy turn);
2022-11-20 19:11:34 +02:00
controlPanel - > blockUI ( stacksController - > getActiveStack ( ) = = nullptr ) ;
// }
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : hideQueue ( )
2009-01-15 19:01:08 +02:00
{
2012-01-12 18:23:00 +03:00
Settings showQueue = settings . write [ " battle " ] [ " showQueue " ] ;
showQueue - > Bool ( ) = false ;
2011-12-14 00:35:28 +03:00
queue - > deactivate ( ) ;
2016-10-28 23:37:20 +02:00
if ( ! queue - > embedded )
2009-01-15 19:01:08 +02:00
{
2011-12-22 16:05:19 +03:00
moveBy ( Point ( 0 , - queue - > pos . h / 2 ) ) ;
2011-12-14 00:35:28 +03:00
GH . totalRedraw ( ) ;
2009-01-15 19:01:08 +02:00
}
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : showQueue ( )
2009-01-15 19:01:08 +02:00
{
2012-01-12 18:23:00 +03:00
Settings showQueue = settings . write [ " battle " ] [ " showQueue " ] ;
showQueue - > Bool ( ) = true ;
2009-01-15 19:01:08 +02:00
2011-12-14 00:35:28 +03:00
queue - > activate ( ) ;
2009-01-15 19:01:08 +02:00
2016-10-28 23:37:20 +02:00
if ( ! queue - > embedded )
2011-12-14 00:35:28 +03:00
{
2011-12-22 16:05:19 +03:00
moveBy ( Point ( 0 , + queue - > pos . h / 2 ) ) ;
2011-12-14 00:35:28 +03:00
GH . totalRedraw ( ) ;
}
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : startAction ( const BattleAction * action )
2009-01-15 19:01:08 +02:00
{
2013-07-16 21:12:47 +03:00
//setActiveStack(nullptr);
2022-11-18 17:54:10 +02:00
controlPanel - > blockUI ( true ) ;
2013-07-06 19:10:20 +03:00
2017-07-20 06:08:49 +02:00
if ( action - > actionType = = EActionType : : END_TACTIC_PHASE )
2009-01-15 19:01:08 +02:00
{
2022-11-18 17:54:10 +02:00
controlPanel - > tacticPhaseEnded ( ) ;
2011-12-14 00:35:28 +03:00
return ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
const CStack * stack = curInt - > cb - > battleGetStackByID ( action - > stackNumber ) ;
2016-10-28 23:37:20 +02:00
if ( stack )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
queue - > update ( ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
else
2009-01-15 19:01:08 +02:00
{
2017-07-20 06:08:49 +02:00
assert ( action - > actionType = = EActionType : : HERO_SPELL ) ; //only cast spell is valid action without acting stack number
2009-01-15 19:01:08 +02:00
}
2022-11-20 19:11:34 +02:00
stacksController - > startAction ( action ) ;
2010-08-30 02:12:34 +03:00
2013-07-15 11:51:48 +03:00
redraw ( ) ; // redraw after deactivation, including proper handling of hovered hexes
2011-12-14 00:35:28 +03:00
2017-07-20 06:08:49 +02:00
if ( action - > actionType = = EActionType : : HERO_SPELL ) //when hero casts spell
2011-12-14 00:35:28 +03:00
{
2017-09-27 16:35:30 +02:00
setHeroAnimation ( action - > side , 4 ) ;
2011-12-14 00:35:28 +03:00
return ;
2010-08-30 02:12:34 +03:00
}
2017-09-27 16:35:30 +02:00
2016-10-28 23:37:20 +02:00
if ( ! stack )
2010-08-30 02:12:34 +03:00
{
2017-08-11 13:38:10 +02:00
logGlobal - > error ( " Something wrong with stackNumber in actionStarted. Stack number: %d " , action - > stackNumber ) ;
2011-12-14 00:35:28 +03:00
return ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
int txtid = 0 ;
switch ( action - > actionType )
{
2017-07-20 06:08:49 +02:00
case EActionType : : WAIT :
2011-12-14 00:35:28 +03:00
txtid = 136 ;
break ;
2017-07-20 06:08:49 +02:00
case EActionType : : BAD_MORALE :
2011-12-14 00:35:28 +03:00
txtid = - 34 ; //negative -> no separate singular/plural form
2017-07-20 06:08:49 +02:00
displayEffect ( 30 , stack - > getPosition ( ) ) ;
2013-05-04 16:14:23 +03:00
CCS - > soundh - > playSound ( soundBase : : BADMRLE ) ;
2011-12-14 00:35:28 +03:00
break ;
}
2009-01-15 19:01:08 +02:00
2017-07-04 13:24:46 +02:00
if ( txtid ! = 0 )
2022-11-18 17:54:10 +02:00
controlPanel - > console - > addText ( stack - > formatGeneralMessage ( txtid ) ) ;
2009-01-15 19:01:08 +02:00
2011-12-14 00:35:28 +03:00
//displaying special abilities
2022-11-20 19:11:34 +02:00
auto actionTarget = action - > getTarget ( curInt - > cb . get ( ) ) ;
2017-07-04 13:24:46 +02:00
switch ( action - > actionType )
2011-01-08 21:38:42 +02:00
{
2017-07-20 06:08:49 +02:00
case EActionType : : STACK_HEAL :
displayEffect ( 74 , actionTarget . at ( 0 ) . hexValue ) ;
2011-12-14 00:35:28 +03:00
CCS - > soundh - > playSound ( soundBase : : REGENER ) ;
break ;
2011-01-08 21:38:42 +02:00
}
2011-12-14 00:35:28 +03:00
}
2011-01-08 21:38:42 +02:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : waitForAnims ( )
{
2016-11-25 21:12:22 +02:00
auto unlockPim = vstd : : makeUnlockGuard ( * CPlayerInterface : : pim ) ;
2011-12-14 00:35:28 +03:00
animsAreDisplayed . waitWhileTrue ( ) ;
2009-01-15 19:01:08 +02:00
}
2022-11-18 17:54:10 +02:00
void CBattleInterface : : tacticPhaseEnd ( )
2009-01-15 19:01:08 +02:00
{
2022-11-20 19:11:34 +02:00
stacksController - > setActiveStack ( nullptr ) ;
2022-11-18 17:54:10 +02:00
controlPanel - > blockUI ( true ) ;
2011-12-14 00:35:28 +03:00
tacticsMode = false ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
static bool immobile ( const CStack * s )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
return ! s - > Speed ( 0 , true ) ; //should bound stacks be immobile?
2009-01-15 19:01:08 +02:00
}
2022-11-18 17:54:10 +02:00
void CBattleInterface : : tacticNextStack ( const CStack * current )
2009-01-15 19:01:08 +02:00
{
2016-10-28 23:37:20 +02:00
if ( ! current )
2022-11-20 19:11:34 +02:00
current = stacksController - > getActiveStack ( ) ;
2012-04-03 02:23:14 +03:00
2012-03-01 14:57:38 +03:00
//no switching stacks when the current one is moving
2012-04-03 02:23:14 +03:00
waitForAnims ( ) ;
2012-03-01 14:57:38 +03:00
2011-12-14 00:35:28 +03:00
TStacks stacksOfMine = tacticianInterface - > cb - > battleGetStacks ( CBattleCallback : : ONLY_MINE ) ;
2016-10-28 23:37:20 +02:00
vstd : : erase_if ( stacksOfMine , & immobile ) ;
if ( stacksOfMine . empty ( ) )
2016-08-09 17:15:58 +02:00
{
2022-11-18 17:54:10 +02:00
tacticPhaseEnd ( ) ;
2016-08-09 17:15:58 +02:00
return ;
}
2013-06-29 16:05:48 +03:00
auto it = vstd : : find ( stacksOfMine , current ) ;
2016-10-28 23:37:20 +02:00
if ( it ! = stacksOfMine . end ( ) & & + + it ! = stacksOfMine . end ( ) )
2011-12-14 00:35:28 +03:00
stackActivated ( * it ) ;
else
stackActivated ( stacksOfMine . front ( ) ) ;
2012-04-04 11:03:52 +03:00
2009-01-15 19:01:08 +02:00
}
2009-08-24 15:55:05 +03:00
2012-03-31 00:36:07 +03:00
std : : string formatDmgRange ( std : : pair < ui32 , ui32 > dmgRange )
{
2016-10-28 23:37:20 +02:00
if ( dmgRange . first ! = dmgRange . second )
2012-03-31 00:36:07 +03:00
return ( boost : : format ( " %d - %d " ) % dmgRange . first % dmgRange . second ) . str ( ) ;
else
return ( boost : : format ( " %d " ) % dmgRange . first ) . str ( ) ;
}
2022-11-20 19:11:34 +02:00
bool CBattleInterface : : canStackMoveHere ( const CStack * stackToMove , BattleHex myNumber )
2012-04-18 12:01:08 +03:00
{
2022-11-20 19:11:34 +02:00
std : : vector < BattleHex > acc = curInt - > cb - > battleGetAvailableHexes ( stackToMove ) ;
BattleHex shiftedDest = myNumber . cloneInDirection ( stackToMove - > destShiftDir ( ) , false ) ;
2012-04-18 12:01:08 +03:00
if ( vstd : : contains ( acc , myNumber ) )
return true ;
2022-11-20 19:11:34 +02:00
else if ( stackToMove - > doubleWide ( ) & & vstd : : contains ( acc , shiftedDest ) )
2012-04-18 12:01:08 +03:00
return true ;
else
return false ;
}
2012-03-31 00:36:07 +03:00
void CBattleInterface : : handleHex ( BattleHex myNumber , int eventType )
{
2019-03-22 22:39:53 +02:00
if ( ! myTurn | | ! battleActionsStarted ) //we are not permit to do anything
2012-04-04 11:03:52 +03:00
return ;
2012-03-31 00:36:07 +03:00
2014-03-07 16:21:09 +03:00
// This function handles mouse move over hexes and l-clicking on them.
2012-03-31 00:36:07 +03:00
// First we decide what happens if player clicks on this hex and set appropriately
// consoleMsg, cursorFrame/Type and prepare lambda realizeAction.
2014-03-07 16:21:09 +03:00
//
2012-03-31 00:36:07 +03:00
// Then, depending whether it was hover/click we either call the action or set tooltip/cursor.
//used when hovering -> tooltip message and cursor to be set
std : : string consoleMsg ;
bool setCursor = true ; //if we want to suppress setting cursor
2012-12-14 18:32:53 +03:00
ECursor : : ECursorTypes cursorType = ECursor : : COMBAT ;
int cursorFrame = ECursor : : COMBAT_POINTER ; //TODO: is this line used?
2014-03-07 16:21:09 +03:00
2012-03-31 00:36:07 +03:00
//used when l-clicking -> action to be called upon the click
std : : function < void ( ) > realizeAction ;
2012-08-28 18:38:00 +03:00
//Get stack on the hex - first try to grab the alive one, if not found -> allow dead stacks.
2017-07-20 06:08:49 +02:00
const CStack * shere = curInt - > cb - > battleGetStackByPos ( myNumber , true ) ;
if ( ! shere )
2012-08-28 18:38:00 +03:00
shere = curInt - > cb - > battleGetStackByPos ( myNumber , false ) ;
2012-03-31 00:36:07 +03:00
2022-11-20 19:11:34 +02:00
if ( ! stacksController - > getActiveStack ( ) )
2012-03-31 00:36:07 +03:00
return ;
2012-04-16 20:12:39 +03:00
bool ourStack = false ;
if ( shere )
ourStack = shere - > owner = = curInt - > playerID ;
2014-03-07 16:21:09 +03:00
2022-11-20 19:11:34 +02:00
//stack may have changed, update selection border
stacksController - > setHoveredStack ( shere ) ;
2013-07-06 19:10:20 +03:00
2012-04-04 11:03:52 +03:00
localActions . clear ( ) ;
2012-04-18 18:57:49 +03:00
illegalActions . clear ( ) ;
2022-11-20 19:11:34 +02:00
reorderPossibleActionsPriority ( stacksController - > getActiveStack ( ) , shere ? MouseHoveredHexContext : : OCCUPIED_HEX : MouseHoveredHexContext : : UNOCCUPIED_HEX ) ;
2016-10-15 22:50:12 +02:00
const bool forcedAction = possibleActions . size ( ) = = 1 ;
2019-05-04 05:42:55 +02:00
for ( PossiblePlayerBattleAction action : possibleActions )
2012-04-04 11:03:52 +03:00
{
2013-07-25 14:53:36 +03:00
bool legalAction = false ; //this action is legal and can be performed
2012-04-18 16:24:18 +03:00
bool notLegal = false ; //this action is not legal and should display message
2014-03-07 16:21:09 +03:00
2012-04-16 20:12:39 +03:00
switch ( action )
2014-03-07 16:21:09 +03:00
{
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : CHOOSE_TACTICS_STACK :
2012-04-16 20:12:39 +03:00
if ( shere & & ourStack )
legalAction = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : MOVE_TACTICS :
case PossiblePlayerBattleAction : : MOVE_STACK :
2012-08-14 09:31:14 +03:00
{
if ( ! ( shere & & shere - > alive ( ) ) ) //we can walk on dead stacks
{
2022-11-20 19:11:34 +02:00
if ( canStackMoveHere ( stacksController - > getActiveStack ( ) , myNumber ) )
2012-08-14 09:31:14 +03:00
legalAction = true ;
}
2012-04-16 20:12:39 +03:00
break ;
2012-08-14 09:31:14 +03:00
}
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : ATTACK :
case PossiblePlayerBattleAction : : WALK_AND_ATTACK :
case PossiblePlayerBattleAction : : ATTACK_AND_RETURN :
2012-03-31 00:36:07 +03:00
{
2022-11-20 19:11:34 +02:00
if ( curInt - > cb - > battleCanAttack ( stacksController - > getActiveStack ( ) , shere , myNumber ) )
2012-04-17 17:50:23 +03:00
{
2022-11-17 23:57:51 +02:00
if ( fieldController - > isTileAttackable ( myNumber ) ) // move isTileAttackable to be part of battleCanAttack?
2012-04-18 12:01:08 +03:00
{
2022-11-17 23:57:51 +02:00
fieldController - > setBattleCursor ( myNumber ) ; // temporary - needed for following function :(
BattleHex attackFromHex = fieldController - > fromWhichHexAttack ( myNumber ) ;
2012-03-31 00:36:07 +03:00
2012-04-18 12:01:08 +03:00
if ( attackFromHex > = 0 ) //we can be in this line when unreachable creature is L - clicked (as of revision 1308)
legalAction = true ;
}
2012-04-17 17:50:23 +03:00
}
2012-04-16 20:12:39 +03:00
}
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : SHOOT :
2022-11-20 19:11:34 +02:00
if ( curInt - > cb - > battleCanShoot ( stacksController - > getActiveStack ( ) , myNumber ) )
2012-04-16 20:12:39 +03:00
legalAction = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : ANY_LOCATION :
2012-05-07 19:04:43 +03:00
if ( myNumber > - 1 ) //TODO: this should be checked for all actions
{
2022-11-20 19:11:34 +02:00
if ( isCastingPossibleHere ( stacksController - > getActiveStack ( ) , shere , myNumber ) )
2016-10-02 00:32:28 +02:00
legalAction = true ;
2012-05-07 19:04:43 +03:00
}
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : AIMED_SPELL_CREATURE :
2022-11-20 19:11:34 +02:00
if ( shere & & isCastingPossibleHere ( stacksController - > getActiveStack ( ) , shere , myNumber ) )
2012-04-16 20:12:39 +03:00
legalAction = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : RANDOM_GENIE_SPELL :
2012-03-31 00:36:07 +03:00
{
2022-11-20 19:11:34 +02:00
if ( shere & & ourStack & & shere ! = stacksController - > getActiveStack ( ) & & shere - > alive ( ) ) //only positive spells for other allied creatures
2012-04-16 20:12:39 +03:00
{
2016-09-09 19:30:36 +02:00
int spellID = curInt - > cb - > battleGetRandomStackSpell ( CRandomGenerator : : getDefault ( ) , shere , CBattleInfoCallback : : RANDOM_GENIE ) ;
2017-07-20 06:08:49 +02:00
if ( spellID > - 1 )
2012-04-16 20:12:39 +03:00
{
legalAction = true ;
}
}
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : OBSTACLE :
2022-11-20 19:11:34 +02:00
if ( isCastingPossibleHere ( stacksController - > getActiveStack ( ) , shere , myNumber ) )
2012-04-28 18:38:34 +03:00
legalAction = true ;
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : TELEPORT :
2012-03-31 00:36:07 +03:00
{
2016-09-24 08:27:58 +02:00
//todo: move to mechanics
2012-04-18 12:01:08 +03:00
ui8 skill = 0 ;
if ( creatureCasting )
2022-11-20 19:11:34 +02:00
skill = stacksController - > getActiveStack ( ) - > getEffectLevel ( SpellID ( SpellID : : TELEPORT ) . toSpell ( ) ) ;
2012-04-18 12:01:08 +03:00
else
2015-09-17 07:42:30 +02:00
skill = getActiveHero ( ) - > getEffectLevel ( SpellID ( SpellID : : TELEPORT ) . toSpell ( ) ) ;
2012-04-17 11:46:09 +03:00
//TODO: explicitely save power, skill
2022-11-20 19:11:34 +02:00
if ( curInt - > cb - > battleCanTeleportTo ( stacksController - > getSelectedStack ( ) , myNumber , skill ) )
2012-04-16 20:12:39 +03:00
legalAction = true ;
2012-03-31 00:36:07 +03:00
else
2012-04-18 16:24:18 +03:00
notLegal = true ;
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : SACRIFICE : //choose our living stack to sacrifice
2022-11-20 19:11:34 +02:00
if ( shere & & shere ! = stacksController - > getSelectedStack ( ) & & ourStack & & shere - > alive ( ) )
2012-04-28 22:40:27 +03:00
legalAction = true ;
else
notLegal = true ;
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : FREE_LOCATION :
2016-03-04 18:11:42 +02:00
legalAction = true ;
2022-11-20 19:11:34 +02:00
if ( ! isCastingPossibleHere ( stacksController - > getActiveStack ( ) , shere , myNumber ) )
2012-05-18 23:50:16 +03:00
{
2016-03-04 18:11:42 +02:00
legalAction = false ;
notLegal = true ;
2012-05-18 23:50:16 +03:00
}
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : CATAPULT :
2022-11-17 18:50:12 +02:00
if ( siegeController & & siegeController - > isCatapultAttackable ( myNumber ) )
2012-04-16 20:12:39 +03:00
legalAction = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : HEAL :
2012-04-16 20:12:39 +03:00
if ( shere & & ourStack & & shere - > canBeHealed ( ) )
legalAction = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : RISE_DEMONS :
2012-04-16 20:12:39 +03:00
if ( shere & & ourStack & & ! shere - > alive ( ) )
2015-11-07 10:42:06 +02:00
{
2016-10-28 23:37:20 +02:00
if ( ! ( shere - > hasBonusOfType ( Bonus : : UNDEAD )
2016-02-28 00:08:56 +02:00
| | shere - > hasBonusOfType ( Bonus : : NON_LIVING )
2020-01-18 11:02:17 +02:00
| | shere - > hasBonusOfType ( Bonus : : GARGOYLE )
2017-07-20 06:08:49 +02:00
| | shere - > summoned
2017-07-04 13:24:46 +02:00
| | shere - > isClone ( )
2015-11-07 10:42:06 +02:00
| | shere - > hasBonusOfType ( Bonus : : SIEGE_WEAPON )
) )
legalAction = true ;
2016-02-28 00:08:56 +02:00
}
2012-04-16 20:12:39 +03:00
break ;
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
if ( legalAction )
localActions . push_back ( action ) ;
2016-10-15 22:50:12 +02:00
else if ( notLegal | | forcedAction )
2012-04-16 20:12:39 +03:00
illegalActions . push_back ( action ) ;
}
2019-05-04 05:42:55 +02:00
illegalAction = PossiblePlayerBattleAction : : INVALID ; //clear it in first place
2012-03-31 00:36:07 +03:00
2012-04-16 20:12:39 +03:00
if ( vstd : : contains ( localActions , selectedAction ) ) //try to use last selected action by default
currentAction = selectedAction ;
2015-03-18 23:10:41 +02:00
else if ( localActions . size ( ) ) //if not possible, select first available action (they are sorted by suggested priority)
2012-04-16 20:12:39 +03:00
currentAction = localActions . front ( ) ;
else //no legal action possible
{
2019-05-04 05:42:55 +02:00
currentAction = PossiblePlayerBattleAction : : INVALID ; //don't allow to do anything
2012-03-31 00:36:07 +03:00
2012-04-16 20:12:39 +03:00
if ( vstd : : contains ( illegalActions , selectedAction ) )
illegalAction = selectedAction ;
else if ( illegalActions . size ( ) )
illegalAction = illegalActions . front ( ) ;
2012-04-17 11:46:09 +03:00
else if ( shere & & ourStack & & shere - > alive ( ) ) //last possibility - display info about our creature
{
2019-05-04 05:42:55 +02:00
currentAction = PossiblePlayerBattleAction : : CREATURE_INFO ;
2012-04-17 11:46:09 +03:00
}
2012-04-16 20:12:39 +03:00
else
2019-05-04 05:42:55 +02:00
illegalAction = PossiblePlayerBattleAction : : INVALID ; //we should never be here
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
bool isCastingPossible = false ;
2012-04-18 16:24:18 +03:00
bool secondaryTarget = false ;
2012-03-31 00:36:07 +03:00
2019-05-04 05:42:55 +02:00
if ( currentAction > PossiblePlayerBattleAction : : INVALID )
2012-03-31 00:36:07 +03:00
{
2012-04-16 20:12:39 +03:00
switch ( currentAction ) //display console message, realize selected action
2012-03-31 00:36:07 +03:00
{
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : CHOOSE_TACTICS_STACK :
2012-04-16 20:12:39 +03:00
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 481 ] ) % shere - > getName ( ) ) . str ( ) ; //Select %s
2017-07-17 14:35:57 +02:00
realizeAction = [ = ] ( ) { stackActivated ( shere ) ; } ;
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : MOVE_TACTICS :
case PossiblePlayerBattleAction : : MOVE_STACK :
2022-11-20 19:11:34 +02:00
if ( stacksController - > getActiveStack ( ) - > hasBonusOfType ( Bonus : : FLYING ) )
2012-04-16 20:12:39 +03:00
{
cursorFrame = ECursor : : COMBAT_FLY ;
2022-11-20 19:11:34 +02:00
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 295 ] ) % stacksController - > getActiveStack ( ) - > getName ( ) ) . str ( ) ; //Fly %s here
2012-04-16 20:12:39 +03:00
}
else
{
cursorFrame = ECursor : : COMBAT_MOVE ;
2022-11-20 19:11:34 +02:00
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 294 ] ) % stacksController - > getActiveStack ( ) - > getName ( ) ) . str ( ) ; //Move %s here
2012-04-16 20:12:39 +03:00
}
2012-03-31 00:36:07 +03:00
2017-07-19 01:06:05 +02:00
realizeAction = [ = ] ( )
2012-03-31 00:36:07 +03:00
{
2022-11-20 19:11:34 +02:00
if ( stacksController - > getActiveStack ( ) - > doubleWide ( ) )
2012-03-31 00:36:07 +03:00
{
2022-11-20 19:11:34 +02:00
std : : vector < BattleHex > acc = curInt - > cb - > battleGetAvailableHexes ( stacksController - > getActiveStack ( ) ) ;
BattleHex shiftedDest = myNumber . cloneInDirection ( stacksController - > getActiveStack ( ) - > destShiftDir ( ) , false ) ;
2017-07-01 10:34:00 +02:00
if ( vstd : : contains ( acc , myNumber ) )
2017-07-20 06:08:49 +02:00
giveCommand ( EActionType : : WALK , myNumber ) ;
2017-07-01 10:34:00 +02:00
else if ( vstd : : contains ( acc , shiftedDest ) )
2017-07-20 06:08:49 +02:00
giveCommand ( EActionType : : WALK , shiftedDest ) ;
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
else
2012-03-31 00:36:07 +03:00
{
2017-07-20 06:08:49 +02:00
giveCommand ( EActionType : : WALK , myNumber ) ;
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
} ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : ATTACK :
case PossiblePlayerBattleAction : : WALK_AND_ATTACK :
case PossiblePlayerBattleAction : : ATTACK_AND_RETURN : //TODO: allow to disable return
2012-03-31 00:36:07 +03:00
{
2022-11-17 23:57:51 +02:00
fieldController - > setBattleCursor ( myNumber ) ; //handle direction of cursor and attackable tile
2017-07-20 06:08:49 +02:00
setCursor = false ; //don't overwrite settings from the call above //TODO: what does it mean?
2012-03-31 00:36:07 +03:00
2019-05-04 05:42:55 +02:00
bool returnAfterAttack = currentAction = = PossiblePlayerBattleAction : : ATTACK_AND_RETURN ;
2017-07-20 06:08:49 +02:00
realizeAction = [ = ] ( )
{
2022-11-17 23:57:51 +02:00
BattleHex attackFromHex = fieldController - > fromWhichHexAttack ( myNumber ) ;
2017-07-20 06:08:49 +02:00
if ( attackFromHex . isValid ( ) ) //we can be in this line when unreachable creature is L - clicked (as of revision 1308)
{
2022-11-20 19:11:34 +02:00
auto command = new BattleAction ( BattleAction : : makeMeleeAttack ( stacksController - > getActiveStack ( ) , myNumber , attackFromHex , returnAfterAttack ) ) ;
sendCommand ( command , stacksController - > getActiveStack ( ) ) ;
2017-07-20 06:08:49 +02:00
}
} ;
2022-11-20 19:11:34 +02:00
TDmgRange damage = curInt - > cb - > battleEstimateDamage ( stacksController - > getActiveStack ( ) , shere ) ;
2020-10-01 10:38:06 +02:00
std : : string estDmgText = formatDmgRange ( std : : make_pair ( ( ui32 ) damage . first , ( ui32 ) damage . second ) ) ; //calculating estimated dmg
2017-07-20 06:08:49 +02:00
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 36 ] ) % shere - > getName ( ) % estDmgText ) . str ( ) ; //Attack %s (%s damage)
}
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : SHOOT :
2012-03-31 00:36:07 +03:00
{
2022-11-20 19:11:34 +02:00
if ( curInt - > cb - > battleHasShootingPenalty ( stacksController - > getActiveStack ( ) , myNumber ) )
2012-04-16 20:12:39 +03:00
cursorFrame = ECursor : : COMBAT_SHOOT_PENALTY ;
else
cursorFrame = ECursor : : COMBAT_SHOOT ;
2017-07-20 06:08:49 +02:00
realizeAction = [ = ] ( ) { giveCommand ( EActionType : : SHOOT , myNumber ) ; } ;
2022-11-20 19:11:34 +02:00
TDmgRange damage = curInt - > cb - > battleEstimateDamage ( stacksController - > getActiveStack ( ) , shere ) ;
2020-10-01 10:38:06 +02:00
std : : string estDmgText = formatDmgRange ( std : : make_pair ( ( ui32 ) damage . first , ( ui32 ) damage . second ) ) ; //calculating estimated dmg
2012-04-16 20:12:39 +03:00
//printing - Shoot %s (%d shots left, %s damage)
2022-11-20 19:11:34 +02:00
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 296 ] ) % shere - > getName ( ) % stacksController - > getActiveStack ( ) - > shots . available ( ) % estDmgText ) . str ( ) ;
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : AIMED_SPELL_CREATURE :
2022-11-20 19:11:34 +02:00
sp = CGI - > spellh - > objects [ creatureCasting ? stacksController - > activeStackSpellToCast ( ) : spellToCast - > actionSubtype ] ; //necessary if creature has random Genie spell at same time
2012-04-18 18:57:49 +03:00
consoleMsg = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 27 ] ) % sp - > name % shere - > getName ( ) ) ; //Cast %s on %s
switch ( sp - > id )
2012-04-18 12:01:08 +03:00
{
2013-02-11 02:24:57 +03:00
case SpellID : : SACRIFICE :
case SpellID : : TELEPORT :
2022-11-20 19:11:34 +02:00
stacksController - > setSelectedStack ( shere ) ; //remember first target
2012-04-18 18:57:49 +03:00
secondaryTarget = true ;
break ;
2012-04-18 12:01:08 +03:00
}
2012-04-18 18:57:49 +03:00
isCastingPossible = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : ANY_LOCATION :
2022-11-20 19:11:34 +02:00
sp = CGI - > spellh - > objects [ creatureCasting ? stacksController - > activeStackSpellToCast ( ) : spellToCast - > actionSubtype ] ; //necessary if creature has random Genie spell at same time
2012-05-18 23:50:16 +03:00
consoleMsg = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 26 ] ) % sp - > name ) ; //Cast %s
2012-05-07 19:04:43 +03:00
isCastingPossible = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : RANDOM_GENIE_SPELL : //we assume that teleport / sacrifice will never be available as random spell
2013-06-26 14:18:27 +03:00
sp = nullptr ;
2012-04-18 18:57:49 +03:00
consoleMsg = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 301 ] ) % shere - > getName ( ) ) ; //Cast a spell on %
2012-06-20 16:03:05 +03:00
creatureCasting = true ;
2012-04-18 18:57:49 +03:00
isCastingPossible = true ;
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : TELEPORT :
2012-04-16 20:12:39 +03:00
consoleMsg = CGI - > generaltexth - > allTexts [ 25 ] ; //Teleport Here
2013-07-25 14:53:36 +03:00
cursorFrame = ECursor : : COMBAT_TELEPORT ;
2012-04-16 20:12:39 +03:00
isCastingPossible = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : OBSTACLE :
2012-04-16 20:12:39 +03:00
consoleMsg = CGI - > generaltexth - > allTexts [ 550 ] ;
2013-07-25 14:53:36 +03:00
//TODO: remove obstacle cursor
2012-04-16 20:12:39 +03:00
isCastingPossible = true ;
2012-04-28 18:18:21 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : SACRIFICE :
2012-04-28 22:40:27 +03:00
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 549 ] ) % shere - > getName ( ) ) . str ( ) ; //sacrifice the %s
2014-03-07 16:21:09 +03:00
cursorFrame = ECursor : : COMBAT_SACRIFICE ;
2012-04-28 22:40:27 +03:00
isCastingPossible = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : FREE_LOCATION :
2012-05-18 23:50:16 +03:00
consoleMsg = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 26 ] ) % sp - > name ) ; //Cast %s
isCastingPossible = true ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : HEAL :
2012-04-16 20:12:39 +03:00
cursorFrame = ECursor : : COMBAT_HEAL ;
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 419 ] ) % shere - > getName ( ) ) . str ( ) ; //Apply first aid to the %s
2017-07-20 06:08:49 +02:00
realizeAction = [ = ] ( ) { giveCommand ( EActionType : : STACK_HEAL , myNumber ) ; } ; //command healing
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : RISE_DEMONS :
2012-04-16 20:12:39 +03:00
cursorType = ECursor : : SPELLBOOK ;
2017-07-19 01:06:05 +02:00
realizeAction = [ = ] ( )
2016-02-28 00:08:56 +02:00
{
2017-07-20 06:08:49 +02:00
giveCommand ( EActionType : : DAEMON_SUMMONING , myNumber ) ;
2015-11-07 10:42:06 +02:00
} ;
2012-04-16 20:12:39 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : CATAPULT :
2012-04-16 20:12:39 +03:00
cursorFrame = ECursor : : COMBAT_SHOOT_CATAPULT ;
2017-07-20 06:08:49 +02:00
realizeAction = [ = ] ( ) { giveCommand ( EActionType : : CATAPULT , myNumber ) ; } ;
2012-04-18 16:24:18 +03:00
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : CREATURE_INFO :
2012-04-17 11:46:09 +03:00
{
cursorFrame = ECursor : : COMBAT_QUERY ;
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 297 ] ) % shere - > getName ( ) ) . str ( ) ;
2018-07-25 00:36:48 +02:00
realizeAction = [ = ] ( ) { GH . pushIntT < CStackWindow > ( shere , false ) ; } ;
2012-04-16 20:12:39 +03:00
break ;
2013-07-06 19:10:20 +03:00
}
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
}
2012-04-17 11:46:09 +03:00
else //no possible valid action, display message
2012-04-16 20:12:39 +03:00
{
switch ( illegalAction )
2012-03-31 00:36:07 +03:00
{
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : AIMED_SPELL_CREATURE :
case PossiblePlayerBattleAction : : RANDOM_GENIE_SPELL :
2012-04-16 20:12:39 +03:00
cursorFrame = ECursor : : COMBAT_BLOCKED ;
consoleMsg = CGI - > generaltexth - > allTexts [ 23 ] ;
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : TELEPORT :
2013-07-25 14:53:36 +03:00
cursorFrame = ECursor : : COMBAT_BLOCKED ;
2012-04-16 20:12:39 +03:00
consoleMsg = CGI - > generaltexth - > allTexts [ 24 ] ; //Invalid Teleport Destination
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : SACRIFICE :
2012-04-28 22:40:27 +03:00
consoleMsg = CGI - > generaltexth - > allTexts [ 543 ] ; //choose army to sacrifice
break ;
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : FREE_LOCATION :
2012-05-18 23:50:16 +03:00
cursorFrame = ECursor : : COMBAT_BLOCKED ;
consoleMsg = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 181 ] ) % sp - > name ) ; //No room to place %s here
break ;
2012-04-16 20:12:39 +03:00
default :
2012-08-06 09:01:02 +03:00
if ( myNumber = = - 1 )
CCS - > curh - > changeGraphic ( ECursor : : COMBAT , ECursor : : COMBAT_POINTER ) ; //set neutral cursor over menu etc.
else
cursorFrame = ECursor : : COMBAT_BLOCKED ;
2012-04-16 20:12:39 +03:00
break ;
2012-03-31 00:36:07 +03:00
}
}
2012-04-16 20:12:39 +03:00
if ( isCastingPossible ) //common part
2012-03-31 00:36:07 +03:00
{
2013-07-25 14:53:36 +03:00
switch ( currentAction ) //don't use that with teleport / sacrifice
{
2019-05-04 05:42:55 +02:00
case PossiblePlayerBattleAction : : TELEPORT : //FIXME: more generic solution?
case PossiblePlayerBattleAction : : SACRIFICE :
2013-07-25 14:53:36 +03:00
break ;
default :
cursorType = ECursor : : SPELLBOOK ;
cursorFrame = 0 ;
2016-10-28 23:37:20 +02:00
if ( consoleMsg . empty ( ) & & sp )
2013-07-25 14:53:36 +03:00
consoleMsg = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 26 ] ) % sp - > name ) ; //Cast %s
break ;
}
2014-03-07 16:21:09 +03:00
2017-07-19 01:06:05 +02:00
realizeAction = [ = ] ( )
2012-03-31 00:36:07 +03:00
{
2017-07-20 06:08:49 +02:00
if ( secondaryTarget ) //select that target now
2012-03-31 00:36:07 +03:00
{
2017-07-20 06:08:49 +02:00
2012-04-18 16:24:18 +03:00
possibleActions . clear ( ) ;
2013-02-11 02:24:57 +03:00
switch ( sp - > id . toEnum ( ) )
2012-04-18 16:24:18 +03:00
{
2014-03-07 16:21:09 +03:00
case SpellID : : TELEPORT : //don't cast spell yet, only select target
2017-07-20 06:08:49 +02:00
spellToCast - > aimToUnit ( shere ) ;
2019-05-04 05:42:55 +02:00
possibleActions . push_back ( PossiblePlayerBattleAction : : TELEPORT ) ;
2012-04-18 16:24:18 +03:00
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : SACRIFICE :
2017-07-20 06:08:49 +02:00
spellToCast - > aimToHex ( myNumber ) ;
2019-05-04 05:42:55 +02:00
possibleActions . push_back ( PossiblePlayerBattleAction : : SACRIFICE ) ;
2012-04-18 16:24:18 +03:00
break ;
}
2012-04-18 12:01:08 +03:00
}
else
{
2016-10-28 23:37:20 +02:00
if ( creatureCasting )
2012-04-18 16:24:18 +03:00
{
2012-04-18 18:57:49 +03:00
if ( sp )
{
2022-11-20 19:11:34 +02:00
giveCommand ( EActionType : : MONSTER_SPELL , myNumber , stacksController - > activeStackSpellToCast ( ) ) ;
2012-04-18 18:57:49 +03:00
}
else //unknown random spell
{
2017-07-20 06:08:49 +02:00
giveCommand ( EActionType : : MONSTER_SPELL , myNumber ) ;
2012-04-18 18:57:49 +03:00
}
2012-04-18 16:24:18 +03:00
}
else
{
2017-07-20 06:08:49 +02:00
assert ( sp ) ;
2013-02-11 02:24:57 +03:00
switch ( sp - > id . toEnum ( ) )
2012-04-28 22:40:27 +03:00
{
2017-07-20 06:08:49 +02:00
case SpellID : : SACRIFICE :
spellToCast - > aimToUnit ( shere ) ; //victim
break ;
default :
spellToCast - > aimToHex ( myNumber ) ;
break ;
2012-04-28 22:40:27 +03:00
}
2018-07-25 00:36:48 +02:00
curInt - > cb - > battleMakeAction ( spellToCast . get ( ) ) ;
2012-04-18 16:24:18 +03:00
endCastingSpell ( ) ;
}
2022-11-20 19:11:34 +02:00
stacksController - > setSelectedStack ( nullptr ) ;
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
} ;
2012-03-31 00:36:07 +03:00
}
2017-07-20 06:08:49 +02:00
2013-07-25 14:53:36 +03:00
{
2016-10-28 23:37:20 +02:00
if ( eventType = = MOVE )
2013-07-25 14:53:36 +03:00
{
2016-10-28 23:37:20 +02:00
if ( setCursor )
2013-07-25 14:53:36 +03:00
CCS - > curh - > changeGraphic ( cursorType , cursorFrame ) ;
2022-11-18 17:54:10 +02:00
controlPanel - > console - > write ( consoleMsg ) ;
2013-07-25 14:53:36 +03:00
}
2016-10-28 23:37:20 +02:00
if ( eventType = = LCLICK & & realizeAction )
2013-07-25 14:53:36 +03:00
{
2014-03-07 16:21:09 +03:00
//opening creature window shouldn't affect myTurn...
2019-05-04 05:42:55 +02:00
if ( ( currentAction ! = PossiblePlayerBattleAction : : CREATURE_INFO ) & & ! secondaryTarget )
2013-07-25 14:53:36 +03:00
{
myTurn = false ; //tends to crash with empty calls
}
realizeAction ( ) ;
if ( ! secondaryTarget ) //do not replace teleport or sacrifice cursor
2014-03-07 16:21:09 +03:00
CCS - > curh - > changeGraphic ( ECursor : : COMBAT , ECursor : : COMBAT_POINTER ) ;
2022-11-18 17:54:10 +02:00
controlPanel - > console - > clear ( ) ;
2013-07-25 14:53:36 +03:00
}
2017-07-20 06:08:49 +02:00
}
2012-04-16 20:12:39 +03:00
}
2016-10-28 23:37:20 +02:00
bool CBattleInterface : : isCastingPossibleHere ( const CStack * sactive , const CStack * shere , BattleHex myNumber )
2012-04-16 20:12:39 +03:00
{
2022-11-20 19:11:34 +02:00
creatureCasting = stacksController - > activeStackSpellcaster ( ) & & ! spellDestSelectMode ; //TODO: allow creatures to cast aimed spells
2014-03-07 16:21:09 +03:00
2012-04-16 20:12:39 +03:00
bool isCastingPossible = true ;
int spellID = - 1 ;
if ( creatureCasting )
{
2022-11-20 19:11:34 +02:00
if ( stacksController - > activeStackSpellToCast ( ) ! = SpellID : : NONE & & ( shere ! = sactive ) ) //can't cast on itself
spellID = stacksController - > activeStackSpellToCast ( ) ; //TODO: merge with SpellTocast?
2012-04-16 20:12:39 +03:00
}
2012-04-18 18:57:49 +03:00
else //hero casting
2017-07-20 06:08:49 +02:00
{
spellID = spellToCast - > actionSubtype ;
}
2012-04-16 20:12:39 +03:00
2013-06-26 14:18:27 +03:00
sp = nullptr ;
2014-03-07 16:21:09 +03:00
if ( spellID > = 0 )
sp = CGI - > spellh - > objects [ spellID ] ;
2012-04-16 20:12:39 +03:00
2012-04-22 16:28:46 +03:00
if ( sp )
2012-04-16 20:12:39 +03:00
{
2017-07-20 06:08:49 +02:00
const spells : : Caster * caster = creatureCasting ? static_cast < const spells : : Caster * > ( sactive ) : static_cast < const spells : : Caster * > ( curInt - > cb - > battleGetMyHero ( ) ) ;
2016-10-28 23:37:20 +02:00
if ( caster = = nullptr )
2015-09-28 15:06:26 +02:00
{
isCastingPossible = false ; //just in case
}
2012-04-16 20:12:39 +03:00
else
2015-09-28 15:06:26 +02:00
{
2017-07-20 06:08:49 +02:00
const spells : : Mode mode = creatureCasting ? spells : : Mode : : CREATURE_ACTIVE : spells : : Mode : : HERO ;
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
spells : : Target target ;
target . emplace_back ( myNumber ) ;
spells : : BattleCast cast ( curInt - > cb . get ( ) , caster , mode , sp ) ;
auto m = sp - > battleMechanics ( & cast ) ;
spells : : detail : : ProblemImpl problem ; //todo: display problem in status bar
2021-02-20 03:57:50 +02:00
isCastingPossible = m - > canBeCastAt ( target , problem ) ;
2015-09-28 15:06:26 +02:00
}
2012-04-16 20:12:39 +03:00
}
2015-09-16 16:35:49 +02:00
else
isCastingPossible = false ;
2016-10-28 23:37:20 +02:00
if ( ! myNumber . isAvailable ( ) & & ! shere ) //empty tile outside battlefield (or in the unavailable border column)
2012-04-16 20:12:39 +03:00
isCastingPossible = false ;
return isCastingPossible ;
2012-03-31 00:36:07 +03:00
}
2012-05-05 00:16:39 +03:00
void CBattleInterface : : obstaclePlaced ( const CObstacleInstance & oi )
{
2022-11-17 19:36:25 +02:00
obstacleController - > obstaclePlaced ( oi ) ;
2012-05-05 00:16:39 +03:00
}
2016-10-28 23:37:20 +02:00
const CGHeroInstance * CBattleInterface : : currentHero ( ) const
2012-08-26 12:07:48 +03:00
{
2016-10-28 23:37:20 +02:00
if ( attackingHeroInstance - > tempOwner = = curInt - > playerID )
2012-08-26 12:07:48 +03:00
return attackingHeroInstance ;
else
return defendingHeroInstance ;
}
InfoAboutHero CBattleInterface : : enemyHero ( ) const
{
InfoAboutHero ret ;
2016-10-28 23:37:20 +02:00
if ( attackingHeroInstance - > tempOwner = = curInt - > playerID )
2012-08-26 12:07:48 +03:00
curInt - > cb - > getHeroInfo ( defendingHeroInstance , ret ) ;
else
curInt - > cb - > getHeroInfo ( attackingHeroInstance , ret ) ;
return ret ;
}
2013-06-23 00:47:51 +03:00
void CBattleInterface : : requestAutofightingAIToTakeAction ( )
{
2013-06-23 14:25:48 +03:00
assert ( curInt - > isAutoFightOn ) ;
2013-06-23 00:47:51 +03:00
2017-07-19 01:06:05 +02:00
boost : : thread aiThread ( [ & ] ( )
2013-06-23 00:47:51 +03:00
{
2022-11-20 19:11:34 +02:00
auto ba = make_unique < BattleAction > ( curInt - > autofightingAI - > activeStack ( stacksController - > getActiveStack ( ) ) ) ;
2013-06-23 00:47:51 +03:00
2022-04-04 09:16:32 +02:00
if ( curInt - > cb - > battleIsFinished ( ) )
{
return ; // battle finished with spellcast
}
2016-10-28 23:37:20 +02:00
if ( curInt - > isAutoFightOn )
2013-06-23 00:47:51 +03:00
{
2016-10-28 23:37:20 +02:00
if ( tacticsMode )
2013-11-09 19:25:20 +03:00
{
// Always end tactics mode. Player interface is blocked currently, so it's not possible that
// the AI can take any action except end tactics phase (AI actions won't be triggered)
//TODO implement the possibility that the AI will be triggered for further actions
//TODO any solution to merge tactics phase & normal phase in the way it is handled by the player and battle interface?
2022-11-20 19:11:34 +02:00
stacksController - > setActiveStack ( nullptr ) ;
2022-11-18 17:54:10 +02:00
controlPanel - > blockUI ( true ) ;
2013-11-09 19:25:20 +03:00
tacticsMode = false ;
}
else
{
2017-06-14 03:53:26 +02:00
givenCommand . setn ( ba . release ( ) ) ;
2013-11-09 19:25:20 +03:00
}
2013-06-23 00:47:51 +03:00
}
2013-06-23 14:25:48 +03:00
else
{
2016-11-25 21:12:22 +02:00
boost : : unique_lock < boost : : recursive_mutex > un ( * CPlayerInterface : : pim ) ;
2013-06-23 14:25:48 +03:00
activateStack ( ) ;
}
2013-06-23 00:47:51 +03:00
} ) ;
2014-03-07 16:21:09 +03:00
2013-06-24 17:35:27 +03:00
aiThread . detach ( ) ;
2013-06-23 00:47:51 +03:00
}
2016-10-28 23:37:20 +02:00
void CBattleInterface : : showAll ( SDL_Surface * to )
2013-07-19 19:35:16 +03:00
{
show ( to ) ;
}
2016-10-28 23:37:20 +02:00
void CBattleInterface : : show ( SDL_Surface * to )
2013-07-19 19:35:16 +03:00
{
assert ( to ) ;
SDL_Rect buf ;
SDL_GetClipRect ( to , & buf ) ;
SDL_SetClipRect ( to , & pos ) ;
+ + animCount ;
2022-11-20 19:11:34 +02:00
if ( stacksController - > getActiveStack ( ) ! = nullptr /*&& creAnims[stacksController->getActiveStack()->ID]->isIdle()*/ ) //show everything with range
2022-11-17 23:57:51 +02:00
{
2022-11-20 19:11:34 +02:00
// FIXME: any *real* reason to keep this separate? Speed difference can't be that big // TODO: move to showAll?
2022-11-17 23:57:51 +02:00
fieldController - > showBackgroundImageWithHexes ( to ) ;
}
else
{
fieldController - > showBackgroundImage ( to ) ;
obstacleController - > showAbsoluteObstacles ( to ) ;
if ( siegeController )
siegeController - > showAbsoluteObstacles ( to ) ;
}
fieldController - > showHighlightedHexes ( to ) ;
2013-07-19 19:35:16 +03:00
showBattlefieldObjects ( to ) ;
2022-11-17 13:21:03 +02:00
projectilesController - > showProjectiles ( to ) ;
2013-07-19 19:35:16 +03:00
2019-03-22 22:39:53 +02:00
if ( battleActionsStarted )
2022-11-20 19:11:34 +02:00
stacksController - > updateBattleAnimations ( ) ;
2013-07-19 19:35:16 +03:00
SDL_SetClipRect ( to , & buf ) ; //restoring previous clip_rect
showInterface ( to ) ;
2022-11-20 19:11:34 +02:00
//activation of next stack, if any
//TODO: should be moved to the very start of this method?
activateStack ( ) ;
2013-07-19 19:35:16 +03:00
}
2016-10-28 23:37:20 +02:00
void CBattleInterface : : showBattlefieldObjects ( SDL_Surface * to )
2013-07-19 19:35:16 +03:00
{
auto showHexEntry = [ & ] ( BattleObjectsByHex : : HexData & hex )
{
2022-11-17 18:50:12 +02:00
if ( siegeController )
siegeController - > showPiecesOfWall ( to , hex . walls ) ;
2022-11-17 19:36:25 +02:00
obstacleController - > showObstacles ( to , hex . obstacles ) ;
2022-11-20 19:11:34 +02:00
stacksController - > showAliveStacks ( to , hex . alive ) ;
2013-07-19 19:35:16 +03:00
showBattleEffects ( to , hex . effects ) ;
} ;
BattleObjectsByHex objects = sortObjectsByHex ( ) ;
2013-07-21 13:10:38 +03:00
// dead stacks should be blit first
2022-11-20 19:11:34 +02:00
stacksController - > showStacks ( to , objects . beforeAll . dead ) ;
2016-10-30 05:47:33 +02:00
for ( auto & data : objects . hex )
2022-11-20 19:11:34 +02:00
stacksController - > showStacks ( to , data . dead ) ;
stacksController - > showStacks ( to , objects . afterAll . dead ) ;
2013-07-21 13:10:38 +03:00
2013-07-19 19:35:16 +03:00
// display objects that must be blit before anything else (e.g. topmost walls)
showHexEntry ( objects . beforeAll ) ;
2013-09-30 11:45:26 +03:00
// show heroes after "beforeAll" - e.g. topmost wall in siege
2016-10-28 23:37:20 +02:00
if ( attackingHero )
2013-09-30 11:45:26 +03:00
attackingHero - > show ( to ) ;
2016-10-28 23:37:20 +02:00
if ( defendingHero )
2013-09-30 11:45:26 +03:00
defendingHero - > show ( to ) ;
2013-07-19 19:35:16 +03:00
// actual blit of most of objects, hex by hex
// NOTE: row-by-row blitting may be a better approach
2016-10-30 05:47:33 +02:00
for ( auto & data : objects . hex )
2013-07-19 19:35:16 +03:00
showHexEntry ( data ) ;
2016-10-30 05:47:33 +02:00
2013-07-19 19:35:16 +03:00
// objects that must be blit *after* everything else - e.g. bottom tower or some spell effects
showHexEntry ( objects . afterAll ) ;
}
void CBattleInterface : : showBattleEffects ( SDL_Surface * to , const std : : vector < const BattleEffect * > & battleEffects )
{
2016-10-28 23:37:20 +02:00
for ( auto & elem : battleEffects )
2013-07-19 19:35:16 +03:00
{
2020-10-01 10:38:06 +02:00
int currentFrame = static_cast < int > ( floor ( elem - > currentFrame ) ) ;
2017-09-05 16:21:44 +02:00
currentFrame % = elem - > animation - > size ( ) ;
2013-07-19 19:35:16 +03:00
2018-03-30 13:02:04 +02:00
auto img = elem - > animation - > getImage ( currentFrame ) ;
2017-09-05 16:21:44 +02:00
SDL_Rect temp_rect = genRect ( img - > height ( ) , img - > width ( ) , elem - > x , elem - > y ) ;
img - > draw ( to , & temp_rect , nullptr ) ;
2013-07-19 19:35:16 +03:00
}
}
2016-10-28 23:37:20 +02:00
void CBattleInterface : : showInterface ( SDL_Surface * to )
2013-07-19 19:35:16 +03:00
{
//showing in-game console
LOCPLINT - > cingconsole - > show ( to ) ;
2022-11-18 17:54:10 +02:00
controlPanel - > show ( to ) ;
2013-07-19 19:35:16 +03:00
Rect posWithQueue = Rect ( pos . x , pos . y , 800 , 600 ) ;
2016-10-28 23:37:20 +02:00
if ( settings [ " battle " ] [ " showQueue " ] . Bool ( ) )
2013-07-19 19:35:16 +03:00
{
2016-10-28 23:37:20 +02:00
if ( ! queue - > embedded )
2013-07-19 19:35:16 +03:00
{
posWithQueue . y - = queue - > pos . h ;
posWithQueue . h + = queue - > pos . h ;
}
2017-07-20 06:08:49 +02:00
queue - > showAll ( to ) ;
2013-07-19 19:35:16 +03:00
}
//printing border around interface
2016-10-28 23:37:20 +02:00
if ( screen - > w ! = 800 | | screen - > h ! = 600 )
2013-07-19 19:35:16 +03:00
{
CMessage : : drawBorder ( curInt - > playerID , to , posWithQueue . w + 28 , posWithQueue . h + 28 , posWithQueue . x - 14 , posWithQueue . y - 15 ) ;
}
}
BattleObjectsByHex CBattleInterface : : sortObjectsByHex ( )
{
BattleObjectsByHex sorted ;
// Sort creatures
2022-11-20 19:11:34 +02:00
stacksController - > sortObjectsByHex ( sorted ) ;
2013-07-19 19:35:16 +03:00
// Sort battle effects (spells)
for ( auto & battleEffect : battleEffects )
{
2016-10-28 23:37:20 +02:00
if ( battleEffect . position . isValid ( ) )
2013-07-19 19:35:16 +03:00
sorted . hex [ battleEffect . position ] . effects . push_back ( & battleEffect ) ;
else
sorted . afterAll . effects . push_back ( & battleEffect ) ;
}
// Sort obstacles
2022-11-17 19:36:25 +02:00
obstacleController - > sortObjectsByHex ( sorted ) ;
2013-07-19 19:35:16 +03:00
// Sort wall parts
2022-11-17 18:50:12 +02:00
if ( siegeController )
siegeController - > sortObjectsByHex ( sorted ) ;
2013-10-27 16:05:01 +03:00
2013-07-19 19:35:16 +03:00
return sorted ;
}