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"
2013-07-06 19:10:20 +03:00
# include "../CBitmapHandler.h"
2014-07-13 20:53:37 +03:00
# include "../CDefHandler.h"
# 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"
# 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"
# include "../../lib/BattleState.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"
2011-12-14 00:35:28 +03:00
# include "../../lib/CTownHandler.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
2012-04-04 11:03:52 +03:00
2009-04-15 17:03:31 +03: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
*
*/
2009-09-16 13:36:50 +03:00
CondSh < bool > CBattleInterface : : animsAreDisplayed ;
2009-03-28 20:46:20 +02:00
2013-07-06 19:10:20 +03:00
static void onAnimationFinished ( const CStack * stack , CCreatureAnimation * anim )
2009-01-15 19:01:08 +02:00
{
2013-07-06 19:10:20 +03:00
if ( anim - > isIdle ( ) )
2009-01-15 19:01:08 +02:00
{
2013-07-06 19:10:20 +03:00
const CCreature * creature = stack - > getCreature ( ) ;
if ( anim - > framesInGroup ( CCreatureAnim : : MOUSEON ) > 0 )
{
2014-04-10 20:11:09 +03:00
if ( CRandomGenerator : : getDefault ( ) . nextDouble ( 99.0 ) < creature - > animation . timeBetweenFidgets * 10 )
2013-07-06 19:10:20 +03:00
anim - > playOnce ( CCreatureAnim : : MOUSEON ) ;
else
anim - > setType ( CCreatureAnim : : HOLDING ) ;
}
else
{
anim - > setType ( CCreatureAnim : : HOLDING ) ;
}
2009-01-15 19:01:08 +02:00
}
2013-07-06 19:10:20 +03:00
// always reset callback
anim - > onAnimationReset + = std : : bind ( & onAnimationFinished , stack , anim ) ;
}
2009-01-15 19:01:08 +02:00
2011-12-14 00:35:28 +03:00
static void transformPalette ( SDL_Surface * surf , double rCor , double gCor , double bCor )
2009-08-01 14:55:40 +03:00
{
SDL_Color * colorsToChange = surf - > format - > palette - > colors ;
for ( int g = 0 ; g < surf - > format - > palette - > ncolors ; + + g )
{
if ( ( colorsToChange + g ) - > b ! = 132 & &
( colorsToChange + g ) - > g ! = 231 & &
( colorsToChange + g ) - > r ! = 255 ) //it's not yellow border
{
2011-12-14 00:35:28 +03:00
( colorsToChange + g ) - > r = static_cast < double > ( ( colorsToChange + g ) - > r ) * rCor ;
( colorsToChange + g ) - > g = static_cast < double > ( ( colorsToChange + g ) - > g ) * gCor ;
( colorsToChange + g ) - > b = static_cast < double > ( ( colorsToChange + g ) - > b ) * bCor ;
2009-08-01 14:55:40 +03:00
}
}
}
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : addNewAnim ( CBattleAnimation * anim )
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
pendingAnims . push_back ( std : : make_pair ( anim , false ) ) ;
animsAreDisplayed . setn ( true ) ;
2009-09-10 14:28:34 +03:00
}
2014-03-07 16:21:09 +03:00
CBattleInterface : : CBattleInterface ( const CCreatureSet * army1 , const CCreatureSet * army2 ,
const CGHeroInstance * hero1 , const CGHeroInstance * hero2 ,
const SDL_Rect & myRect ,
2015-12-29 04:43:33 +02:00
std : : shared_ptr < CPlayerInterface > att , std : : shared_ptr < CPlayerInterface > defen )
2013-06-26 14:18:27 +03:00
: background ( nullptr ) , queue ( nullptr ) , attackingHeroInstance ( hero1 ) , defendingHeroInstance ( hero2 ) , animCount ( 0 ) ,
2013-07-06 19:10:20 +03:00
activeStack ( nullptr ) , mouseHoveredStack ( nullptr ) , stackToActivate ( nullptr ) , selectedStack ( nullptr ) , previouslyHoveredHex ( - 1 ) ,
2013-06-26 14:18:27 +03:00
currentlyHoveredHex ( - 1 ) , attackingHex ( - 1 ) , stackCanCastSpell ( false ) , creatureCasting ( false ) , spellDestSelectMode ( false ) , spellSelMode ( NO_LOCATION ) , spellToCast ( nullptr ) , sp ( nullptr ) ,
siegeH ( nullptr ) , attackerInt ( att ) , defenderInt ( defen ) , curInt ( att ) , animIDhelper ( 0 ) ,
2013-07-06 19:10:20 +03:00
givenCommand ( nullptr ) , myTurn ( false ) , resWindow ( nullptr ) , moveStarted ( false ) , moveSoundHander ( - 1 ) , bresult ( nullptr )
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
2013-06-23 00:47:51 +03:00
if ( ! curInt )
{
//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 ;
2013-06-26 14:18:27 +03:00
givenCommand = new CondSh < BattleAction * > ( 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)
if ( attackerInt & & attackerInt - > cb - > battleGetTacticDist ( ) )
2011-12-14 00:35:28 +03:00
tacticianInterface = attackerInt ;
2012-02-16 20:10:58 +03: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
bool embedQueue = screen - > h < 700 ;
queue = new CStackQueue ( embedQueue , this ) ;
if ( ! embedQueue )
{
2012-01-12 18:23:00 +03: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->pos.x = pos.x;
// queue->pos.y = pos.y - queue->pos.h;
// pos.h += queue->pos.h;
// center();
}
queue - > update ( ) ;
2010-01-28 19:23:01 +02:00
2011-12-14 00:35:28 +03:00
//preparing siege info
const CGTownInstance * town = curInt - > cb - > battleGetDefendedTown ( ) ;
if ( town & & town - > hasFort ( ) )
{
siegeH = new SiegeHelper ( town , this ) ;
}
2010-01-28 19:23:01 +02:00
2011-12-14 00:35:28 +03:00
curInt - > 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 ;
2013-09-27 22:42:17 +03:00
std : : vector < const CStack * > stacks = curInt - > cb - > battleGetAllStacks ( true ) ;
2013-06-29 16:05:48 +03:00
for ( const CStack * s : stacks )
2011-12-14 00:35:28 +03:00
{
newStack ( s ) ;
}
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
//preparing menu background and terrain
if ( siegeH )
2009-09-17 15:59:04 +03:00
{
2011-12-14 00:35:28 +03:00
background = BitmapHandler : : loadBitmap ( siegeH - > getSiegeName ( 0 ) , false ) ;
ui8 siegeLevel = curInt - > cb - > battleGetSiegeLevel ( ) ;
if ( siegeLevel > = 2 ) //citadel or castle
2009-09-17 15:59:04 +03:00
{
2011-12-14 00:35:28 +03:00
//print moat/mlip
SDL_Surface * moat = BitmapHandler : : loadBitmap ( siegeH - > getSiegeName ( 13 ) ) ,
* mlip = BitmapHandler : : loadBitmap ( siegeH - > getSiegeName ( 14 ) ) ;
2009-09-17 15:59:04 +03:00
2012-10-05 21:03:49 +03:00
auto & info = siegeH - > town - > town - > clientInfo ;
Point moatPos ( info . siegePositions [ 13 ] . x , info . siegePositions [ 13 ] . y ) ;
Point mlipPos ( info . siegePositions [ 14 ] . x , info . siegePositions [ 14 ] . y ) ;
2010-07-15 20:13:17 +03:00
2011-12-14 00:35:28 +03:00
if ( moat ) //eg. tower has no moat
blitAt ( moat , moatPos . x , moatPos . y , background ) ;
if ( mlip ) //eg. tower has no mlip
blitAt ( mlip , mlipPos . x , mlipPos . y , background ) ;
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
SDL_FreeSurface ( moat ) ;
SDL_FreeSurface ( mlip ) ;
2009-09-23 16:22:40 +03:00
}
2009-09-17 15:59:04 +03:00
}
2011-12-14 00:35:28 +03:00
else
2009-09-17 15:59:04 +03:00
{
2013-06-22 17:47:20 +03:00
auto bfieldType = ( int ) curInt - > cb - > battleGetBattlefieldType ( ) ;
if ( graphics - > battleBacks . size ( ) < = bfieldType | | bfieldType < 0 )
logGlobal - > errorStream ( ) < < bfieldType < < " is not valid battlefield type index! " ;
else if ( graphics - > battleBacks [ bfieldType ] . empty ( ) )
logGlobal - > errorStream ( ) < < bfieldType < < " battlefield type does not have any backgrounds! " ;
else
{
2014-04-10 20:11:09 +03:00
const std : : string bgName = * RandomGeneratorUtil : : nextItem ( graphics - > battleBacks [ bfieldType ] , CRandomGenerator : : getDefault ( ) ) ;
2013-06-22 17:47:20 +03:00
background = BitmapHandler : : loadBitmap ( bgName , false ) ;
}
2009-09-17 15:59:04 +03:00
}
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
//preparing menu background
//graphics->blueToPlayersAdv(menu, hero1->tempOwner);
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
//preparing graphics for displaying amounts of creatures
amountNormal = BitmapHandler : : loadBitmap ( " CMNUMWIN.BMP " ) ;
CSDL_Ext : : alphaTransform ( amountNormal ) ;
transformPalette ( amountNormal , 0.59 , 0.19 , 0.93 ) ;
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
amountPositive = BitmapHandler : : loadBitmap ( " CMNUMWIN.BMP " ) ;
CSDL_Ext : : alphaTransform ( amountPositive ) ;
transformPalette ( amountPositive , 0.18 , 1.00 , 0.18 ) ;
2009-09-23 16:22:40 +03:00
2011-12-14 00:35:28 +03:00
amountNegative = BitmapHandler : : loadBitmap ( " CMNUMWIN.BMP " ) ;
CSDL_Ext : : alphaTransform ( amountNegative ) ;
transformPalette ( amountNegative , 1.00 , 0.18 , 0.18 ) ;
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
amountEffNeutral = BitmapHandler : : loadBitmap ( " CMNUMWIN.BMP " ) ;
CSDL_Ext : : alphaTransform ( amountEffNeutral ) ;
transformPalette ( amountEffNeutral , 1.00 , 1.00 , 0.18 ) ;
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
////blitting menu background and terrain
// blitAt(background, pos.x, pos.y);
// blitAt(menu, pos.x, 556 + pos.y);
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
//preparing buttons and console
2014-08-09 15:14:31 +03:00
bOptions = new CButton ( Point ( 3 , 561 ) , " icm003.def " , CGI - > generaltexth - > zelp [ 381 ] , std : : bind ( & CBattleInterface : : bOptionsf , this ) , SDLK_o ) ;
bSurrender = new CButton ( Point ( 54 , 561 ) , " icm001.def " , CGI - > generaltexth - > zelp [ 379 ] , std : : bind ( & CBattleInterface : : bSurrenderf , this ) , SDLK_s ) ;
bFlee = new CButton ( Point ( 105 , 561 ) , " icm002.def " , CGI - > generaltexth - > zelp [ 380 ] , std : : bind ( & CBattleInterface : : bFleef , this ) , SDLK_r ) ;
bAutofight = new CButton ( Point ( 157 , 561 ) , " icm004.def " , CGI - > generaltexth - > zelp [ 382 ] , std : : bind ( & CBattleInterface : : bAutofightf , this ) , SDLK_a ) ;
bSpell = new CButton ( Point ( 645 , 561 ) , " icm005.def " , CGI - > generaltexth - > zelp [ 385 ] , std : : bind ( & CBattleInterface : : bSpellf , this ) , SDLK_c ) ;
bWait = new CButton ( Point ( 696 , 561 ) , " icm006.def " , CGI - > generaltexth - > zelp [ 386 ] , std : : bind ( & CBattleInterface : : bWaitf , this ) , SDLK_w ) ;
bDefence = new CButton ( Point ( 747 , 561 ) , " icm007.def " , CGI - > generaltexth - > zelp [ 387 ] , std : : bind ( & CBattleInterface : : bDefencef , this ) , SDLK_d ) ;
2011-12-14 00:35:28 +03:00
bDefence - > assignedKeys . insert ( SDLK_SPACE ) ;
2014-08-09 15:14:31 +03:00
bConsoleUp = new CButton ( Point ( 624 , 561 ) , " ComSlide.def " , std : : make_pair ( " " , " " ) , std : : bind ( & CBattleInterface : : bConsoleUpf , this ) , SDLK_UP ) ;
bConsoleDown = new CButton ( Point ( 624 , 580 ) , " ComSlide.def " , std : : make_pair ( " " , " " ) , std : : bind ( & CBattleInterface : : bConsoleDownf , this ) , SDLK_DOWN ) ;
2014-08-03 14:16:19 +03:00
bConsoleDown - > setImageOrder ( 2 , 3 , 4 , 5 ) ;
2011-12-14 00:35:28 +03:00
console = new CBattleConsole ( ) ;
2012-06-02 18:16:54 +03:00
console - > pos . x + = 211 ;
console - > pos . y + = 560 ;
2011-12-14 00:35:28 +03:00
console - > pos . w = 406 ;
console - > pos . h = 38 ;
if ( tacticsMode )
2009-09-17 15:59:04 +03:00
{
2014-08-03 14:16:19 +03:00
btactNext = new CButton ( Point ( 213 , 560 ) , " icm011.def " , std : : make_pair ( " " , " " ) , [ & ] { bTacticNextStack ( nullptr ) ; } , SDLK_SPACE ) ;
btactEnd = new CButton ( Point ( 419 , 560 ) , " icm012.def " , std : : make_pair ( " " , " " ) , [ & ] { bEndTacticPhase ( ) ; } , SDLK_RETURN ) ;
2011-12-14 00:35:28 +03:00
menu = BitmapHandler : : loadBitmap ( " COPLACBR.BMP " ) ;
2009-09-17 15:59:04 +03:00
}
2011-12-14 00:35:28 +03:00
else
2009-09-17 15:59:04 +03:00
{
2011-12-14 00:35:28 +03:00
menu = BitmapHandler : : loadBitmap ( " CBAR.BMP " ) ;
2013-06-26 14:18:27 +03:00
btactEnd = btactNext = nullptr ;
2009-09-17 15:59:04 +03:00
}
2011-12-14 00:35:28 +03:00
graphics - > blueToPlayersAdv ( menu , curInt - > playerID ) ;
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
//loading hero animations
if ( hero1 ) // attacking hero
2009-09-16 13:09:08 +03:00
{
2012-12-16 16:47:53 +03:00
std : : string battleImage ;
if ( hero1 - > sex )
battleImage = hero1 - > type - > heroClass - > imageBattleFemale ;
else
battleImage = hero1 - > type - > heroClass - > imageBattleMale ;
2013-06-26 14:18:27 +03:00
attackingHero = new CBattleHero ( battleImage , false , hero1 - > tempOwner , hero1 - > tempOwner = = curInt - > playerID ? hero1 : nullptr , this ) ;
2011-12-14 00:35:28 +03:00
attackingHero - > pos = genRect ( attackingHero - > dh - > ourImages [ 0 ] . bitmap - > h , attackingHero - > dh - > ourImages [ 0 ] . bitmap - > w , pos . x - 43 , pos . y - 19 ) ;
2009-09-16 13:09:08 +03:00
}
2011-12-14 00:35:28 +03:00
else
2009-09-16 13:09:08 +03:00
{
2013-06-26 14:18:27 +03:00
attackingHero = nullptr ;
2009-09-16 13:09:08 +03:00
}
2011-12-14 00:35:28 +03:00
if ( hero2 ) // defending hero
2009-09-16 13:09:08 +03:00
{
2012-12-16 16:47:53 +03:00
std : : string battleImage ;
if ( hero2 - > sex )
2013-02-09 15:34:49 +03:00
battleImage = hero2 - > type - > heroClass - > imageBattleFemale ;
2012-12-16 16:47:53 +03:00
else
2013-02-09 15:34:49 +03:00
battleImage = hero2 - > type - > heroClass - > imageBattleMale ;
2012-12-16 16:47:53 +03:00
2013-06-26 14:18:27 +03:00
defendingHero = new CBattleHero ( battleImage , true , hero2 - > tempOwner , hero2 - > tempOwner = = curInt - > playerID ? hero2 : nullptr , this ) ;
2011-12-14 00:35:28 +03:00
defendingHero - > pos = genRect ( defendingHero - > dh - > ourImages [ 0 ] . bitmap - > h , defendingHero - > dh - > ourImages [ 0 ] . bitmap - > w , pos . x + 693 , pos . y - 19 ) ;
2009-09-16 13:09:08 +03:00
}
else
{
2013-06-26 14:18:27 +03:00
defendingHero = nullptr ;
2009-09-16 13:09:08 +03:00
}
2011-09-01 06:12:54 +03:00
2011-12-14 00:35:28 +03:00
//preparing cells and hexes
cellBorder = BitmapHandler : : loadBitmap ( " CCELLGRD.BMP " ) ;
CSDL_Ext : : alphaTransform ( cellBorder ) ;
cellShade = BitmapHandler : : loadBitmap ( " CCELLSHD.BMP " ) ;
CSDL_Ext : : alphaTransform ( cellShade ) ;
2012-06-09 19:45:45 +03:00
for ( int h = 0 ; h < GameConstants : : BFIELD_SIZE ; + + h )
2009-09-10 14:28:34 +03:00
{
2013-06-29 16:05:48 +03:00
auto hex = new CClickableHex ( ) ;
2012-06-09 19:45:45 +03:00
hex - > myNumber = h ;
hex - > pos = hexPosition ( h ) ;
hex - > accessible = true ;
hex - > myInterface = this ;
bfield . push_back ( hex ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
//locking occupied positions on batlefield
2013-06-29 16:05:48 +03:00
for ( const CStack * s : stacks ) //stacks gained at top of this function
2011-12-14 00:35:28 +03:00
if ( s - > position > = 0 ) //turrets have position < 0
2012-06-09 19:45:45 +03:00
bfield [ s - > position ] - > accessible = false ;
2009-09-14 15:00:23 +03:00
2011-12-14 00:35:28 +03:00
//loading projectiles for units
2013-06-29 16:05:48 +03:00
for ( const CStack * s : stacks )
2009-09-10 14:28:34 +03:00
{
2012-09-21 22:49:35 +03:00
if ( s - > getCreature ( ) - > isShooting ( ) )
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
CDefHandler * & projectile = idToProjectile [ s - > getCreature ( ) - > idNumber ] ;
2012-10-05 21:03:49 +03:00
const CCreature * creature ; //creature whose shots should be loaded
2013-02-09 00:17:39 +03:00
if ( s - > getCreature ( ) - > idNumber = = CreatureID : : ARROW_TOWERS )
2012-10-05 21:03:49 +03:00
creature = CGI - > creh - > creatures [ siegeH - > town - > town - > clientInfo . siegeShooter ] ;
else
creature = s - > getCreature ( ) ;
2013-03-02 19:55:51 +03:00
projectile = CDefHandler : : giveDef ( creature - > animation . projectileImageName ) ;
2011-12-14 00:35:28 +03:00
2013-06-29 16:05:48 +03:00
for ( auto & elem : projectile - > ourImages ) //alpha transforming
2011-12-14 00:35:28 +03:00
{
2013-06-29 16:05:48 +03:00
CSDL_Ext : : alphaTransform ( elem . bitmap ) ;
2011-12-14 00:35:28 +03:00
}
2009-09-10 14:28:34 +03:00
}
}
2011-09-01 06:12:54 +03:00
2011-12-14 00:35:28 +03:00
//preparing graphic with cell borders
cellBorders = CSDL_Ext : : newSurface ( background - > w , background - > h , cellBorder ) ;
//copying palette
for ( int g = 0 ; g < cellBorder - > format - > palette - > ncolors ; + + g ) //we assume that cellBorders->format->palette->ncolors == 256
2011-09-01 06:12:54 +03:00
{
2011-12-14 00:35:28 +03:00
cellBorders - > format - > palette - > colors [ g ] = cellBorder - > format - > palette - > colors [ g ] ;
2011-09-01 06:12:54 +03:00
}
2011-12-14 00:35:28 +03:00
//palette copied
for ( int i = 0 ; i < GameConstants : : BFIELD_HEIGHT ; + + i ) //rows
2011-09-01 06:12:54 +03:00
{
2011-12-14 00:35:28 +03:00
for ( int j = 0 ; j < GameConstants : : BFIELD_WIDTH - 2 ; + + j ) //columns
2011-09-01 06:12:54 +03:00
{
2011-12-14 00:35:28 +03:00
int x = 58 + ( i % 2 = = 0 ? 22 : 0 ) + 44 * j ;
int y = 86 + 42 * i ;
for ( int cellX = 0 ; cellX < cellBorder - > w ; + + cellX )
{
for ( int cellY = 0 ; cellY < cellBorder - > h ; + + cellY )
{
if ( y + cellY < cellBorders - > h & & x + cellX < cellBorders - > w )
* ( ( Uint8 * ) cellBorders - > pixels + ( y + cellY ) * cellBorders - > pitch + ( x + cellX ) ) | = * ( ( Uint8 * ) cellBorder - > pixels + cellY * cellBorder - > pitch + cellX ) ;
}
}
2011-09-01 06:12:54 +03:00
}
}
2011-12-14 00:35:28 +03:00
backgroundWithHexes = CSDL_Ext : : newSurface ( background - > w , background - > h , screen ) ;
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
//preparing obstacle defs
2012-05-18 23:50:16 +03:00
auto obst = curInt - > cb - > battleGetAllObstacles ( ) ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : obst )
2009-09-10 14:28:34 +03:00
{
2013-06-29 16:05:48 +03:00
const int ID = elem - > ID ;
if ( elem - > obstacleType = = CObstacleInstance : : USUAL )
2009-09-17 15:59:04 +03:00
{
2013-06-29 16:05:48 +03:00
idToObstacle [ ID ] = CDefHandler : : giveDef ( elem - > getInfo ( ) . defName ) ;
for ( auto & _n : idToObstacle [ ID ] - > ourImages )
2015-03-22 11:18:58 +02:00
{
2014-07-02 21:20:54 +03:00
CSDL_Ext : : setDefaultColorKey ( _n . bitmap ) ;
2012-04-23 22:56:37 +03:00
}
}
2013-06-29 16:05:48 +03:00
else if ( elem - > obstacleType = = CObstacleInstance : : ABSOLUTE_OBSTACLE )
2012-04-23 22:56:37 +03:00
{
2013-06-29 16:05:48 +03:00
idToAbsoluteObstacle [ ID ] = BitmapHandler : : loadBitmap ( elem - > getInfo ( ) . defName ) ;
2009-09-17 15:59:04 +03:00
}
2009-09-10 14:28:34 +03:00
}
2012-05-05 00:16:39 +03:00
quicksand = CDefHandler : : giveDef ( " C17SPE1.DEF " ) ;
landMine = CDefHandler : : giveDef ( " C09SPF1.DEF " ) ;
2012-05-18 23:50:16 +03:00
fireWall = CDefHandler : : giveDef ( " C07SPF61 " ) ;
bigForceField [ 0 ] = CDefHandler : : giveDef ( " C15SPE10.DEF " ) ;
bigForceField [ 1 ] = CDefHandler : : giveDef ( " C15SPE7.DEF " ) ;
smallForceField [ 0 ] = CDefHandler : : giveDef ( " C15SPE1.DEF " ) ;
smallForceField [ 1 ] = CDefHandler : : giveDef ( " C15SPE4.DEF " ) ;
2012-05-05 00:16:39 +03:00
2013-06-29 16:05:48 +03:00
for ( auto hex : bfield )
2012-06-09 19:45:45 +03:00
addChild ( hex ) ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
if ( tacticsMode )
bTacticNextStack ( ) ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
CCS - > musich - > stopMusic ( ) ;
int channel = CCS - > soundh - > playSoundFromSet ( CCS - > soundh - > battleIntroSounds ) ;
2012-08-06 10:34:37 +03:00
auto onIntroPlayed = [ ] ( )
{
if ( LOCPLINT - > battleInt )
CCS - > musich - > playMusicFromSet ( " battle " , true ) ;
} ;
CCS - > soundh - > setCallback ( channel , onIntroPlayed ) ;
memset ( stackCountOutsideHexes , 1 , GameConstants : : BFIELD_SIZE * sizeof ( bool ) ) ; //initialize array with trues
2012-04-04 11:03:52 +03:00
currentAction = INVALID ;
selectedAction = INVALID ;
2012-06-02 18:16:54 +03:00
addUsedEvents ( RCLICK | MOVE | KEYBOARD ) ;
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
{
2013-06-26 14:18:27 +03:00
curInt - > battleInt = nullptr ;
2012-02-20 00:03:43 +03:00
givenCommand - > cond . notify_all ( ) ; //that two lines should make any activeStack waiting thread to finish
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
SDL_FreeSurface ( background ) ;
SDL_FreeSurface ( menu ) ;
SDL_FreeSurface ( amountNormal ) ;
SDL_FreeSurface ( amountNegative ) ;
SDL_FreeSurface ( amountPositive ) ;
SDL_FreeSurface ( amountEffNeutral ) ;
SDL_FreeSurface ( cellBorders ) ;
SDL_FreeSurface ( backgroundWithHexes ) ;
delete bOptions ;
delete bSurrender ;
delete bFlee ;
delete bAutofight ;
delete bSpell ;
delete bWait ;
delete bDefence ;
2012-06-09 19:45:45 +03:00
2013-06-29 16:05:48 +03:00
for ( auto hex : bfield )
2012-06-09 19:45:45 +03:00
delete hex ;
2011-12-14 00:35:28 +03:00
delete bConsoleUp ;
delete bConsoleDown ;
delete console ;
delete givenCommand ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
delete attackingHero ;
delete defendingHero ;
delete queue ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
SDL_FreeSurface ( cellBorder ) ;
SDL_FreeSurface ( cellShade ) ;
2009-09-10 14:28:34 +03:00
2013-06-29 16:05:48 +03:00
for ( auto & elem : creAnims )
delete elem . second ;
2009-09-10 14:28:34 +03:00
2013-06-29 16:05:48 +03:00
for ( auto & elem : idToProjectile )
delete elem . second ;
2009-09-10 14:28:34 +03:00
2013-06-29 16:05:48 +03:00
for ( auto & elem : idToObstacle )
delete elem . second ;
2009-09-11 15:46:26 +03:00
2012-05-05 00:16:39 +03:00
delete quicksand ;
delete landMine ;
2012-05-18 23:50:16 +03:00
delete fireWall ;
delete smallForceField [ 0 ] ;
delete smallForceField [ 1 ] ;
delete bigForceField [ 0 ] ;
delete bigForceField [ 1 ] ;
2012-05-05 00:16:39 +03:00
2011-12-14 00:35:28 +03:00
delete siegeH ;
2014-03-07 16:21:09 +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
2011-12-14 00:35:28 +03:00
if ( adventureInt & & adventureInt - > selection )
{
2012-11-06 19:39:29 +03:00
int terrain = LOCPLINT - > cb - > getTile ( adventureInt - > selection - > visitablePos ( ) ) - > terType ;
2012-08-06 10:34:37 +03:00
CCS - > musich - > playMusicFromSet ( " terrain " , terrain , true ) ;
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 : : 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 ;
2011-12-14 00:35:28 +03:00
redrawBackgroundWithHexes ( activeStack ) ;
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 ;
2011-12-14 00:35:28 +03:00
redrawBackgroundWithHexes ( activeStack ) ;
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 ( )
{
2013-06-23 14:25:48 +03:00
if ( curInt - > isAutoFightOn )
2013-06-23 00:47:51 +03:00
{
bAutofight - > activate ( ) ;
return ;
}
2012-06-02 18:16:54 +03:00
CIntObject : : activate ( ) ;
2011-12-14 00:35:28 +03:00
bOptions - > activate ( ) ;
bSurrender - > activate ( ) ;
bFlee - > activate ( ) ;
bAutofight - > activate ( ) ;
bSpell - > activate ( ) ;
bWait - > activate ( ) ;
bDefence - > activate ( ) ;
2012-06-09 19:45:45 +03:00
2013-06-29 16:05:48 +03:00
for ( auto hex : bfield )
2012-06-09 19:45:45 +03:00
hex - > activate ( ) ;
2011-12-14 00:35:28 +03:00
if ( attackingHero )
attackingHero - > activate ( ) ;
if ( defendingHero )
defendingHero - > activate ( ) ;
2012-01-12 18:23:00 +03: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
if ( tacticsMode )
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
btactNext - > activate ( ) ;
btactEnd - > activate ( ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
else
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
bConsoleUp - > activate ( ) ;
bConsoleDown - > activate ( ) ;
2009-09-10 14:28:34 +03:00
}
2009-09-11 15:46:26 +03:00
2011-12-14 00:35:28 +03:00
LOCPLINT - > cingconsole - > activate ( ) ;
}
void CBattleInterface : : deactivate ( )
{
2012-06-02 18:16:54 +03:00
CIntObject : : deactivate ( ) ;
2011-12-14 00:35:28 +03:00
bOptions - > deactivate ( ) ;
bSurrender - > deactivate ( ) ;
bFlee - > deactivate ( ) ;
bAutofight - > deactivate ( ) ;
bSpell - > deactivate ( ) ;
bWait - > deactivate ( ) ;
bDefence - > deactivate ( ) ;
2012-06-09 19:45:45 +03:00
2013-06-29 16:05:48 +03:00
for ( auto hex : bfield )
2012-06-09 19:45:45 +03:00
hex - > deactivate ( ) ;
2011-12-14 00:35:28 +03:00
if ( attackingHero )
attackingHero - > deactivate ( ) ;
if ( defendingHero )
defendingHero - > deactivate ( ) ;
2012-01-12 18:23:00 +03: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
if ( tacticsMode )
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
btactNext - > deactivate ( ) ;
btactEnd - > deactivate ( ) ;
2009-09-10 14:28:34 +03:00
}
else
{
2011-12-14 00:35:28 +03:00
bConsoleUp - > deactivate ( ) ;
bConsoleDown - > deactivate ( ) ;
2009-09-20 15:47:40 +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 )
{
if ( key . keysym . sym = = SDLK_q & & key . state = = SDL_PRESSED )
2009-09-04 17:11:42 +03:00
{
2012-01-12 18:23:00 +03: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
}
else if ( key . keysym . sym = = SDLK_ESCAPE & & spellDestSelectMode )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03: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 )
{
2012-06-09 19:45:45 +03:00
auto hexItr = std : : find_if ( bfield . begin ( ) , bfield . end ( ) , [ ] ( const CClickableHex * hex )
2011-12-14 00:35:28 +03:00
{
2012-06-09 19:45:45 +03:00
return hex - > hovered & & hex - > strictHovered ;
2012-03-31 00:36:07 +03:00
} ) ;
2011-12-14 00:35:28 +03:00
2012-06-09 19:45:45 +03:00
handleHex ( hexItr = = bfield . end ( ) ? - 1 : ( * hexItr ) - > myNumber , MOVE ) ;
2011-12-14 00:35:28 +03:00
}
void CBattleInterface : : setBattleCursor ( const int myNumber )
{
2012-06-09 19:45:45 +03:00
const CClickableHex & hoveredHex = * bfield [ myNumber ] ;
2011-12-14 00:35:28 +03:00
CCursorHandler * cursor = CCS - > curh ;
const double subdividingAngle = 2.0 * M_PI / 6.0 ; // Divide a hex into six sectors.
const double hexMidX = hoveredHex . pos . x + hoveredHex . pos . w / 2 ;
const double hexMidY = hoveredHex . pos . y + hoveredHex . pos . h / 2 ;
const double cursorHexAngle = M_PI - atan2 ( hexMidY - cursor - > ypos , cursor - > xpos - hexMidX ) + subdividingAngle / 2 ; //TODO: refactor this nightmare
const double sector = fmod ( cursorHexAngle / subdividingAngle , 6.0 ) ;
const int zigzagCorrection = ! ( ( myNumber / GameConstants : : BFIELD_WIDTH ) % 2 ) ; // Off-by-one correction needed to deal with the odd battlefield rows.
std : : vector < int > sectorCursor ; // From left to bottom left.
sectorCursor . push_back ( 8 ) ;
sectorCursor . push_back ( 9 ) ;
sectorCursor . push_back ( 10 ) ;
sectorCursor . push_back ( 11 ) ;
sectorCursor . push_back ( 12 ) ;
sectorCursor . push_back ( 7 ) ;
2011-10-01 22:56:54 +03:00
2011-12-14 00:35:28 +03:00
const bool doubleWide = activeStack - > doubleWide ( ) ;
bool aboveAttackable = true , belowAttackable = true ;
2011-10-01 22:56:54 +03:00
2011-12-14 00:35:28 +03:00
// Exclude directions which cannot be attacked from.
// Check to the left.
2012-02-16 20:10:58 +03:00
if ( myNumber % GameConstants : : BFIELD_WIDTH < = 1 | | ! vstd : : contains ( occupyableHexes , myNumber - 1 ) )
2011-12-14 00:35:28 +03:00
{
sectorCursor [ 0 ] = - 1 ;
}
// Check top left, top right as well as above for 2-hex creatures.
2012-02-16 20:10:58 +03:00
if ( myNumber / GameConstants : : BFIELD_WIDTH = = 0 )
2011-12-14 00:35:28 +03:00
{
sectorCursor [ 1 ] = - 1 ;
sectorCursor [ 2 ] = - 1 ;
aboveAttackable = false ;
2012-02-16 20:10:58 +03:00
}
else
2011-12-14 00:35:28 +03:00
{
2012-02-16 20:10:58 +03:00
if ( doubleWide )
2011-12-14 00:35:28 +03:00
{
bool attackRow [ 4 ] = { true , true , true , true } ;
2011-10-01 22:56:54 +03:00
2011-12-14 00:35:28 +03:00
if ( myNumber % GameConstants : : BFIELD_WIDTH < = 1 | | ! vstd : : contains ( occupyableHexes , myNumber - GameConstants : : BFIELD_WIDTH - 2 + zigzagCorrection ) )
attackRow [ 0 ] = false ;
if ( ! vstd : : contains ( occupyableHexes , myNumber - GameConstants : : BFIELD_WIDTH - 1 + zigzagCorrection ) )
attackRow [ 1 ] = false ;
if ( ! vstd : : contains ( occupyableHexes , myNumber - GameConstants : : BFIELD_WIDTH + zigzagCorrection ) )
attackRow [ 2 ] = false ;
if ( myNumber % GameConstants : : BFIELD_WIDTH > = GameConstants : : BFIELD_WIDTH - 2 | | ! vstd : : contains ( occupyableHexes , myNumber - GameConstants : : BFIELD_WIDTH + 1 + zigzagCorrection ) )
attackRow [ 3 ] = false ;
2011-10-01 22:56:54 +03:00
2011-12-14 00:35:28 +03:00
if ( ! ( attackRow [ 0 ] & & attackRow [ 1 ] ) )
sectorCursor [ 1 ] = - 1 ;
if ( ! ( attackRow [ 1 ] & & attackRow [ 2 ] ) )
aboveAttackable = false ;
if ( ! ( attackRow [ 2 ] & & attackRow [ 3 ] ) )
sectorCursor [ 2 ] = - 1 ;
}
else
{
if ( ! vstd : : contains ( occupyableHexes , myNumber - GameConstants : : BFIELD_WIDTH - 1 + zigzagCorrection ) )
sectorCursor [ 1 ] = - 1 ;
if ( ! vstd : : contains ( occupyableHexes , myNumber - GameConstants : : BFIELD_WIDTH + zigzagCorrection ) )
sectorCursor [ 2 ] = - 1 ;
}
}
// Check to the right.
if ( myNumber % GameConstants : : BFIELD_WIDTH > = GameConstants : : BFIELD_WIDTH - 2 | | ! vstd : : contains ( occupyableHexes , myNumber + 1 ) )
{
sectorCursor [ 3 ] = - 1 ;
}
// Check bottom right, bottom left as well as below for 2-hex creatures.
if ( myNumber / GameConstants : : BFIELD_WIDTH = = GameConstants : : BFIELD_HEIGHT - 1 )
{
sectorCursor [ 4 ] = - 1 ;
sectorCursor [ 5 ] = - 1 ;
belowAttackable = false ;
2012-02-16 20:10:58 +03:00
}
else
2011-12-14 00:35:28 +03:00
{
if ( doubleWide )
{
bool attackRow [ 4 ] = { true , true , true , true } ;
2011-10-01 22:56:54 +03:00
2011-12-14 00:35:28 +03:00
if ( myNumber % GameConstants : : BFIELD_WIDTH < = 1 | | ! vstd : : contains ( occupyableHexes , myNumber + GameConstants : : BFIELD_WIDTH - 2 + zigzagCorrection ) )
attackRow [ 0 ] = false ;
if ( ! vstd : : contains ( occupyableHexes , myNumber + GameConstants : : BFIELD_WIDTH - 1 + zigzagCorrection ) )
attackRow [ 1 ] = false ;
if ( ! vstd : : contains ( occupyableHexes , myNumber + GameConstants : : BFIELD_WIDTH + zigzagCorrection ) )
attackRow [ 2 ] = false ;
if ( myNumber % GameConstants : : BFIELD_WIDTH > = GameConstants : : BFIELD_WIDTH - 2 | | ! vstd : : contains ( occupyableHexes , myNumber + GameConstants : : BFIELD_WIDTH + 1 + zigzagCorrection ) )
attackRow [ 3 ] = false ;
2011-10-01 22:56:54 +03:00
2011-12-14 00:35:28 +03:00
if ( ! ( attackRow [ 0 ] & & attackRow [ 1 ] ) )
sectorCursor [ 5 ] = - 1 ;
if ( ! ( attackRow [ 1 ] & & attackRow [ 2 ] ) )
belowAttackable = false ;
if ( ! ( attackRow [ 2 ] & & attackRow [ 3 ] ) )
sectorCursor [ 4 ] = - 1 ;
2012-02-16 20:10:58 +03:00
}
else
2011-12-14 00:35:28 +03:00
{
if ( ! vstd : : contains ( occupyableHexes , myNumber + GameConstants : : BFIELD_WIDTH + zigzagCorrection ) )
sectorCursor [ 4 ] = - 1 ;
if ( ! vstd : : contains ( occupyableHexes , myNumber + GameConstants : : BFIELD_WIDTH - 1 + zigzagCorrection ) )
sectorCursor [ 5 ] = - 1 ;
2009-01-15 19:01:08 +02:00
}
}
2011-12-14 00:35:28 +03:00
// Determine index from sector.
int cursorIndex ;
2012-02-16 20:10:58 +03:00
if ( doubleWide )
2011-12-14 00:35:28 +03:00
{
sectorCursor . insert ( sectorCursor . begin ( ) + 5 , belowAttackable ? 13 : - 1 ) ;
sectorCursor . insert ( sectorCursor . begin ( ) + 2 , aboveAttackable ? 14 : - 1 ) ;
if ( sector < 1.5 )
cursorIndex = sector ;
else if ( sector > = 1.5 & & sector < 2.5 )
cursorIndex = 2 ;
else if ( sector > = 2.5 & & sector < 4.5 )
cursorIndex = ( int ) sector + 1 ;
else if ( sector > = 4.5 & & sector < 5.5 )
cursorIndex = 6 ;
else
cursorIndex = ( int ) sector + 2 ;
2012-02-16 20:10:58 +03:00
}
else
2011-12-14 00:35:28 +03:00
{
cursorIndex = sector ;
}
2012-09-28 22:18:08 +03:00
// Generally should NEVER happen, but to avoid the possibility of having endless loop below... [#1016]
if ( ! vstd : : contains_if ( sectorCursor , [ ] ( int sc ) { return sc ! = - 1 ; } ) )
{
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < " Error: for hex " < < myNumber < < " cannot find a hex to attack from! " ;
2012-09-28 22:18:08 +03:00
attackingHex = - 1 ;
return ;
}
2011-12-14 00:35:28 +03:00
// Find the closest direction attackable, starting with the right one.
// FIXME: Is this really how the original H3 client does it?
int i = 0 ;
while ( sectorCursor [ ( cursorIndex + i ) % sectorCursor . size ( ) ] = = - 1 ) //Why hast thou forsaken me?
i = i < = 0 ? 1 - i : - i ; // 0, 1, -1, 2, -2, 3, -3 etc..
int index = ( cursorIndex + i ) % sectorCursor . size ( ) ; //hopefully we get elements from sectorCursor
2012-12-14 18:32:53 +03:00
cursor - > changeGraphic ( ECursor : : COMBAT , sectorCursor [ index ] ) ;
2011-12-14 00:35:28 +03:00
switch ( index )
{
case 0 :
attackingHex = myNumber - 1 ; //left
break ;
case 1 :
attackingHex = myNumber - GameConstants : : BFIELD_WIDTH - 1 + zigzagCorrection ; //top left
break ;
case 2 :
attackingHex = myNumber - GameConstants : : BFIELD_WIDTH + zigzagCorrection ; //top right
break ;
case 3 :
attackingHex = myNumber + 1 ; //right
2013-11-03 15:51:25 +03:00
break ;
2011-12-14 00:35:28 +03:00
case 4 :
attackingHex = myNumber + GameConstants : : BFIELD_WIDTH + zigzagCorrection ; //bottom right
2013-11-03 15:51:25 +03:00
break ;
case 5 :
2011-12-14 00:35:28 +03:00
attackingHex = myNumber + GameConstants : : BFIELD_WIDTH - 1 + zigzagCorrection ; //bottom left
break ;
}
2011-12-22 16:05:19 +03:00
BattleHex hex ( attackingHex ) ;
2011-12-14 00:35:28 +03:00
if ( ! hex . isValid ( ) )
attackingHex = - 1 ;
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
{
2011-12-14 00:35:28 +03:00
if ( ! down & & spellDestSelectMode )
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
2011-12-14 00:35:28 +03:00
void CBattleInterface : : bOptionsf ( )
{
if ( spellDestSelectMode ) //we are casting a spell
return ;
2012-12-14 18:32:53 +03:00
CCS - > curh - > changeGraphic ( ECursor : : ADVENTURE , 0 ) ;
2011-12-14 00:35:28 +03:00
2011-12-22 16:05:19 +03:00
Rect tempRect = genRect ( 431 , 481 , 160 , 84 ) ;
2011-12-14 00:35:28 +03:00
tempRect + = pos . topLeft ( ) ;
2013-06-29 16:05:48 +03:00
auto optionsWin = new CBattleOptionsWindow ( tempRect , this ) ;
2011-12-14 00:35:28 +03:00
GH . pushInt ( optionsWin ) ;
}
void CBattleInterface : : bSurrenderf ( )
{
if ( spellDestSelectMode ) //we are casting a spell
return ;
int cost = curInt - > cb - > battleGetSurrenderCost ( ) ;
if ( cost > = 0 )
{
2013-01-20 23:29:35 +03:00
std : : string enemyHeroName = curInt - > cb - > battleGetEnemyHero ( ) . name ;
if ( enemyHeroName . empty ( ) )
enemyHeroName = " #ENEMY# " ; //TODO: should surrendering without enemy hero be enabled?
2011-12-14 00:35:28 +03:00
std : : string surrenderMessage = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 32 ] ) % enemyHeroName % cost ) ; //%s states: "I will accept your surrender and grant you and your troops safe passage for the price of %d gold."
2013-01-20 23:29:35 +03:00
curInt - > showYesNoDialog ( surrenderMessage , [ this ] { reallySurrender ( ) ; } , 0 , false ) ;
2011-12-14 00:35:28 +03:00
}
}
void CBattleInterface : : bFleef ( )
{
if ( spellDestSelectMode ) //we are casting a spell
return ;
if ( curInt - > cb - > battleCanFlee ( ) )
{
2014-08-04 21:33:59 +03:00
CFunctionList < void ( ) > ony = std : : bind ( & CBattleInterface : : reallyFlee , this ) ;
2012-04-09 05:53:50 +03:00
curInt - > showYesNoDialog ( CGI - > generaltexth - > allTexts [ 28 ] , ony , 0 , false ) ; //Are you sure you want to retreat?
2011-12-14 00:35:28 +03:00
}
else
{
2011-12-22 16:05:19 +03:00
std : : vector < CComponent * > comps ;
2011-12-14 00:35:28 +03:00
std : : string heroName ;
//calculating fleeing hero's name
if ( attackingHeroInstance )
if ( attackingHeroInstance - > tempOwner = = curInt - > cb - > getMyColor ( ) )
heroName = attackingHeroInstance - > name ;
if ( defendingHeroInstance )
if ( defendingHeroInstance - > tempOwner = = curInt - > cb - > getMyColor ( ) )
heroName = defendingHeroInstance - > name ;
//calculating text
2014-11-22 13:45:13 +02:00
auto txt = boost : : format ( CGI - > generaltexth - > allTexts [ 340 ] ) % heroName ; //The Shackles of War are present. %s can not retreat!
2011-12-14 00:35:28 +03:00
//printing message
2014-11-22 13:45:13 +02:00
curInt - > showInfoDialog ( boost : : to_string ( txt ) , comps ) ;
2011-12-14 00:35:28 +03:00
}
}
void CBattleInterface : : reallyFlee ( )
{
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : RETREAT , 0 , 0 ) ;
2012-12-14 18:32:53 +03:00
CCS - > curh - > changeGraphic ( ECursor : : ADVENTURE , 0 ) ;
2011-12-14 00:35:28 +03:00
}
void CBattleInterface : : reallySurrender ( )
{
if ( curInt - > cb - > getResourceAmount ( Res : : GOLD ) < curInt - > cb - > battleGetSurrenderCost ( ) )
{
curInt - > showInfoDialog ( CGI - > generaltexth - > allTexts [ 29 ] ) ; //You don't have enough gold!
}
else
{
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : SURRENDER , 0 , 0 ) ;
2012-12-14 18:32:53 +03:00
CCS - > curh - > changeGraphic ( ECursor : : ADVENTURE , 0 ) ;
2009-09-24 16:44:55 +03:00
}
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : bAutofightf ( )
2009-09-15 15:20:11 +03:00
{
2011-12-14 00:35:28 +03:00
if ( spellDestSelectMode ) //we are casting a spell
return ;
2014-03-07 16:21:09 +03:00
2013-06-23 00:47:51 +03:00
//Stop auto-fight mode
2013-06-23 14:25:48 +03:00
if ( curInt - > isAutoFightOn )
2013-06-22 17:47:20 +03:00
{
2013-06-23 14:25:48 +03:00
assert ( curInt - > autofightingAI ) ;
curInt - > isAutoFightOn = false ;
2013-06-23 00:47:51 +03:00
logGlobal - > traceStream ( ) < < " Stopping the autofight... " ;
2013-06-22 17:47:20 +03:00
}
else
{
2014-03-07 16:21:09 +03:00
curInt - > isAutoFightOn = true ;
2013-07-21 13:10:38 +03:00
blockUI ( true ) ;
2013-06-23 14:25:48 +03:00
auto ai = CDynLibHandler : : getNewBattleAI ( settings [ " server " ] [ " neutralAI " ] . String ( ) ) ;
ai - > init ( curInt - > cb ) ;
ai - > battleStart ( army1 , army2 , int3 ( 0 , 0 , 0 ) , attackingHeroInstance , defendingHeroInstance , curInt - > cb - > battleGetMySide ( ) ) ;
curInt - > autofightingAI = ai ;
curInt - > cb - > registerBattleInterface ( ai ) ;
2013-06-22 17:47:20 +03:00
2013-06-23 00:47:51 +03:00
requestAutofightingAIToTakeAction ( ) ;
2013-06-22 17:47:20 +03:00
}
2009-09-15 15:20:11 +03:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : bSpellf ( )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
if ( spellDestSelectMode ) //we are casting a spell
return ;
2012-12-14 18:32:53 +03:00
CCS - > curh - > changeGraphic ( ECursor : : ADVENTURE , 0 ) ;
2011-12-14 00:35:28 +03:00
2012-08-26 12:07:48 +03:00
if ( ! myTurn )
return ;
auto myHero = currentHero ( ) ;
ESpellCastProblem : : ESpellCastProblem spellCastProblem ;
if ( curInt - > cb - > battleCanCastSpell ( & spellCastProblem ) )
2012-01-19 17:33:22 +03:00
{
2013-06-29 16:05:48 +03:00
auto spellWindow = new CSpellWindow ( genRect ( 595 , 620 , ( screen - > w - 620 ) / 2 , ( screen - > h - 595 ) / 2 ) , myHero , curInt . get ( ) ) ;
2012-01-19 17:33:22 +03:00
GH . pushInt ( spellWindow ) ;
}
2012-08-26 12:07:48 +03:00
else if ( spellCastProblem = = ESpellCastProblem : : MAGIC_IS_BLOCKED )
{
//Handle Orb of Inhibition-like effects -> we want to display dialog with info, why casting is impossible
2012-09-20 19:55:21 +03:00
auto blockingBonus = currentHero ( ) - > getBonusLocalFirst ( Selector : : type ( Bonus : : BLOCK_ALL_MAGIC ) ) ;
2012-08-26 12:07:48 +03:00
if ( ! blockingBonus )
return ; ;
2014-03-07 16:21:09 +03:00
2012-08-26 12:07:48 +03:00
if ( blockingBonus - > source = = Bonus : : ARTIFACT )
{
const int artID = blockingBonus - > sid ;
//If we have artifact, put name of our hero. Otherwise assume it's the enemy.
//TODO check who *really* is source of bonus
std : : string heroName = myHero - > hasArt ( artID ) ? myHero - > name : enemyHero ( ) . name ;
//%s wields the %s, an ancient artifact which creates a p dead to all magic.
2014-03-07 16:21:09 +03:00
LOCPLINT - > showInfoDialog ( boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 683 ] )
2012-08-26 12:07:48 +03:00
% heroName % CGI - > arth - > artifacts [ artID ] - > Name ( ) ) ) ;
}
}
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : bWaitf ( )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
if ( spellDestSelectMode ) //we are casting a spell
return ;
2009-01-15 19:01:08 +02:00
2013-06-26 14:18:27 +03:00
if ( activeStack ! = nullptr )
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : WAIT , 0 , activeStack - > ID ) ;
2011-12-14 00:35:28 +03:00
}
2009-01-15 19:01:08 +02:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : bDefencef ( )
{
if ( spellDestSelectMode ) //we are casting a spell
return ;
2009-01-15 19:01:08 +02:00
2013-06-26 14:18:27 +03:00
if ( activeStack ! = nullptr )
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : DEFEND , 0 , activeStack - > ID ) ;
2011-12-14 00:35:28 +03:00
}
2009-09-17 15:59:04 +03:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : bConsoleUpf ( )
{
if ( spellDestSelectMode ) //we are casting a spell
return ;
2009-07-26 15:15:38 +03:00
2011-12-14 00:35:28 +03:00
console - > scrollUp ( ) ;
}
2009-11-08 16:44:58 +02:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : bConsoleDownf ( )
{
if ( spellDestSelectMode ) //we are casting a spell
return ;
console - > scrollDown ( ) ;
}
void CBattleInterface : : newStack ( const CStack * stack )
{
2013-07-07 22:44:08 +03:00
creDir [ stack - > ID ] = stack - > attackerOwned ; // must be set before getting stack position
Point coords = CClickableHex : : getXYUnitAnim ( stack - > position , stack , this ) ;
2011-12-14 00:35:28 +03:00
if ( stack - > position < 0 ) //turret
2009-11-08 16:44:58 +02:00
{
2013-07-06 19:10:20 +03:00
const CCreature * turretCreature = CGI - > creh - > creatures [ siegeH - > town - > town - > clientInfo . siegeShooter ] ;
creAnims [ stack - > ID ] = AnimationControls : : getAnimation ( turretCreature ) ;
2011-12-14 00:35:28 +03:00
2013-07-28 17:49:50 +03:00
// Turret positions are read out of the config/wall_pos.txt
2011-12-14 00:35:28 +03:00
int posID = 0 ;
switch ( stack - > position )
2010-03-02 13:40:29 +02:00
{
2011-12-14 00:35:28 +03:00
case - 2 : // keep creature
posID = 18 ;
break ;
case - 3 : // bottom creature
posID = 19 ;
break ;
case - 4 : // upper creature
posID = 20 ;
break ;
2010-03-02 13:40:29 +02:00
}
2011-12-14 00:35:28 +03:00
if ( posID ! = 0 )
2010-03-02 13:40:29 +02:00
{
2012-10-05 21:03:49 +03:00
coords . x = siegeH - > town - > town - > clientInfo . siegePositions [ posID ] . x + this - > pos . x ;
coords . y = siegeH - > town - > town - > clientInfo . siegePositions [ posID ] . y + this - > pos . y ;
2010-03-02 13:40:29 +02:00
}
2013-08-01 01:24:44 +03:00
creAnims [ stack - > ID ] - > pos . h = 225 ;
2011-12-14 00:35:28 +03:00
}
else
{
2013-07-06 19:10:20 +03:00
creAnims [ stack - > ID ] = AnimationControls : : getAnimation ( stack - > getCreature ( ) ) ;
creAnims [ stack - > ID ] - > onAnimationReset + = std : : bind ( & onAnimationFinished , stack , creAnims [ stack - > ID ] ) ;
creAnims [ stack - > ID ] - > pos . h = creAnims [ stack - > ID ] - > getHeight ( ) ;
2011-12-14 00:35:28 +03:00
}
2013-07-06 19:10:20 +03:00
creAnims [ stack - > ID ] - > pos . x = coords . x ;
creAnims [ stack - > ID ] - > pos . y = coords . y ;
creAnims [ stack - > ID ] - > pos . w = creAnims [ stack - > ID ] - > getWidth ( ) ;
2011-12-14 00:35:28 +03:00
creAnims [ stack - > ID ] - > setType ( CCreatureAnim : : HOLDING ) ;
2014-03-07 16:21:09 +03:00
2011-12-14 00:35:28 +03:00
}
void CBattleInterface : : stackRemoved ( int stackID )
{
2015-10-06 02:46:35 +02:00
if ( activeStack ! = nullptr )
{
if ( activeStack - > ID = = stackID )
{
2015-10-08 07:15:29 +02:00
BattleAction * action = new BattleAction ( ) ;
action - > side = defendingHeroInstance ? ( curInt - > playerID = = defendingHeroInstance - > tempOwner ) : false ;
action - > actionType = Battle : : CANCEL ;
action - > stackNumber = activeStack - > ID ;
givenCommand - > setn ( action ) ;
2015-10-06 02:46:35 +02:00
setActiveStack ( nullptr ) ;
}
}
2016-01-26 08:37:55 +02:00
2011-12-14 00:35:28 +03:00
delete creAnims [ stackID ] ;
creAnims . erase ( stackID ) ;
creDir . erase ( stackID ) ;
2012-09-18 18:17:32 +03:00
redrawBackgroundWithHexes ( activeStack ) ;
2012-02-22 20:43:59 +03:00
queue - > update ( ) ;
2011-12-14 00:35:28 +03:00
}
void CBattleInterface : : stackActivated ( const CStack * stack ) //TODO: check it all before game state is changed due to abilities
{
2013-06-26 14:18:27 +03:00
//givenCommand = nullptr;
2011-12-14 00:35:28 +03:00
stackToActivate = stack ;
2012-02-18 20:39:47 +03:00
waitForAnims ( ) ;
//if(pendingAnims.size() == 0)
2012-02-20 00:03:43 +03:00
if ( stackToActivate ) //during waiting stack may have gotten activated through show
2011-12-14 00:35:28 +03:00
activateStack ( ) ;
}
2011-12-22 16:05:19 +03:00
void CBattleInterface : : stackMoved ( const CStack * stack , std : : vector < BattleHex > destHex , int distance )
2011-12-14 00:35:28 +03:00
{
addNewAnim ( new CMovementAnimation ( this , stack , destHex , distance ) ) ;
waitForAnims ( ) ;
}
2011-12-22 16:05:19 +03:00
void CBattleInterface : : stacksAreAttacked ( std : : vector < StackAttackedInfo > attackedInfos )
2011-12-14 00:35:28 +03:00
{
2013-06-29 16:05:48 +03:00
for ( auto & attackedInfo : attackedInfos )
2011-12-14 00:35:28 +03:00
{
2014-09-23 16:02:30 +03:00
//if (!attackedInfo.cloneKilled) //FIXME: play dead animation for cloned creature before it vanishes
2013-06-29 16:05:48 +03:00
addNewAnim ( new CDefenceAnimation ( attackedInfo , this ) ) ;
2014-09-23 16:02:30 +03:00
2013-06-29 16:05:48 +03:00
if ( attackedInfo . rebirth )
2011-06-01 21:26:44 +03:00
{
2013-06-29 16:05:48 +03:00
displayEffect ( 50 , attackedInfo . defender - > position ) ; //TODO: play reverse death animation
2011-12-14 00:35:28 +03:00
CCS - > soundh - > playSound ( soundBase : : RESURECT ) ;
2011-06-01 21:26:44 +03:00
}
2009-11-08 16:44:58 +02:00
}
2011-12-14 00:35:28 +03:00
waitForAnims ( ) ;
int targets = 0 , killed = 0 , damage = 0 ;
2013-06-29 16:05:48 +03:00
for ( auto & attackedInfo : attackedInfos )
2009-11-08 16:44:58 +02:00
{
2011-12-14 00:35:28 +03:00
+ + targets ;
2013-06-29 16:05:48 +03:00
killed + = attackedInfo . amountKilled ;
damage + = attackedInfo . dmg ;
2009-11-08 16:44:58 +02:00
}
2011-12-14 00:35:28 +03:00
2013-06-29 16:05:48 +03:00
for ( auto & attackedInfo : attackedInfos )
2011-10-09 14:23:24 +03:00
{
2013-06-29 16:05:48 +03:00
if ( attackedInfo . rebirth )
creAnims [ attackedInfo . defender - > ID ] - > setType ( CCreatureAnim : : HOLDING ) ;
if ( attackedInfo . cloneKilled )
stackRemoved ( attackedInfo . defender - > ID ) ;
2011-10-09 14:23:24 +03:00
}
2014-09-23 16:02:30 +03:00
/* if (attackedInfos.front().cloneKilled) //FIXME: cloned stack is already removed
return ; */
if ( targets > 1 )
printConsoleAttacked ( attackedInfos . front ( ) . defender , damage , killed , attackedInfos . front ( ) . attacker , true ) ; //creatures perish
else
printConsoleAttacked ( attackedInfos . front ( ) . defender , damage , killed , attackedInfos . front ( ) . attacker , false ) ;
2009-03-21 18:03:07 +02:00
}
2011-12-22 16:05:19 +03:00
void CBattleInterface : : stackAttacking ( const CStack * attacker , BattleHex dest , const CStack * attacked , bool shooting )
2009-03-21 18:03:07 +02:00
{
2011-12-14 00:35:28 +03:00
if ( shooting )
2009-03-21 18:03:07 +02:00
{
2011-12-14 00:35:28 +03:00
addNewAnim ( new CShootingAnimation ( this , attacker , dest , attacked ) ) ;
2011-01-18 19:23:31 +02:00
}
2011-12-14 00:35:28 +03:00
else
2011-01-18 19:23:31 +02:00
{
2011-12-14 00:35:28 +03:00
addNewAnim ( new CMeleeAttackAnimation ( this , attacker , dest , attacked ) ) ;
}
2013-07-16 21:12:47 +03:00
//waitForAnims();
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 )
{
console - > addText ( CGI - > generaltexth - > allTexts [ 412 ] ) ;
}
2013-02-04 00:05:44 +03:00
void CBattleInterface : : giveCommand ( Battle : : ActionType action , BattleHex tile , ui32 stackID , si32 additional , si32 selected )
2011-12-14 00:35:28 +03:00
{
2012-04-03 02:23:14 +03:00
const CStack * stack = curInt - > cb - > battleGetStackByID ( stackID ) ;
2013-02-04 00:05:44 +03:00
if ( ! stack & & action ! = Battle : : HERO_SPELL & & action ! = Battle : : RETREAT & & action ! = Battle : : SURRENDER )
2011-12-14 00:35:28 +03:00
{
return ;
2009-01-15 19:01:08 +02:00
}
2012-04-03 02:23:14 +03:00
if ( stack & & stack ! = activeStack )
2013-04-09 17:31:36 +03:00
logGlobal - > warnStream ( ) < < " Warning: giving an order to a non-active stack? " ;
2012-04-03 02:23:14 +03:00
2013-06-29 16:05:48 +03:00
auto ba = new BattleAction ( ) ; //is deleted in CPlayerInterface::activeStack()
2011-12-14 00:35:28 +03:00
ba - > side = defendingHeroInstance ? ( curInt - > playerID = = defendingHeroInstance - > tempOwner ) : false ;
ba - > actionType = action ;
ba - > destinationTile = tile ;
2012-04-03 02:23:14 +03:00
ba - > stackNumber = stackID ;
2011-12-14 00:35:28 +03:00
ba - > additionalInfo = additional ;
2012-04-28 22:40:27 +03:00
ba - > selectedStack = selected ;
2011-01-18 19:23:31 +02:00
2011-12-14 00:35:28 +03:00
//some basic validations
switch ( action )
{
2013-02-04 00:05:44 +03:00
case Battle : : WALK_AND_ATTACK :
2011-12-14 00:35:28 +03:00
assert ( curInt - > cb - > battleGetStackByPos ( additional ) ) ; //stack to attack must exist
2013-02-04 00:05:44 +03:00
case Battle : : WALK :
case Battle : : SHOOT :
case Battle : : CATAPULT :
2011-12-14 00:35:28 +03:00
assert ( tile < GameConstants : : BFIELD_SIZE ) ;
break ;
}
2011-01-18 19:23:31 +02:00
2011-12-14 00:35:28 +03:00
if ( ! tacticsMode )
2010-03-02 13:40:29 +02:00
{
2013-04-09 17:31:36 +03:00
logGlobal - > traceStream ( ) < < " Setting command for " < < ( stack ? stack - > nodeName ( ) : " hero " ) ;
2011-12-14 00:35:28 +03:00
myTurn = false ;
2013-07-06 19:10:20 +03:00
setActiveStack ( nullptr ) ;
2011-12-14 00:35:28 +03:00
givenCommand - > setn ( ba ) ;
}
else
{
curInt - > cb - > battleMakeTacticAction ( ba ) ;
vstd : : clear_pointer ( ba ) ;
2013-07-06 19:10:20 +03:00
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
}
2011-12-22 16:05:19 +03:00
bool CBattleInterface : : isTileAttackable ( const BattleHex & number ) const
2009-01-15 19:01:08 +02:00
{
2013-06-29 16:05:48 +03:00
for ( auto & elem : occupyableHexes )
2011-12-14 00:35:28 +03:00
{
2013-06-29 16:05:48 +03:00
if ( BattleHex : : mutualPosition ( elem , number ) ! = - 1 | | elem = = number )
2011-12-14 00:35:28 +03:00
return true ;
}
return false ;
}
2009-01-15 19:01:08 +02:00
2011-12-22 16:05:19 +03:00
bool CBattleInterface : : isCatapultAttackable ( BattleHex hex ) const
2011-12-14 00:35:28 +03:00
{
2013-12-08 20:54:13 +03:00
if ( ! siegeH | | tacticsMode ) return false ;
2011-12-14 00:35:28 +03:00
2013-12-08 20:54:13 +03:00
auto wallPart = curInt - > cb - > battleHexToWallPart ( hex ) ;
if ( ! curInt - > cb - > isWallPartPotentiallyAttackable ( wallPart ) ) return false ;
2011-12-14 00:35:28 +03:00
2013-12-08 20:54:13 +03:00
auto state = curInt - > cb - > battleGetWallState ( static_cast < int > ( wallPart ) ) ;
2013-08-06 18:25:51 +03:00
return state ! = EWallState : : DESTROYED & & state ! = EWallState : : NONE ;
2011-12-14 00:35:28 +03:00
}
const CGHeroInstance * CBattleInterface : : getActiveHero ( )
{
const CStack * attacker = activeStack ;
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
}
if ( attacker - > attackerOwned )
{
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
{
2015-03-18 16:48:32 +02:00
if ( ca . attacker ! = - 1 )
2011-10-08 09:59:36 +03:00
{
2011-12-14 00:35:28 +03:00
const CStack * stack = curInt - > cb - > battleGetStackByID ( ca . attacker ) ;
2015-03-18 12:27:07 +02:00
for ( auto attackInfo : ca . attackedParts )
{
addNewAnim ( new CShootingAnimation ( this , stack , attackInfo . destinationTile , nullptr , true , attackInfo . damageDealt ) ) ;
2016-01-26 08:37:55 +02:00
}
2013-07-21 13:10:38 +03:00
}
2015-03-18 16:48:32 +02:00
else
{
2016-01-26 08:37:55 +02:00
//no attacker stack, assume spell-related (earthquake) - only hit animation
2015-03-18 16:48:32 +02:00
for ( auto attackInfo : ca . attackedParts )
{
Point destPos = CClickableHex : : getXYUnitAnim ( attackInfo . destinationTile , nullptr , this ) + Point ( 99 , 120 ) ;
2016-01-26 08:37:55 +02:00
2015-03-18 16:48:32 +02:00
addNewAnim ( new CSpellEffectAnimation ( this , " SGEXPL.DEF " , destPos . x , destPos . y ) ) ;
}
2016-01-26 08:37:55 +02:00
}
2009-11-13 23:01:33 +02:00
2013-07-21 13:10:38 +03:00
waitForAnims ( ) ;
2015-03-18 12:27:07 +02:00
for ( auto attackInfo : ca . attackedParts )
2013-07-21 13:10:38 +03:00
{
2016-02-10 06:10:32 +02:00
int wallId = attackInfo . attackedPart + 2 ;
//gate state changing handled separately
2016-02-13 16:10:14 +02:00
if ( wallId = = SiegeHelper : : GATE )
2016-02-10 06:10:32 +02:00
continue ;
SDL_FreeSurface ( siegeH - > walls [ wallId ] ) ;
siegeH - > walls [ wallId ] = BitmapHandler : : loadBitmap (
siegeH - > getSiegeName ( wallId , curInt - > cb - > battleGetWallState ( attackInfo . attackedPart ) ) ) ;
2011-12-14 00:35:28 +03:00
}
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
{
auto unlockPim = vstd : : makeUnlockGuard ( * LOCPLINT - > pim ) ;
animsAreDisplayed . waitUntil ( false ) ;
}
2011-12-14 00:35:28 +03:00
displayBattleFinished ( ) ;
2013-07-06 19:10:20 +03:00
setActiveStack ( nullptr ) ;
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 ) ;
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
SDL_Rect temp_rect = genRect ( 561 , 470 , ( screen - > w - 800 ) / 2 + 165 , ( screen - > h - 600 ) / 2 + 19 ) ;
2013-06-23 14:25:48 +03:00
resWindow = new CBattleResultWindow ( * bresult , temp_rect , * this - > curInt ) ;
2011-12-14 00:35:28 +03:00
GH . pushInt ( resWindow ) ;
2009-04-16 17:01:27 +03:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : spellCast ( const BattleSpellCast * sc )
2009-08-26 17:09:55 +03:00
{
2014-11-27 23:36:14 +02:00
const SpellID spellID ( sc - > id ) ;
2015-03-22 12:27:11 +02:00
const CSpell & spell = * spellID . toSpell ( ) ;
2009-08-26 17:09:55 +03:00
2015-03-22 12:27:11 +02:00
const std : : string & castSoundPath = spell . getCastSound ( ) ;
2015-02-07 10:13:24 +02:00
2014-03-10 19:00:58 +03:00
if ( ! castSoundPath . empty ( ) )
CCS - > soundh - > playSound ( castSoundPath ) ;
2014-11-27 20:06:11 +02:00
Point srccoord = ( sc - > side ? Point ( 770 , 60 ) : Point ( 30 , 60 ) ) + pos ; //hero position by default
{
const auto casterStackID = sc - > casterStack ;
2014-11-27 20:33:57 +02:00
2014-11-27 20:06:11 +02:00
if ( casterStackID > 0 )
{
const CStack * casterStack = curInt - > cb - > battleGetStackByID ( casterStackID ) ;
if ( casterStack ! = nullptr )
{
2015-03-22 11:18:58 +02:00
srccoord = CClickableHex : : getXYUnitAnim ( casterStack - > position , casterStack , this ) ;
2014-11-27 20:33:57 +02:00
srccoord . x + = 250 ;
srccoord . y + = 240 ;
2014-11-27 20:06:11 +02:00
}
}
2014-11-27 20:33:57 +02:00
}
2015-03-22 15:47:20 +02:00
//todo: play custom cast animation
displaySpellCast ( spellID , BattleHex : : INVALID , false ) ;
2015-03-22 11:18:58 +02:00
2014-11-26 23:27:38 +02:00
//playing projectile animation
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 ) ;
if ( Vflip )
angle = - angle ;
2015-02-07 10:13:24 +02:00
2014-11-26 23:27:38 +02:00
std : : string animToDisplay = spell . animationInfo . selectProjectile ( angle ) ;
2015-02-07 10:13:24 +02:00
2014-11-26 23:27:38 +02:00
if ( ! animToDisplay . empty ( ) )
2009-08-26 17:09:55 +03:00
{
2011-12-14 00:35:28 +03:00
//displaying animation
CDefEssential * animDef = CDefHandler : : giveDefEss ( animToDisplay ) ;
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
2013-07-16 21:12:47 +03:00
int steps = distance / AnimationControls : : getSpellEffectSpeed ( ) + 1 ;
int dx = ( destcoord . x - srccoord . x - animDef - > ourImages [ 0 ] . bitmap - > w ) / steps ;
int dy = ( destcoord . y - srccoord . y - animDef - > ourImages [ 0 ] . bitmap - > h ) / steps ;
2011-12-14 00:35:28 +03:00
delete animDef ;
2015-02-07 10:13:24 +02:00
addNewAnim ( new CSpellEffectAnimation ( 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
}
2014-11-26 23:27:38 +02:00
waitForAnims ( ) ;
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
for ( auto & elem : sc - > affectedCres )
2014-11-26 23:27:38 +02:00
{
2014-11-27 15:51:16 +02:00
BattleHex position = curInt - > cb - > battleGetStackByID ( elem , false ) - > position ;
2015-09-14 04:45:05 +02:00
displaySpellEffect ( spellID , position ) ;
}
2015-02-07 10:13:24 +02:00
2015-09-14 05:21:49 +02:00
//queuing additional animation
for ( auto & elem : sc - > customEffects )
2015-09-14 04:45:05 +02:00
{
2015-09-14 05:21:49 +02:00
BattleHex position = curInt - > cb - > battleGetStackByID ( elem . stack , false ) - > position ;
displayEffect ( elem . effect , position ) ;
2014-11-26 23:27:38 +02:00
}
2011-12-14 00:35:28 +03:00
//displaying message in console
2015-03-22 11:18:58 +02:00
std : : vector < std : : string > logLines ;
2016-01-26 08:37:55 +02:00
2015-03-22 12:27:11 +02:00
spell . prepareBattleLog ( curInt - > cb . get ( ) , sc , logLines ) ;
2016-01-26 08:37:55 +02:00
2015-03-19 00:28:28 +02:00
for ( auto line : logLines )
console - > addText ( line ) ;
2015-03-22 11:18:58 +02:00
2011-12-14 00:35:28 +03:00
waitForAnims ( ) ;
//mana absorption
2014-11-27 18:09:59 +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 ;
2011-12-14 00:35:28 +03:00
addNewAnim ( new CSpellEffectAnimation ( this , sc - > side ? " SP07_A.DEF " : " SP07_B.DEF " , leftHero . x , leftHero . y , 0 , 0 , false ) ) ;
addNewAnim ( new CSpellEffectAnimation ( 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
{
2014-11-27 15:51:16 +02:00
if ( sse . effect . back ( ) . sid = = - 1 & & sse . stacks . size ( ) = = 1 & & sse . effect . size ( ) = = 2 )
2011-12-14 00:35:28 +03:00
{
const Bonus & bns = sse . effect . front ( ) ;
if ( bns . source = = Bonus : : OTHER & & bns . type = = Bonus : : PRIMARY_SKILL )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
//defensive stance
const CStack * stack = LOCPLINT - > cb - > battleGetStackByID ( * sse . stacks . begin ( ) ) ;
int txtid = 120 ;
if ( stack - > count ! = 1 )
txtid + + ; //move to plural text
BonusList defenseBonuses = * ( stack - > getBonuses ( Selector : : typeSubtype ( Bonus : : PRIMARY_SKILL , PrimarySkill : : DEFENSE ) ) ) ;
2015-04-13 09:24:32 +02:00
defenseBonuses . remove_if ( Bonus : : UntilGetsTurn ) ; //remove bonuses gained from defensive stance
2011-12-14 00:35:28 +03:00
int val = stack - > Defense ( ) - defenseBonuses . totalValue ( ) ;
2014-11-22 13:45:13 +02:00
auto txt = boost : : format ( CGI - > generaltexth - > allTexts [ txtid ] ) % ( ( stack - > count ! = 1 ) ? stack - > getCreature ( ) - > namePl : stack - > getCreature ( ) - > nameSing ) % val ;
console - > addText ( boost : : to_string ( txt ) ) ;
2009-01-15 19:01:08 +02:00
}
}
2013-06-26 14:18:27 +03:00
if ( activeStack ! = nullptr ) //it can be -1 when a creature casts effect
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
redrawBackgroundWithHexes ( activeStack ) ;
}
}
2011-05-26 17:47:45 +03:00
2015-01-30 08:39:36 +02:00
void CBattleInterface : : castThisSpell ( SpellID spellID )
2011-12-14 00:35:28 +03:00
{
2013-06-29 16:05:48 +03:00
auto ba = new BattleAction ;
2013-02-04 00:05:44 +03:00
ba - > actionType = Battle : : HERO_SPELL ;
2011-12-14 00:35:28 +03:00
ba - > additionalInfo = spellID ; //spell number
ba - > destinationTile = - 1 ;
ba - > stackNumber = ( attackingHeroInstance - > tempOwner = = curInt - > playerID ) ? - 1 : - 2 ;
ba - > side = defendingHeroInstance ? ( curInt - > playerID = = defendingHeroInstance - > tempOwner ) : false ;
spellToCast = ba ;
spellDestSelectMode = true ;
2014-11-14 03:12:50 +02:00
creatureCasting = false ;
2011-05-30 17:16:34 +03:00
2011-12-14 00:35:28 +03:00
//choosing possible tragets
const CGHeroInstance * castingHero = ( attackingHeroInstance - > tempOwner = = curInt - > playerID ) ? attackingHeroInstance : defendingHeroInstance ;
2013-11-07 15:48:41 +03:00
assert ( castingHero ) ; // code below assumes non-null hero
2015-04-11 08:24:51 +02:00
sp = spellID . toSpell ( ) ;
2011-12-14 00:35:28 +03:00
spellSelMode = ANY_LOCATION ;
2015-03-22 11:18:58 +02:00
2015-09-19 15:51:06 +02:00
const CSpell : : TargetInfo ti ( sp , castingHero - > getSpellSchoolLevel ( sp ) ) ;
2015-03-22 11:18:58 +02:00
2014-11-13 14:00:45 +02:00
if ( ti . massive | | ti . type = = CSpell : : NO_TARGET )
2015-03-22 11:18:58 +02:00
spellSelMode = NO_LOCATION ;
2014-11-13 14:00:45 +02:00
else if ( ti . type = = CSpell : : LOCATION & & ti . clearAffected )
{
2015-03-22 11:18:58 +02:00
spellSelMode = FREE_LOCATION ;
2014-11-13 14:00:45 +02:00
}
2014-03-17 16:11:10 +03:00
else if ( ti . type = = CSpell : : CREATURE )
2011-12-14 00:35:28 +03:00
{
2014-03-17 16:11:10 +03:00
if ( ti . smart )
2012-04-18 12:01:08 +03:00
spellSelMode = selectionTypeByPositiveness ( * sp ) ;
2009-01-15 19:01:08 +02:00
else
2014-03-17 16:11:10 +03:00
spellSelMode = ANY_CREATURE ;
2015-03-22 11:18:58 +02:00
}
2014-03-17 16:11:10 +03:00
else if ( ti . type = = CSpell : : OBSTACLE )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
spellSelMode = OBSTACLE ;
2015-03-22 11:18:58 +02:00
}
2012-05-18 23:50:16 +03:00
2012-04-16 20:12:39 +03:00
if ( spellSelMode = = NO_LOCATION ) //user does not have to select location
2011-12-14 00:35:28 +03:00
{
spellToCast - > destinationTile = - 1 ;
curInt - > cb - > battleMakeAction ( spellToCast ) ;
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
2013-07-10 13:45:51 +03:00
void CBattleInterface : : displayEffect ( ui32 effect , int destTile , bool areaEffect )
2011-12-14 00:35:28 +03:00
{
2014-11-27 16:39:37 +02:00
//todo: recheck areaEffect usage
addNewAnim ( new CSpellEffectAnimation ( this , effect , destTile , 0 , 0 , false ) ) ;
2011-12-14 00:35:28 +03:00
}
2011-01-14 20:08:01 +02:00
2015-03-22 15:47:20 +02:00
void CBattleInterface : : displaySpellAnimation ( const CSpell : : TAnimation & animation , BattleHex destinationTile , bool areaEffect )
2016-01-26 08:37:55 +02:00
{
2015-03-22 15:47:20 +02:00
if ( animation . pause > 0 )
{
2016-01-26 08:37:55 +02:00
addNewAnim ( new CDummyAnimation ( this , animation . pause ) ) ;
2015-03-22 15:47:20 +02:00
}
else
{
2016-01-26 08:37:55 +02:00
addNewAnim ( new CSpellEffectAnimation ( this , animation . resourceName , destinationTile , false , animation . verticalPosition = = VerticalPosition : : BOTTOM ) ) ;
}
2015-03-22 15:47:20 +02:00
}
void CBattleInterface : : displaySpellCast ( SpellID spellID , BattleHex destinationTile , bool areaEffect )
{
const CSpell * spell = spellID . toSpell ( ) ;
if ( spell = = nullptr )
return ;
2016-01-26 08:37:55 +02:00
2015-03-22 15:47:20 +02:00
for ( const CSpell : : TAnimation & animation : spell - > animationInfo . cast )
{
displaySpellAnimation ( animation , destinationTile , areaEffect ) ;
2016-01-26 08:37:55 +02:00
}
2015-03-22 15:47:20 +02:00
}
2014-11-27 23:36:14 +02:00
void CBattleInterface : : displaySpellEffect ( SpellID spellID , BattleHex destinationTile , bool areaEffect )
{
const CSpell * spell = spellID . toSpell ( ) ;
2015-03-22 11:18:58 +02:00
2014-11-27 23:36:14 +02:00
if ( spell = = nullptr )
return ;
for ( const CSpell : : TAnimation & animation : spell - > animationInfo . affect )
2015-03-22 11:18:58 +02:00
{
2015-03-22 15:47:20 +02:00
displaySpellAnimation ( animation , destinationTile , areaEffect ) ;
2014-11-27 23:36:14 +02:00
}
}
void CBattleInterface : : displaySpellHit ( SpellID spellID , BattleHex destinationTile , bool areaEffect )
{
const CSpell * spell = spellID . toSpell ( ) ;
2015-03-22 11:18:58 +02:00
2014-11-27 23:36:14 +02:00
if ( spell = = nullptr )
2015-03-22 11:18:58 +02:00
return ;
2014-11-27 23:36:14 +02:00
for ( const CSpell : : TAnimation & animation : spell - > animationInfo . hit )
2015-03-22 11:18:58 +02:00
{
2015-03-22 15:47:20 +02:00
displaySpellAnimation ( animation , destinationTile , areaEffect ) ;
2014-11-27 23:36:14 +02:00
}
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : battleTriggerEffect ( const BattleTriggerEffect & bte )
{
const CStack * stack = curInt - > cb - > battleGetStackByID ( bte . stackID ) ;
//don't show animation when no HP is regenerated
switch ( bte . effect )
2011-01-14 20:08:01 +02:00
{
2011-12-14 00:35:28 +03:00
case Bonus : : HP_REGENERATION :
2012-04-17 11:46:09 +03:00
displayEffect ( 74 , stack - > position ) ;
CCS - > soundh - > playSound ( soundBase : : REGENER ) ;
2011-12-14 00:35:28 +03:00
break ;
case Bonus : : MANA_DRAIN :
displayEffect ( 77 , stack - > position ) ;
CCS - > soundh - > playSound ( soundBase : : MANADRAI ) ;
break ;
case Bonus : : POISON :
displayEffect ( 67 , stack - > position ) ;
CCS - > soundh - > playSound ( soundBase : : POISON ) ;
break ;
case Bonus : : FEAR :
displayEffect ( 15 , stack - > position ) ;
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 ( ) ) ) ;
displayEffect ( 20 , stack - > position ) ;
2013-05-04 16:14:23 +03:00
CCS - > soundh - > playSound ( soundBase : : GOODMRLE ) ;
2012-01-26 19:48:53 +03:00
console - > addText ( hlp ) ;
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
{
2013-07-07 11:27:27 +03:00
return vstd : : round ( settings [ " battle " ] [ " animationSpeed " ] . Float ( ) * 100 ) ;
2013-07-06 19:10:20 +03:00
}
2013-11-03 19:44:47 +03:00
CPlayerInterface * CBattleInterface : : getCurrentPlayerInterface ( ) const
{
return curInt . get ( ) ;
}
2013-07-06 19:10:20 +03:00
void CBattleInterface : : setActiveStack ( const CStack * stack )
{
if ( activeStack ) // update UI
creAnims [ activeStack - > ID ] - > setBorderColor ( AnimationControls : : getNoBorder ( ) ) ;
activeStack = stack ;
if ( activeStack ) // update UI
creAnims [ activeStack - > ID ] - > setBorderColor ( AnimationControls : : getGoldBorder ( ) ) ;
2013-07-21 13:10:38 +03:00
blockUI ( activeStack = = nullptr ) ;
2013-07-06 19:10:20 +03:00
}
void CBattleInterface : : setHoveredStack ( const CStack * stack )
{
if ( mouseHoveredStack )
creAnims [ mouseHoveredStack - > ID ] - > setBorderColor ( AnimationControls : : getNoBorder ( ) ) ;
// stack must be alive and not active (which uses gold border instead)
if ( stack & & stack - > alive ( ) & & stack ! = activeStack )
{
mouseHoveredStack = stack ;
if ( mouseHoveredStack )
{
creAnims [ mouseHoveredStack - > ID ] - > setBorderColor ( AnimationControls : : getBlueBorder ( ) ) ;
if ( creAnims [ mouseHoveredStack - > ID ] - > framesInGroup ( CCreatureAnim : : MOUSEON ) > 0 )
creAnims [ mouseHoveredStack - > ID ] - > playOnce ( CCreatureAnim : : MOUSEON ) ;
}
}
else
mouseHoveredStack = nullptr ;
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
{
2013-11-02 23:07:45 +03:00
myTurn = true ;
if ( ! ! attackerInt & & defenderInt ) //hotseat -> need to pick which interface "takes over" as active
curInt = attackerInt - > playerID = = stackToActivate - > owner ? attackerInt : defenderInt ;
2013-07-06 19:10:20 +03:00
setActiveStack ( stackToActivate ) ;
2013-06-26 14:18:27 +03:00
stackToActivate = nullptr ;
2011-12-14 00:35:28 +03:00
const CStack * s = activeStack ;
2009-09-24 16:23:52 +03:00
2011-12-14 00:35:28 +03:00
queue - > update ( ) ;
redrawBackgroundWithHexes ( activeStack ) ;
2012-03-31 00:36:07 +03:00
2011-12-14 00:35:28 +03:00
//set casting flag to true if creature can use it to not check it every time
2012-09-20 19:55:21 +03:00
const Bonus * spellcaster = s - > getBonusLocalFirst ( Selector : : type ( Bonus : : SPELLCASTER ) ) ,
* randomSpellcaster = s - > getBonusLocalFirst ( Selector : : type ( Bonus : : RANDOM_SPELLCASTER ) ) ;
2012-03-31 00:36:07 +03:00
if ( s - > casts & & ( spellcaster | | randomSpellcaster ) )
2009-09-20 15:47:40 +03:00
{
2011-12-14 00:35:28 +03:00
stackCanCastSpell = true ;
2012-03-31 00:36:07 +03:00
if ( randomSpellcaster )
2013-01-22 12:47:25 +03:00
creatureSpellToCast = - 1 ; //spell will be set later on cast
creatureSpellToCast = curInt - > cb - > battleGetRandomStackSpell ( s , CBattleInfoCallback : : RANDOM_AIMED ) ; //faerie dragon can cast only one spell until their next move
//TODO: what if creature can cast BOTH random genie spell and aimed spell?
2011-12-14 00:35:28 +03:00
}
else
{
stackCanCastSpell = false ;
creatureSpellToCast = - 1 ;
2009-09-20 15:47:40 +03:00
}
2012-05-07 19:04:43 +03:00
getPossibleActionsForStack ( s ) ;
2009-09-24 16:23:52 +03:00
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
{
2011-12-14 00:35:28 +03:00
assert ( spellDestSelectMode ) ;
2011-08-25 18:24:37 +03:00
2015-03-18 23:10:41 +02:00
vstd : : clear_pointer ( spellToCast ) ;
2013-06-26 14:18:27 +03:00
sp = nullptr ;
2011-12-14 00:35:28 +03:00
spellDestSelectMode = false ;
2012-12-14 18:32:53 +03:00
CCS - > curh - > changeGraphic ( ECursor : : COMBAT , ECursor : : COMBAT_POINTER ) ;
2012-04-04 11:03:52 +03:00
2012-05-07 19:04:43 +03:00
if ( activeStack )
{
getPossibleActionsForStack ( activeStack ) ; //restore actions after they were cleared
myTurn = true ;
}
2012-04-04 11:03:52 +03:00
}
void CBattleInterface : : getPossibleActionsForStack ( const CStack * stack )
{
2012-04-18 12:01:08 +03:00
possibleActions . clear ( ) ;
2012-04-28 16:01:39 +03:00
if ( tacticsMode )
{
2014-10-02 18:43:46 +03:00
possibleActions . push_back ( MOVE_TACTICS ) ;
possibleActions . push_back ( CHOOSE_TACTICS_STACK ) ;
2012-04-28 16:01:39 +03:00
}
else
2012-04-04 11:03:52 +03:00
{
2012-04-28 16:01:39 +03:00
//first action will be prioritized over later ones
if ( stack - > casts ) //TODO: check for battlefield effects that prevent casting?
2012-04-04 11:03:52 +03:00
{
2012-04-28 16:01:39 +03:00
if ( stack - > hasBonusOfType ( Bonus : : SPELLCASTER ) )
2012-04-16 20:12:39 +03:00
{
2012-04-28 16:01:39 +03:00
//TODO: poll possible spells
const CSpell * spell ;
BonusList spellBonuses = * stack - > getBonuses ( Selector : : type ( Bonus : : SPELLCASTER ) ) ;
2013-06-29 16:05:48 +03:00
for ( Bonus * spellBonus : spellBonuses )
2012-04-17 11:46:09 +03:00
{
2014-03-07 16:21:09 +03:00
spell = CGI - > spellh - > objects [ spellBonus - > subtype ] ;
2013-07-27 22:01:31 +03:00
switch ( spellBonus - > subtype )
2012-04-17 11:46:09 +03:00
{
2013-07-27 22:01:31 +03:00
case SpellID : : REMOVE_OBSTACLE :
possibleActions . push_back ( OBSTACLE ) ;
break ;
default :
possibleActions . push_back ( selectionTypeByPositiveness ( * spell ) ) ;
break ;
2012-04-17 11:46:09 +03:00
}
2012-04-28 16:01:39 +03:00
}
std : : sort ( possibleActions . begin ( ) , possibleActions . end ( ) ) ;
auto it = std : : unique ( possibleActions . begin ( ) , possibleActions . end ( ) ) ;
possibleActions . erase ( it , possibleActions . end ( ) ) ;
2012-04-16 20:12:39 +03:00
}
2012-04-28 16:01:39 +03:00
if ( stack - > hasBonusOfType ( Bonus : : RANDOM_SPELLCASTER ) )
possibleActions . push_back ( RANDOM_GENIE_SPELL ) ;
if ( stack - > hasBonusOfType ( Bonus : : DAEMON_SUMMONING ) )
possibleActions . push_back ( RISE_DEMONS ) ;
2012-04-04 11:03:52 +03:00
}
2012-04-28 16:01:39 +03:00
if ( stack - > shots & & stack - > hasBonusOfType ( Bonus : : SHOOTER ) )
possibleActions . push_back ( SHOOT ) ;
if ( stack - > hasBonusOfType ( Bonus : : RETURN_AFTER_STRIKE ) )
possibleActions . push_back ( ATTACK_AND_RETURN ) ;
2012-04-04 11:03:52 +03:00
2012-04-28 16:01:39 +03:00
possibleActions . push_back ( ATTACK ) ; //all active stacks can attack
possibleActions . push_back ( WALK_AND_ATTACK ) ; //not all stacks can always walk, but we will check this elsewhere
2012-04-04 11:03:52 +03:00
2012-05-27 22:06:35 +03:00
if ( stack - > canMove ( ) & & stack - > Speed ( ) ) //probably no reason to try move war machines or bound stacks
2012-04-28 16:01:39 +03:00
possibleActions . push_back ( MOVE_STACK ) ; //all active stacks can attack
2012-04-04 11:03:52 +03:00
2012-04-28 16:01:39 +03:00
if ( siegeH & & stack - > hasBonusOfType ( Bonus : : CATAPULT ) ) //TODO: check shots
possibleActions . push_back ( CATAPULT ) ;
if ( stack - > hasBonusOfType ( Bonus : : HEALER ) )
possibleActions . push_back ( HEAL ) ;
}
2011-12-14 00:35:28 +03:00
}
2011-02-14 22:31:53 +02:00
2013-07-19 19:35:16 +03:00
void CBattleInterface : : printConsoleAttacked ( const CStack * defender , int dmg , int killed , const CStack * attacker , bool multiple )
2009-01-15 19:01:08 +02:00
{
2015-01-25 19:03:34 +02:00
std : : string formattedText ;
2011-12-14 00:35:28 +03:00
if ( attacker ) //ignore if stacks were killed by spell
2009-01-15 19:01:08 +02:00
{
2015-01-25 19:03:34 +02:00
boost : : format txt = boost : : format ( CGI - > generaltexth - > allTexts [ attacker - > count > 1 ? 377 : 376 ] ) %
2014-11-22 13:45:13 +02:00
( attacker - > count > 1 ? attacker - > getCreature ( ) - > namePl : attacker - > getCreature ( ) - > nameSing ) % dmg ;
2015-01-25 19:03:34 +02:00
formattedText . append ( boost : : to_string ( txt ) ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
if ( killed > 0 )
2009-01-15 19:01:08 +02:00
{
2015-01-26 18:16:39 +02:00
if ( attacker )
formattedText . append ( " " ) ;
2015-03-22 11:18:58 +02:00
2015-01-25 19:03:34 +02:00
boost : : format txt ;
2011-12-14 00:35:28 +03:00
if ( killed > 1 )
{
2014-11-22 13:45:13 +02:00
txt = boost : : format ( CGI - > generaltexth - > allTexts [ 379 ] ) % killed % ( multiple ? CGI - > generaltexth - > allTexts [ 43 ] : defender - > getCreature ( ) - > namePl ) ; // creatures perish
2011-12-14 00:35:28 +03:00
}
else //killed == 1
{
2014-11-22 13:45:13 +02:00
txt = boost : : format ( CGI - > generaltexth - > allTexts [ 378 ] ) % ( multiple ? CGI - > generaltexth - > allTexts [ 42 ] : defender - > getCreature ( ) - > nameSing ) ; // creature perishes
2011-12-14 00:35:28 +03:00
}
2015-01-25 19:03:34 +02:00
std : : string trimmed = boost : : to_string ( txt ) ;
boost : : algorithm : : trim ( trimmed ) ; // these default h3 texts have unnecessary new lines, so get rid of them before displaying
formattedText . append ( trimmed ) ;
2009-01-15 19:01:08 +02:00
}
2015-01-25 19:03:34 +02:00
console - > addText ( formattedText ) ;
2009-01-15 19:01:08 +02:00
}
2013-04-04 16:18:38 +03:00
2009-01-15 19:01:08 +02:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : endAction ( const BattleAction * action )
2012-02-16 20:10:58 +03:00
{
2012-04-03 02:23:14 +03:00
const CStack * stack = curInt - > cb - > battleGetStackByID ( action - > stackNumber ) ;
2013-05-19 18:14:23 +03:00
2013-02-04 00:05:44 +03:00
if ( action - > actionType = = Battle : : HERO_SPELL )
2009-04-03 18:55:26 +03:00
{
2011-12-14 00:35:28 +03:00
if ( action - > side )
defendingHero - > setPhase ( 0 ) ;
else
attackingHero - > setPhase ( 0 ) ;
2009-04-03 18:55:26 +03:00
}
2012-05-05 00:16:39 +03:00
2013-02-04 00:05:44 +03:00
if ( stack & & action - > actionType = = Battle : : WALK & &
2013-07-06 19:10:20 +03:00
! creAnims [ action - > stackNumber ] - > isIdle ( ) ) //walk or walk & attack
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
pendingAnims . push_back ( std : : make_pair ( new CMovementEndAnimation ( this , stack , action - > destinationTile ) , false ) ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
//check if we should reverse stacks
//for some strange reason, it's not enough
TStacks stacks = curInt - > cb - > battleGetStacks ( CBattleCallback : : MINE_AND_ENEMY ) ;
2013-06-29 16:05:48 +03:00
for ( const CStack * s : stacks )
2009-01-15 19:01:08 +02:00
{
2013-07-06 19:10:20 +03:00
if ( s & & creDir [ s - > ID ] ! = bool ( s - > attackerOwned ) & & s - > alive ( )
& & creAnims [ s - > ID ] - > isIdle ( ) )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
addNewAnim ( new CReverseAnimation ( this , s , s - > position , false ) ) ;
2009-01-15 19:01:08 +02:00
}
}
2011-12-14 00:35:28 +03:00
queue - > update ( ) ;
2012-04-03 02:23:14 +03:00
if ( tacticsMode ) //stack ended movement in tactics phase -> select the next one
2014-03-07 16:21:09 +03:00
bTacticNextStack ( stack ) ;
2012-04-03 02:23:14 +03:00
2013-02-04 00:05:44 +03:00
if ( action - > actionType = = Battle : : HERO_SPELL ) //we have activated next stack after sending request that has been just realized -> blockmap due to movement has changed
2011-12-14 00:35:28 +03:00
redrawBackgroundWithHexes ( activeStack ) ;
2013-05-19 18:14:23 +03:00
if ( activeStack & & ! animsAreDisplayed . get ( ) & & pendingAnims . empty ( ) & & ! active )
{
logGlobal - > warnStream ( ) < < " Something wrong... interface was deactivated but there is no animation. Reactivating... " ;
2013-07-21 13:10:38 +03:00
blockUI ( false ) ;
}
else
{
// block UI if no active stack (e.g. enemy turn);
blockUI ( activeStack = = nullptr ) ;
2013-05-19 18:14:23 +03:00
}
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 ( ) ;
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
2011-12-14 00:35:28 +03:00
if ( ! queue - > embedded )
{
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
}
2013-07-21 13:10:38 +03:00
void CBattleInterface : : blockUI ( bool on )
{
ESpellCastProblem : : ESpellCastProblem spellcastingProblem ;
bool canCastSpells = curInt - > cb - > battleCanCastSpell ( & spellcastingProblem ) ;
bool canWait = activeStack ? ! activeStack - > waited ( ) : false ;
bOptions - > block ( on ) ;
bFlee - > block ( on | | ! curInt - > cb - > battleCanFlee ( ) ) ;
bSurrender - > block ( on | | curInt - > cb - > battleGetSurrenderCost ( ) < 0 ) ;
2014-01-13 20:44:21 +03:00
// block only if during enemy turn and auto-fight is off
// othervice - crash on accessing non-exisiting active stack
2014-01-14 17:34:08 +03:00
bAutofight - > block ( ! curInt - > isAutoFightOn & & ! activeStack ) ;
2013-07-21 13:10:38 +03:00
if ( tacticsMode & & btactEnd & & btactNext )
{
btactNext - > block ( on ) ;
btactEnd - > block ( on ) ;
}
else
{
bConsoleUp - > block ( on ) ;
bConsoleDown - > block ( on ) ;
}
//if magic is blocked, we leave button active, so the message can be displayed (cf bug #97)
bSpell - > block ( on | | ( ! canCastSpells & & spellcastingProblem ! = ESpellCastProblem : : MAGIC_IS_BLOCKED ) ) ;
bWait - > block ( on | | tacticsMode | | ! canWait ) ;
bDefence - > block ( on | | tacticsMode ) ;
}
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);
2013-07-06 19:10:20 +03:00
setHoveredStack ( nullptr ) ;
2013-07-21 13:10:38 +03:00
blockUI ( true ) ;
2013-07-06 19:10:20 +03:00
2013-02-04 00:05:44 +03:00
if ( action - > actionType = = Battle : : END_TACTIC_PHASE )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
SDL_FreeSurface ( menu ) ;
menu = BitmapHandler : : loadBitmap ( " CBAR.bmp " ) ;
2009-01-15 19:01:08 +02:00
2011-12-14 00:35:28 +03:00
graphics - > blueToPlayersAdv ( menu , curInt - > playerID ) ;
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 ) ;
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
{
2013-02-04 00:05:44 +03:00
assert ( action - > actionType = = Battle : : HERO_SPELL ) ; //only cast spell is valid action without acting stack number
2009-01-15 19:01:08 +02:00
}
2013-02-04 00:05:44 +03:00
if ( action - > actionType = = Battle : : WALK
| | ( action - > actionType = = Battle : : WALK_AND_ATTACK & & action - > destinationTile ! = stack - > position ) )
2009-01-15 19:01:08 +02:00
{
2013-11-07 15:48:41 +03:00
assert ( stack ) ;
2011-12-14 00:35:28 +03:00
moveStarted = true ;
if ( creAnims [ action - > stackNumber ] - > framesInGroup ( CCreatureAnim : : MOVE_START ) )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
pendingAnims . push_back ( std : : make_pair ( new CMovementStartAnimation ( this , stack ) , false ) ) ;
2009-01-15 19:01:08 +02:00
}
}
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
2013-02-04 00:05:44 +03:00
if ( action - > actionType = = Battle : : HERO_SPELL ) //when hero casts spell
2011-12-14 00:35:28 +03:00
{
if ( action - > side )
defendingHero - > setPhase ( 4 ) ;
else
attackingHero - > setPhase ( 4 ) ;
return ;
2010-08-30 02:12:34 +03:00
}
2011-12-14 00:35:28 +03:00
if ( ! stack )
2010-08-30 02:12:34 +03:00
{
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < " Something wrong with stackNumber in actionStarted. Stack number: " < < 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 )
{
2013-02-04 00:05:44 +03:00
case Battle : : WAIT :
2011-12-14 00:35:28 +03:00
txtid = 136 ;
break ;
2013-02-04 00:05:44 +03:00
case Battle : : BAD_MORALE :
2011-12-14 00:35:28 +03:00
txtid = - 34 ; //negative -> no separate singular/plural form
displayEffect ( 30 , stack - > position ) ;
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
2011-12-14 00:35:28 +03:00
if ( txtid > 0 & & stack - > count ! = 1 )
txtid + + ; //move to plural text
else if ( txtid < 0 )
txtid = - txtid ;
2009-06-28 19:02:39 +03:00
2011-12-14 00:35:28 +03:00
if ( txtid )
{
2013-11-06 16:42:58 +03:00
std : : string name = ( stack - > count ! = 1 ) ? stack - > getCreature ( ) - > namePl . c_str ( ) : stack - > getCreature ( ) - > nameSing . c_str ( ) ;
console - > addText ( ( boost : : format ( CGI - > generaltexth - > allTexts [ txtid ] . c_str ( ) ) % name ) . str ( ) ) ;
2011-12-14 00:35:28 +03:00
}
2009-01-15 19:01:08 +02:00
2011-12-14 00:35:28 +03:00
//displaying special abilities
switch ( action - > actionType )
2011-01-08 21:38:42 +02:00
{
2013-02-04 00:05:44 +03:00
case Battle : : STACK_HEAL :
2011-12-14 00:35:28 +03:00
displayEffect ( 74 , action - > destinationTile ) ;
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 ( )
{
2012-02-20 00:03:43 +03:00
auto unlockPim = vstd : : makeUnlockGuard ( * LOCPLINT - > pim ) ;
2011-12-14 00:35:28 +03:00
animsAreDisplayed . waitWhileTrue ( ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : bEndTacticPhase ( )
2009-01-15 19:01:08 +02:00
{
2013-07-06 19:10:20 +03:00
setActiveStack ( nullptr ) ;
2013-07-21 13:10:38 +03:00
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
}
2013-06-26 14:18:27 +03:00
void CBattleInterface : : bTacticNextStack ( const CStack * current /*= nullptr*/ )
2009-01-15 19:01:08 +02:00
{
2012-04-03 02:23:14 +03:00
if ( ! current )
current = activeStack ;
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-01-26 08:37:55 +02:00
vstd : : erase_if ( stacksOfMine , & immobile ) ;
2013-06-29 16:05:48 +03:00
auto it = vstd : : find ( stacksOfMine , current ) ;
2011-12-14 00:35:28 +03:00
if ( it ! = stacksOfMine . end ( ) & & + + it ! = stacksOfMine . end ( ) )
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-04-16 20:12:39 +03:00
CBattleInterface : : PossibleActions CBattleInterface : : selectionTypeByPositiveness ( const CSpell & spell )
2012-02-17 00:19:07 +03:00
{
switch ( spell . positiveness )
{
case CSpell : : NEGATIVE :
2012-04-17 11:46:09 +03:00
return HOSTILE_CREATURE_SPELL ;
2012-02-17 00:19:07 +03:00
case CSpell : : NEUTRAL :
return ANY_CREATURE ;
case CSpell : : POSITIVE :
2012-04-17 11:46:09 +03:00
return FRIENDLY_CREATURE_SPELL ;
2012-02-17 00:19:07 +03:00
}
2012-02-21 17:08:42 +03:00
assert ( 0 ) ;
return NO_LOCATION ; //should never happen
2012-02-17 00:19:07 +03:00
}
2012-03-31 00:36:07 +03:00
std : : string formatDmgRange ( std : : pair < ui32 , ui32 > dmgRange )
{
if ( dmgRange . first ! = dmgRange . second )
return ( boost : : format ( " %d - %d " ) % dmgRange . first % dmgRange . second ) . str ( ) ;
else
return ( boost : : format ( " %d " ) % dmgRange . first ) . str ( ) ;
}
2012-04-18 12:01:08 +03:00
bool CBattleInterface : : canStackMoveHere ( const CStack * activeStack , BattleHex myNumber )
{
std : : vector < BattleHex > acc = curInt - > cb - > battleGetAvailableHexes ( activeStack , false ) ;
int shiftedDest = myNumber + ( activeStack - > attackerOwned ? 1 : - 1 ) ;
if ( vstd : : contains ( acc , myNumber ) )
return true ;
else if ( activeStack - > doubleWide ( ) & & vstd : : contains ( acc , shiftedDest ) )
return true ;
else
return false ;
}
2012-03-31 00:36:07 +03:00
void CBattleInterface : : handleHex ( BattleHex myNumber , int eventType )
{
if ( ! myTurn ) //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 ;
const CStack * const sactive = activeStack ;
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.
const CStack * shere = curInt - > cb - > battleGetStackByPos ( myNumber , true ) ;
if ( ! shere )
shere = curInt - > cb - > battleGetStackByPos ( myNumber , false ) ;
2012-03-31 00:36:07 +03:00
2012-04-16 20:12:39 +03:00
if ( ! sactive )
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
2013-07-06 19:10:20 +03:00
//stack changed, update selection border
if ( shere ! = mouseHoveredStack )
{
setHoveredStack ( shere ) ;
}
2012-04-04 11:03:52 +03:00
localActions . clear ( ) ;
2012-04-18 18:57:49 +03:00
illegalActions . clear ( ) ;
2013-06-29 16:05:48 +03:00
for ( PossibleActions 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
{
2012-04-16 20:12:39 +03:00
case CHOOSE_TACTICS_STACK :
if ( shere & & ourStack )
legalAction = true ;
break ;
2012-04-28 16:01:39 +03:00
case MOVE_TACTICS :
2012-04-16 20:12:39 +03:00
case MOVE_STACK :
2012-08-14 09:31:14 +03:00
{
if ( ! ( shere & & shere - > alive ( ) ) ) //we can walk on dead stacks
{
if ( canStackMoveHere ( sactive , myNumber ) )
legalAction = true ;
}
2012-04-16 20:12:39 +03:00
break ;
2012-08-14 09:31:14 +03:00
}
2012-04-16 20:12:39 +03:00
case ATTACK :
case WALK_AND_ATTACK :
case ATTACK_AND_RETURN :
2012-03-31 00:36:07 +03:00
{
2015-04-09 21:49:11 +02:00
if ( curInt - > cb - > battleCanAttack ( sactive , shere , myNumber ) )
2012-04-17 17:50:23 +03:00
{
2015-04-09 21:49:11 +02:00
if ( isTileAttackable ( myNumber ) ) // move isTileAttackable to be part of battleCanAttack?
2012-04-18 12:01:08 +03:00
{
setBattleCursor ( myNumber ) ; // temporary - needed for following function :(
BattleHex attackFromHex = 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 ;
case SHOOT :
if ( curInt - > cb - > battleCanShoot ( activeStack , myNumber ) )
legalAction = true ;
break ;
2012-05-07 19:04:43 +03:00
case ANY_LOCATION :
if ( myNumber > - 1 ) //TODO: this should be checked for all actions
{
creatureCasting = stackCanCastSpell & & ! spellDestSelectMode ; //as isCastingPossibleHere is not called
legalAction = true ;
}
break ;
2015-04-11 09:55:03 +02:00
case ANY_CREATURE :
if ( shere & & shere - > alive ( ) & & isCastingPossibleHere ( sactive , shere , myNumber ) )
legalAction = true ;
2016-01-26 08:37:55 +02:00
break ;
2012-04-28 18:38:34 +03:00
case HOSTILE_CREATURE_SPELL :
2012-04-18 18:57:49 +03:00
if ( shere & & shere - > alive ( ) & & ! ourStack & & isCastingPossibleHere ( sactive , shere , myNumber ) )
2012-04-16 20:12:39 +03:00
legalAction = true ;
break ;
2012-04-17 11:46:09 +03:00
case FRIENDLY_CREATURE_SPELL :
2013-07-27 22:01:31 +03:00
{
2013-07-28 17:49:50 +03:00
if ( isCastingPossibleHere ( sactive , shere , myNumber ) ) //need to be called before sp is determined
2013-07-27 22:01:31 +03:00
{
bool rise = false ; //TODO: can you imagine rising hostile creatures?
2014-03-07 16:21:09 +03:00
sp = CGI - > spellh - > objects [ creatureCasting ? creatureSpellToCast : spellToCast - > additionalInfo ] ;
2013-07-27 22:01:31 +03:00
if ( sp & & sp - > isRisingSpell ( ) )
rise = true ;
if ( shere & & ( shere - > alive ( ) | | rise ) & & ourStack )
legalAction = true ;
}
2012-04-16 20:12:39 +03:00
break ;
2013-07-27 22:01:31 +03:00
}
2012-04-16 20:12:39 +03:00
case RANDOM_GENIE_SPELL :
2012-03-31 00:36:07 +03:00
{
2012-04-28 16:01:39 +03:00
if ( shere & & ourStack & & shere ! = sactive ) //only positive spells for other allied creatures
2012-04-16 20:12:39 +03:00
{
int spellID = curInt - > cb - > battleGetRandomStackSpell ( shere , CBattleInfoCallback : : RANDOM_GENIE ) ;
if ( spellID > - 1 )
{
legalAction = true ;
}
}
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
break ;
case OBSTACLE :
2012-04-28 18:18:21 +03:00
if ( isCastingPossibleHere ( sactive , shere , myNumber ) )
2012-04-28 18:38:34 +03:00
legalAction = true ;
2012-04-16 20:12:39 +03:00
break ;
case TELEPORT :
2012-03-31 00:36:07 +03:00
{
2012-04-18 12:01:08 +03:00
ui8 skill = 0 ;
if ( creatureCasting )
2015-09-17 07:42:30 +02:00
skill = sactive - > 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
2012-04-28 22:40:27 +03:00
if ( curInt - > cb - > battleCanTeleportTo ( selectedStack , 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 ;
2012-04-28 22:40:27 +03:00
case SACRIFICE : //choose our living stack to sacrifice
if ( shere & & shere ! = selectedStack & & ourStack & & shere - > alive ( ) )
legalAction = true ;
else
notLegal = true ;
2012-04-16 20:12:39 +03:00
break ;
2012-05-18 23:50:16 +03:00
case FREE_LOCATION :
{
ui8 side = curInt - > cb - > battleGetMySide ( ) ;
2013-01-20 23:29:35 +03:00
auto hero = curInt - > cb - > battleGetMyHero ( ) ;
2012-05-18 23:50:16 +03:00
assert ( ! creatureCasting ) ; //we assume hero casts this spell
assert ( hero ) ;
legalAction = true ;
bool hexesOutsideBattlefield = false ;
auto tilesThatMustBeClear = sp - > rangeInHexes ( myNumber , hero - > getSpellSchoolLevel ( sp ) , side , & hexesOutsideBattlefield ) ;
2013-06-29 16:05:48 +03:00
for ( BattleHex hex : tilesThatMustBeClear )
2012-05-18 23:50:16 +03:00
{
2015-03-29 15:24:43 +02:00
if ( curInt - > cb - > battleGetStackByPos ( hex , true ) | | ! ! curInt - > cb - > battleGetObstacleOnPos ( hex , false )
2012-05-18 23:50:16 +03:00
| | ! hex . isAvailable ( ) )
{
legalAction = false ;
notLegal = true ;
}
}
if ( hexesOutsideBattlefield )
{
legalAction = false ;
notLegal = true ;
}
}
break ;
2012-04-16 20:12:39 +03:00
case CATAPULT :
if ( isCatapultAttackable ( myNumber ) )
legalAction = true ;
break ;
case HEAL :
if ( shere & & ourStack & & shere - > canBeHealed ( ) )
legalAction = true ;
break ;
case RISE_DEMONS :
if ( shere & & ourStack & & ! shere - > alive ( ) )
2015-11-07 10:35:02 +02:00
legalAction = true ;
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 ) ;
2012-04-18 16:24:18 +03:00
else if ( notLegal )
2012-04-16 20:12:39 +03:00
illegalActions . push_back ( action ) ;
}
2012-04-18 16:24:18 +03:00
illegalAction = 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
{
currentAction = 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
{
currentAction = CREATURE_INFO ;
}
2012-04-16 20:12:39 +03:00
else
illegalAction = 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
2012-04-16 20:12:39 +03:00
if ( currentAction > 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
{
2012-04-16 20:12:39 +03:00
case CHOOSE_TACTICS_STACK :
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 481 ] ) % shere - > getName ( ) ) . str ( ) ; //Select %s
realizeAction = [ = ] { stackActivated ( shere ) ; } ;
break ;
2012-04-28 16:01:39 +03:00
case MOVE_TACTICS :
2012-04-17 17:50:23 +03:00
case MOVE_STACK :
2012-04-28 16:01:39 +03:00
if ( activeStack - > hasBonusOfType ( Bonus : : FLYING ) )
2012-04-16 20:12:39 +03:00
{
cursorFrame = ECursor : : COMBAT_FLY ;
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 295 ] ) % activeStack - > getName ( ) ) . str ( ) ; //Fly %s here
}
else
{
cursorFrame = ECursor : : COMBAT_MOVE ;
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 294 ] ) % activeStack - > getName ( ) ) . str ( ) ; //Move %s here
}
2012-03-31 00:36:07 +03:00
2012-04-16 20:12:39 +03:00
realizeAction = [ = ]
2012-03-31 00:36:07 +03:00
{
2012-04-28 16:01:39 +03:00
if ( activeStack - > doubleWide ( ) )
2012-03-31 00:36:07 +03:00
{
2012-04-16 20:12:39 +03:00
std : : vector < BattleHex > acc = curInt - > cb - > battleGetAvailableHexes ( activeStack , false ) ;
int shiftedDest = myNumber + ( activeStack - > attackerOwned ? 1 : - 1 ) ;
if ( vstd : : contains ( acc , myNumber ) )
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : WALK , myNumber , activeStack - > ID ) ;
2012-04-16 20:12:39 +03:00
else if ( vstd : : contains ( acc , shiftedDest ) )
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : WALK , shiftedDest , activeStack - > ID ) ;
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
{
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : WALK , myNumber , activeStack - > ID ) ;
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
} ;
break ;
case ATTACK :
case WALK_AND_ATTACK :
case ATTACK_AND_RETURN : //TODO: allow to disable return
2012-03-31 00:36:07 +03:00
{
setBattleCursor ( myNumber ) ; //handle direction of cursor and attackable tile
2012-04-17 17:50:23 +03:00
setCursor = false ; //don't overwrite settings from the call above //TODO: what does it mean?
2012-03-31 00:36:07 +03:00
realizeAction = [ = ]
{
BattleHex attackFromHex = fromWhichHexAttack ( myNumber ) ;
2012-04-28 16:01:39 +03:00
if ( attackFromHex > = 0 ) //we can be in this line when unreachable creature is L - clicked (as of revision 1308)
2012-03-31 00:36:07 +03:00
{
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : WALK_AND_ATTACK , attackFromHex , activeStack - > ID , myNumber ) ;
2012-03-31 00:36:07 +03:00
}
} ;
std : : string estDmgText = formatDmgRange ( curInt - > cb - > battleEstimateDamage ( sactive , shere ) ) ; //calculating estimated dmg
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 36 ] ) % shere - > getName ( ) % estDmgText ) . str ( ) ; //Attack %s (%s damage)
}
2012-04-16 20:12:39 +03:00
break ;
case SHOOT :
2012-03-31 00:36:07 +03:00
{
2012-04-16 20:12:39 +03:00
if ( curInt - > cb - > battleHasShootingPenalty ( activeStack , myNumber ) )
cursorFrame = ECursor : : COMBAT_SHOOT_PENALTY ;
else
cursorFrame = ECursor : : COMBAT_SHOOT ;
2013-02-04 00:05:44 +03:00
realizeAction = [ = ] { giveCommand ( Battle : : SHOOT , myNumber , activeStack - > ID ) ; } ;
2012-04-16 20:12:39 +03:00
std : : string estDmgText = formatDmgRange ( curInt - > cb - > battleEstimateDamage ( sactive , shere ) ) ; //calculating estimated dmg
//printing - Shoot %s (%d shots left, %s damage)
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 296 ] ) % shere - > getName ( ) % sactive - > shots % estDmgText ) . str ( ) ;
2012-03-31 00:36:07 +03:00
}
2012-04-16 20:12:39 +03:00
break ;
2012-04-17 11:46:09 +03:00
case HOSTILE_CREATURE_SPELL :
case FRIENDLY_CREATURE_SPELL :
2015-04-11 09:55:03 +02:00
case ANY_CREATURE :
2014-03-07 16:21:09 +03:00
sp = CGI - > spellh - > objects [ creatureCasting ? creatureSpellToCast : spellToCast - > additionalInfo ] ; //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 :
2013-07-25 14:53:36 +03:00
selectedStack = 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 ;
2012-05-07 19:04:43 +03:00
case ANY_LOCATION :
2014-03-07 16:21:09 +03:00
sp = CGI - > spellh - > objects [ creatureCasting ? creatureSpellToCast : spellToCast - > additionalInfo ] ; //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 ;
2014-03-23 15:59:03 +03:00
case 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 ;
case TELEPORT :
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 ;
case OBSTACLE :
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 ;
2012-04-28 22:40:27 +03:00
case SACRIFICE :
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
spellToCast - > selectedStack = shere - > ID ; //sacrificed creature is selected
isCastingPossible = true ;
break ;
2012-05-18 23:50:16 +03:00
case FREE_LOCATION :
//cursorFrame = ECursor::SPELLBOOK;
consoleMsg = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 26 ] ) % sp - > name ) ; //Cast %s
isCastingPossible = true ;
break ;
2012-04-16 20:12:39 +03:00
case HEAL :
cursorFrame = ECursor : : COMBAT_HEAL ;
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 419 ] ) % shere - > getName ( ) ) . str ( ) ; //Apply first aid to the %s
2013-02-04 00:05:44 +03:00
realizeAction = [ = ] { giveCommand ( Battle : : STACK_HEAL , myNumber , activeStack - > ID ) ; } ; //command healing
2012-04-16 20:12:39 +03:00
break ;
case RISE_DEMONS :
cursorType = ECursor : : SPELLBOOK ;
2015-11-07 10:35:02 +02:00
realizeAction = [ = ] { giveCommand ( Battle : : DAEMON_SUMMONING , myNumber , activeStack - > ID ) ; } ;
2012-04-16 20:12:39 +03:00
break ;
case CATAPULT :
cursorFrame = ECursor : : COMBAT_SHOOT_CATAPULT ;
2013-02-04 00:05:44 +03:00
realizeAction = [ = ] { giveCommand ( Battle : : CATAPULT , myNumber , activeStack - > ID ) ; } ;
2012-04-18 16:24:18 +03:00
break ;
2012-04-17 11:46:09 +03:00
case CREATURE_INFO :
{
cursorFrame = ECursor : : COMBAT_QUERY ;
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 297 ] ) % shere - > getName ( ) ) . str ( ) ;
2014-06-26 22:07:33 +03:00
realizeAction = [ = ] { GH . pushInt ( new 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
{
2015-04-11 09:55:03 +02:00
case ANY_CREATURE :
2012-04-17 11:46:09 +03:00
case HOSTILE_CREATURE_SPELL :
case FRIENDLY_CREATURE_SPELL :
2012-04-16 20:12:39 +03:00
case RANDOM_GENIE_SPELL :
cursorFrame = ECursor : : COMBAT_BLOCKED ;
consoleMsg = CGI - > generaltexth - > allTexts [ 23 ] ;
break ;
case 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 ;
2012-04-28 22:40:27 +03:00
case SACRIFICE :
consoleMsg = CGI - > generaltexth - > allTexts [ 543 ] ; //choose army to sacrifice
break ;
2012-05-18 23:50:16 +03:00
case FREE_LOCATION :
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
{
case TELEPORT : //FIXME: more generic solution?
case SACRIFICE :
break ;
default :
cursorType = ECursor : : SPELLBOOK ;
cursorFrame = 0 ;
if ( consoleMsg . empty ( ) & & sp )
consoleMsg = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 26 ] ) % sp - > name ) ; //Cast %s
break ;
}
2014-03-07 16:21:09 +03:00
2012-04-16 20:12:39 +03:00
realizeAction = [ = ]
2012-03-31 00:36:07 +03:00
{
2012-04-18 16:24:18 +03:00
if ( secondaryTarget ) //select that target now
2012-03-31 00:36:07 +03: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
2012-04-18 16:24:18 +03:00
possibleActions . push_back ( TELEPORT ) ;
2012-04-28 22:40:27 +03:00
spellToCast - > selectedStack = selectedStack - > ID ;
2012-04-18 16:24:18 +03:00
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : SACRIFICE :
2012-04-18 16:24:18 +03:00
possibleActions . push_back ( SACRIFICE ) ;
break ;
}
2012-04-18 12:01:08 +03:00
}
else
{
2012-04-18 16:24:18 +03:00
if ( creatureCasting )
{
2012-04-18 18:57:49 +03:00
if ( sp )
{
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : MONSTER_SPELL , myNumber , sactive - > ID , creatureSpellToCast ) ;
2012-04-18 18:57:49 +03:00
}
else //unknown random spell
{
2013-02-04 00:05:44 +03:00
giveCommand ( Battle : : MONSTER_SPELL , myNumber , sactive - > ID , curInt - > cb - > battleGetRandomStackSpell ( shere , CBattleInfoCallback : : RANDOM_GENIE ) ) ;
2012-04-18 18:57:49 +03:00
}
2012-04-18 16:24:18 +03:00
}
else
{
2012-06-20 16:03:05 +03:00
assert ( sp ) ;
2013-02-11 02:24:57 +03:00
switch ( sp - > id . toEnum ( ) )
2012-04-28 22:40:27 +03:00
{
2013-02-11 02:24:57 +03:00
case SpellID : : SACRIFICE :
2012-04-28 22:40:27 +03:00
spellToCast - > destinationTile = selectedStack - > position ; //cast on first creature that will be resurrected
break ;
default :
spellToCast - > destinationTile = myNumber ;
break ;
}
2012-04-18 16:24:18 +03:00
curInt - > cb - > battleMakeAction ( spellToCast ) ;
endCastingSpell ( ) ;
}
2013-06-26 14:18:27 +03:00
selectedStack = 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
}
2013-07-25 14:53:36 +03:00
//helper lambda that appropriately realizes action / sets cursor and tooltip
auto realizeThingsToDo = [ & ] ( )
{
if ( eventType = = MOVE )
{
if ( setCursor )
CCS - > curh - > changeGraphic ( cursorType , cursorFrame ) ;
this - > console - > alterText ( consoleMsg ) ;
this - > console - > whoSetAlter = 0 ;
}
if ( eventType = = LCLICK & & realizeAction )
{
2014-03-07 16:21:09 +03:00
//opening creature window shouldn't affect myTurn...
2013-07-25 14:53:36 +03:00
if ( ( currentAction ! = CREATURE_INFO ) & & ! secondaryTarget )
{
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 ) ;
2013-07-25 14:53:36 +03:00
this - > console - > alterText ( " " ) ;
}
} ;
2012-03-31 00:36:07 +03:00
realizeThingsToDo ( ) ;
2012-04-16 20:12:39 +03:00
}
bool CBattleInterface : : isCastingPossibleHere ( const CStack * sactive , const CStack * shere , BattleHex myNumber )
{
2012-04-22 16:28:46 +03:00
creatureCasting = stackCanCastSpell & & ! 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 )
{
2012-04-18 18:57:49 +03:00
if ( creatureSpellToCast > - 1 & & ( shere ! = sactive ) ) //can't cast on itself
2012-04-18 12:01:08 +03:00
spellID = creatureSpellToCast ; //TODO: merge with SpellTocast?
2012-04-16 20:12:39 +03:00
}
2012-04-18 18:57:49 +03:00
else //hero casting
2012-04-16 20:12:39 +03:00
spellID = spellToCast - > additionalInfo ;
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
{
2015-09-28 15:06:26 +02:00
const ISpellCaster * caster = creatureCasting ? dynamic_cast < const ISpellCaster * > ( sactive ) : dynamic_cast < const ISpellCaster * > ( curInt - > cb - > battleGetMyHero ( ) ) ;
if ( caster = = nullptr )
{
isCastingPossible = false ; //just in case
}
2012-04-16 20:12:39 +03:00
else
2015-09-28 15:06:26 +02:00
{
const ECastingMode : : ECastingMode mode = creatureCasting ? ECastingMode : : CREATURE_ACTIVE_CASTING : ECastingMode : : HERO_CASTING ;
isCastingPossible = ( curInt - > cb - > battleCanCastThisSpellHere ( caster , sp , mode , myNumber ) = = ESpellCastProblem : : OK ) ;
}
2012-04-16 20:12:39 +03:00
}
2015-09-16 16:35:49 +02:00
else
isCastingPossible = false ;
2012-04-16 20:12:39 +03:00
if ( ! myNumber . isAvailable ( ) & & ! shere ) //empty tile outside battlefield (or in the unavailable border column)
isCastingPossible = false ;
return isCastingPossible ;
2012-03-31 00:36:07 +03:00
}
BattleHex CBattleInterface : : fromWhichHexAttack ( BattleHex myNumber )
{
//TODO far too much repeating code
BattleHex destHex = - 1 ;
2012-12-14 18:32:53 +03:00
switch ( CCS - > curh - > frame )
2012-03-31 00:36:07 +03:00
{
case 12 : //from bottom right
{
bool doubleWide = activeStack - > doubleWide ( ) ;
destHex = myNumber + ( ( myNumber / GameConstants : : BFIELD_WIDTH ) % 2 ? GameConstants : : BFIELD_WIDTH : GameConstants : : BFIELD_WIDTH + 1 ) +
( activeStack - > attackerOwned & & doubleWide ? 1 : 0 ) ;
if ( vstd : : contains ( occupyableHexes , destHex ) )
return destHex ;
else if ( activeStack - > attackerOwned ) //if we are attacker
{
if ( vstd : : contains ( occupyableHexes , destHex + 1 ) )
return destHex + 1 ;
}
else //if we are defender
{
if ( vstd : : contains ( occupyableHexes , destHex - 1 ) )
return destHex - 1 ;
}
break ;
}
case 7 : //from bottom left
{
destHex = myNumber + ( ( myNumber / GameConstants : : BFIELD_WIDTH ) % 2 ? GameConstants : : BFIELD_WIDTH - 1 : GameConstants : : BFIELD_WIDTH ) ;
if ( vstd : : contains ( occupyableHexes , destHex ) )
return destHex ;
else if ( activeStack - > attackerOwned ) //if we are attacker
{
if ( vstd : : contains ( occupyableHexes , destHex + 1 ) )
return destHex + 1 ;
}
else //if we are defender
{
if ( vstd : : contains ( occupyableHexes , destHex - 1 ) )
return destHex - 1 ;
}
break ;
}
case 8 : //from left
{
if ( activeStack - > doubleWide ( ) & & ! activeStack - > attackerOwned )
{
std : : vector < BattleHex > acc = curInt - > cb - > battleGetAvailableHexes ( activeStack , false ) ;
if ( vstd : : contains ( acc , myNumber ) )
return myNumber - 1 ;
else
return myNumber - 2 ;
}
else
{
return myNumber - 1 ;
}
break ;
}
case 9 : //from top left
{
destHex = myNumber - ( ( myNumber / GameConstants : : BFIELD_WIDTH ) % 2 ? GameConstants : : BFIELD_WIDTH + 1 : GameConstants : : BFIELD_WIDTH ) ;
if ( vstd : : contains ( occupyableHexes , destHex ) )
return destHex ;
else if ( activeStack - > attackerOwned ) //if we are attacker
{
if ( vstd : : contains ( occupyableHexes , destHex + 1 ) )
return destHex + 1 ;
}
else //if we are defender
{
if ( vstd : : contains ( occupyableHexes , destHex - 1 ) )
return destHex - 1 ;
}
break ;
}
case 10 : //from top right
{
bool doubleWide = activeStack - > doubleWide ( ) ;
destHex = myNumber - ( ( myNumber / GameConstants : : BFIELD_WIDTH ) % 2 ? GameConstants : : BFIELD_WIDTH : GameConstants : : BFIELD_WIDTH - 1 ) +
( activeStack - > attackerOwned & & doubleWide ? 1 : 0 ) ;
if ( vstd : : contains ( occupyableHexes , destHex ) )
return destHex ;
else if ( activeStack - > attackerOwned ) //if we are attacker
{
if ( vstd : : contains ( occupyableHexes , destHex + 1 ) )
return destHex + 1 ;
}
else //if we are defender
{
if ( vstd : : contains ( occupyableHexes , destHex - 1 ) )
return destHex - 1 ;
}
break ;
}
case 11 : //from right
{
if ( activeStack - > doubleWide ( ) & & activeStack - > attackerOwned )
{
std : : vector < BattleHex > acc = curInt - > cb - > battleGetAvailableHexes ( activeStack , false ) ;
if ( vstd : : contains ( acc , myNumber ) )
return myNumber + 1 ;
else
return myNumber + 2 ;
}
else
{
return myNumber + 1 ;
}
break ;
}
case 13 : //from bottom
{
destHex = myNumber + ( ( myNumber / GameConstants : : BFIELD_WIDTH ) % 2 ? GameConstants : : BFIELD_WIDTH : GameConstants : : BFIELD_WIDTH + 1 ) ;
if ( vstd : : contains ( occupyableHexes , destHex ) )
return destHex ;
else if ( attackingHeroInstance - > tempOwner = = curInt - > cb - > getMyColor ( ) ) //if we are attacker
{
if ( vstd : : contains ( occupyableHexes , destHex + 1 ) )
return destHex + 1 ;
}
else //if we are defender
{
if ( vstd : : contains ( occupyableHexes , destHex - 1 ) )
return destHex - 1 ;
}
break ;
}
case 14 : //from top
{
destHex = myNumber - ( ( myNumber / GameConstants : : BFIELD_WIDTH ) % 2 ? GameConstants : : BFIELD_WIDTH : GameConstants : : BFIELD_WIDTH - 1 ) ;
if ( vstd : : contains ( occupyableHexes , destHex ) )
return destHex ;
else if ( attackingHeroInstance - > tempOwner = = curInt - > cb - > getMyColor ( ) ) //if we are attacker
{
if ( vstd : : contains ( occupyableHexes , destHex + 1 ) )
return destHex + 1 ;
}
else //if we are defender
{
if ( vstd : : contains ( occupyableHexes , destHex - 1 ) )
return destHex - 1 ;
}
break ;
}
}
2012-04-08 13:34:23 +03:00
return - 1 ;
2012-03-31 00:36:07 +03:00
}
2012-04-23 22:56:37 +03:00
Rect CBattleInterface : : hexPosition ( BattleHex hex ) const
{
int x = 14 + ( ( hex . getY ( ) ) % 2 = = 0 ? 22 : 0 ) + 44 * hex . getX ( ) + pos . x ;
int y = 86 + 42 * hex . getY ( ) + pos . y ;
int w = cellShade - > w ;
int h = cellShade - > h ;
return Rect ( x , y , w , h ) ;
}
2012-05-05 00:16:39 +03:00
void CBattleInterface : : obstaclePlaced ( const CObstacleInstance & oi )
{
//so when multiple obstacles are added, they show up one after another
waitForAnims ( ) ;
int effectID = - 1 ;
2012-05-19 14:47:26 +03:00
soundBase : : soundID sound = soundBase : : invalid ; //FIXME: variable set but unused. Missing soundh->playSound()?
2012-05-18 23:50:16 +03:00
std : : string defname ;
2012-05-05 00:16:39 +03:00
switch ( oi . obstacleType )
{
case CObstacleInstance : : QUICKSAND :
effectID = 55 ;
2012-05-18 23:50:16 +03:00
sound = soundBase : : QUIKSAND ;
2012-05-05 00:16:39 +03:00
break ;
case CObstacleInstance : : LAND_MINE :
effectID = 47 ;
2012-05-18 23:50:16 +03:00
sound = soundBase : : LANDMINE ;
break ;
case CObstacleInstance : : FORCE_FIELD :
{
auto & spellObstacle = dynamic_cast < const SpellCreatedObstacle & > ( oi ) ;
if ( spellObstacle . casterSide )
{
if ( oi . getAffectedTiles ( ) . size ( ) < 3 )
defname = " C15SPE0.DEF " ; //TODO cannot find def for 2-hex force field \ appearing
else
defname = " C15SPE6.DEF " ;
}
else
{
if ( oi . getAffectedTiles ( ) . size ( ) < 3 )
defname = " C15SPE0.DEF " ;
else
defname = " C15SPE9.DEF " ;
}
}
sound = soundBase : : FORCEFLD ;
break ;
case CObstacleInstance : : FIRE_WALL :
if ( oi . getAffectedTiles ( ) . size ( ) < 3 )
effectID = 43 ; //small fire wall appearing
else
effectID = 44 ; //and the big one
sound = soundBase : : fireWall ;
2012-05-05 00:16:39 +03:00
break ;
default :
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < " I don't know how to animate appearing obstacle of type " < < ( int ) oi . obstacleType ;
2012-05-05 00:16:39 +03:00
return ;
}
if ( graphics - > battleACToDef [ effectID ] . empty ( ) )
{
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < " Cannot find def for effect type " < < effectID ;
2012-05-05 00:16:39 +03:00
return ;
}
2012-05-18 23:50:16 +03:00
if ( defname . empty ( ) & & effectID > = 0 )
defname = graphics - > battleACToDef [ effectID ] . front ( ) ;
2012-05-05 00:16:39 +03:00
2012-05-18 23:50:16 +03:00
assert ( ! defname . empty ( ) ) ;
2012-05-05 00:16:39 +03:00
//we assume here that effect graphics have the same size as the usual obstacle image
// -> if we know how to blit obstacle, let's blit the effect in the same place
2013-07-19 19:35:16 +03:00
Point whereTo = getObstaclePosition ( getObstacleImage ( oi ) , oi ) ;
2012-05-05 00:16:39 +03:00
addNewAnim ( new CSpellEffectAnimation ( this , defname , whereTo . x , whereTo . y ) ) ;
2012-05-18 23:50:16 +03:00
//TODO we need to wait after playing sound till it's finished, otherwise it overlaps and sounds really bad
//CCS->soundh->playSound(sound);
2012-05-05 00:16:39 +03:00
}
2016-02-13 16:40:31 +02:00
void CBattleInterface : : gateStateChanged ( const EGateState state )
2016-02-10 06:10:32 +02:00
{
2016-02-13 16:40:31 +02:00
auto oldState = curInt - > cb - > battleGetGateState ( ) ;
2016-02-10 06:10:32 +02:00
bool playSound = false ;
int stateId = EWallState : : NONE ;
switch ( state )
{
2016-02-13 16:40:31 +02:00
case EGateState : : CLOSED :
if ( oldState ! = EGateState : : BLOCKED )
2016-02-10 06:10:32 +02:00
playSound = true ;
break ;
2016-02-13 16:40:31 +02:00
case EGateState : : BLOCKED :
if ( oldState ! = EGateState : : CLOSED )
2016-02-10 06:10:32 +02:00
playSound = true ;
break ;
2016-02-13 16:40:31 +02:00
case EGateState : : OPENED :
2016-02-10 06:10:32 +02:00
playSound = true ;
stateId = EWallState : : DAMAGED ;
break ;
2016-02-13 16:40:31 +02:00
case EGateState : : DESTROYED :
2016-02-10 06:10:32 +02:00
stateId = EWallState : : DESTROYED ;
break ;
}
SDL_FreeSurface ( siegeH - > walls [ 9 ] ) ;
siegeH - > walls [ 9 ] = nullptr ;
if ( stateId ! = EWallState : : NONE )
siegeH - > walls [ 9 ] = BitmapHandler : : loadBitmap ( siegeH - > getSiegeName ( 9 , stateId ) ) ;
if ( playSound )
CCS - > soundh - > playSound ( soundBase : : DRAWBRG ) ;
}
2012-08-26 12:07:48 +03:00
const CGHeroInstance * CBattleInterface : : currentHero ( ) const
{
if ( attackingHeroInstance - > tempOwner = = curInt - > playerID )
return attackingHeroInstance ;
else
return defendingHeroInstance ;
}
InfoAboutHero CBattleInterface : : enemyHero ( ) const
{
InfoAboutHero ret ;
if ( attackingHeroInstance - > tempOwner = = curInt - > playerID )
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
2014-03-07 16:21:09 +03:00
boost : : thread aiThread ( [ & ]
2013-06-23 00:47:51 +03:00
{
2013-11-09 19:25:20 +03:00
auto ba = new BattleAction ( curInt - > autofightingAI - > activeStack ( activeStack ) ) ;
2013-06-23 00:47:51 +03:00
2013-06-23 14:25:48 +03:00
if ( curInt - > isAutoFightOn )
2013-06-23 00:47:51 +03:00
{
2013-11-09 19:25:20 +03:00
if ( tacticsMode )
{
// 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?
setActiveStack ( nullptr ) ;
blockUI ( true ) ;
tacticsMode = false ;
}
else
{
givenCommand - > setn ( ba ) ;
}
2013-06-23 00:47:51 +03:00
}
2013-06-23 14:25:48 +03:00
else
{
2013-06-24 17:35:27 +03:00
delete ba ;
2013-06-23 14:25:48 +03:00
boost : : unique_lock < boost : : recursive_mutex > un ( * LOCPLINT - > pim ) ;
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
}
2009-08-26 17:09:55 +03:00
CBattleInterface : : SiegeHelper : : SiegeHelper ( const CGTownInstance * siegeTown , const CBattleInterface * _owner )
2016-02-13 16:10:14 +02:00
: owner ( _owner ) , town ( siegeTown )
2009-08-26 17:09:55 +03:00
{
2011-12-14 00:35:28 +03:00
for ( int g = 0 ; g < ARRAY_COUNT ( walls ) ; + + g )
2009-08-26 17:09:55 +03:00
{
2016-02-13 16:40:31 +02:00
//gate have no displayed bitmap when drawbridge is raised
2016-02-13 16:10:14 +02:00
if ( g = = SiegeHelper : : GATE )
2016-02-10 06:10:32 +02:00
walls [ g ] = nullptr ;
else
2016-02-13 16:10:14 +02:00
walls [ g ] = BitmapHandler : : loadBitmap ( getSiegeName ( g ) ) ;
2009-08-26 17:09:55 +03:00
}
}
CBattleInterface : : SiegeHelper : : ~ SiegeHelper ( )
{
2013-06-29 16:05:48 +03:00
for ( auto & elem : walls )
2009-08-26 17:09:55 +03:00
{
2013-06-29 16:05:48 +03:00
SDL_FreeSurface ( elem ) ;
2009-08-26 17:09:55 +03:00
}
}
2013-08-06 14:20:28 +03:00
std : : string CBattleInterface : : SiegeHelper : : getSiegeName ( ui16 what ) const
2009-08-26 17:09:55 +03:00
{
2013-08-06 14:20:28 +03:00
return getSiegeName ( what , EWallState : : INTACT ) ;
}
std : : string CBattleInterface : : SiegeHelper : : getSiegeName ( ui16 what , int state ) const
{
auto getImageIndex = [ & ] ( ) - > int
{
switch ( state )
{
case EWallState : : INTACT : return 1 ;
case EWallState : : DAMAGED : return 2 ;
case EWallState : : DESTROYED :
if ( what = = 2 | | what = = 3 | | what = = 8 ) // towers don't have separate image here
return 2 ;
else
return 3 ;
}
return 1 ;
} ;
2012-10-05 21:03:49 +03:00
2013-09-07 00:57:16 +03:00
const std : : string & prefix = town - > town - > clientInfo . siegePrefix ;
2013-08-06 14:20:28 +03:00
std : : string addit = boost : : lexical_cast < std : : string > ( getImageIndex ( ) ) ;
2012-10-05 21:03:49 +03:00
2009-08-26 17:09:55 +03:00
switch ( what )
{
2016-02-13 16:10:14 +02:00
case SiegeHelper : : BACKGROUND :
2012-10-05 21:03:49 +03:00
return prefix + " BACK.BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : BACKGROUND_WALL :
2009-08-28 12:03:58 +03:00
{
2013-04-21 15:49:26 +03:00
switch ( town - > town - > faction - > index )
2009-08-28 12:03:58 +03:00
{
2016-02-13 16:10:14 +02:00
case ETownType : : RAMPART :
case ETownType : : NECROPOLIS :
case ETownType : : DUNGEON :
case ETownType : : STRONGHOLD :
2012-10-05 21:03:49 +03:00
return prefix + " TPW1.BMP " ;
2009-08-28 12:03:58 +03:00
default :
2012-12-13 16:07:56 +03:00
return prefix + " TPWL.BMP " ;
2009-08-28 12:03:58 +03:00
}
}
2016-02-13 16:10:14 +02:00
case SiegeHelper : : KEEP :
2012-10-05 21:03:49 +03:00
return prefix + " MAN " + addit + " .BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : BOTTOM_TOWER :
2012-10-05 21:03:49 +03:00
return prefix + " TW1 " + addit + " .BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : BOTTOM_WALL :
2012-10-05 21:03:49 +03:00
return prefix + " WA1 " + addit + " .BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : WALL_BELLOW_GATE :
2012-10-05 21:03:49 +03:00
return prefix + " WA3 " + addit + " .BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : WALL_OVER_GATE :
2012-10-05 21:03:49 +03:00
return prefix + " WA4 " + addit + " .BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : UPPER_WALL :
2012-10-05 21:03:49 +03:00
return prefix + " WA6 " + addit + " .BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : UPPER_TOWER :
2012-10-05 21:03:49 +03:00
return prefix + " TW2 " + addit + " .BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : GATE :
2012-10-05 21:03:49 +03:00
return prefix + " DRW " + addit + " .BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : GATE_ARCH :
2012-10-05 21:03:49 +03:00
return prefix + " ARCH.BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : BOTTOM_STATIC_WALL :
2012-10-05 21:03:49 +03:00
return prefix + " WA2.BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : UPPER_STATIC_WALL :
2012-10-05 21:03:49 +03:00
return prefix + " WA5.BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : MOAT :
2012-10-05 21:03:49 +03:00
return prefix + " MOAT.BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : BACKGROUND_MOAT :
2012-10-05 21:03:49 +03:00
return prefix + " MLIP.BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : KEEP_BATTLEMENT :
2012-10-05 21:03:49 +03:00
return prefix + " MANC.BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : BOTTOM_BATTLEMENT :
2012-10-05 21:03:49 +03:00
return prefix + " TW1C.BMP " ;
2016-02-13 16:10:14 +02:00
case SiegeHelper : : UPPER_BATTLEMENT :
2012-10-05 21:03:49 +03:00
return prefix + " TW2C.BMP " ;
2009-08-26 17:09:55 +03:00
default :
return " " ;
}
}
void CBattleInterface : : SiegeHelper : : printPartOfWall ( SDL_Surface * to , int what )
2009-08-24 15:55:05 +03:00
{
2011-12-22 16:05:19 +03:00
Point pos = Point ( - 1 , - 1 ) ;
2013-08-01 01:24:44 +03:00
auto & ci = owner - > siegeH - > town - > town - > clientInfo ;
2012-02-16 20:10:58 +03:00
2016-02-13 16:10:14 +02:00
if ( vstd : : iswithin ( what , 1 , 17 ) )
2009-08-26 17:09:55 +03:00
{
2013-08-01 01:24:44 +03:00
pos . x = ci . siegePositions [ what ] . x + owner - > pos . x ;
pos . y = ci . siegePositions [ what ] . y + owner - > pos . y ;
2011-04-30 22:52:35 +03:00
}
2009-08-26 17:09:55 +03:00
2013-08-01 01:24:44 +03:00
if ( town - > town - > faction - > index = = ETownType : : TOWER
2016-02-13 16:10:14 +02:00
& & ( what = = SiegeHelper : : MOAT | | what = = SiegeHelper : : BACKGROUND_MOAT ) )
2013-08-01 01:24:44 +03:00
return ; // no moat in Tower. TODO: remove hardcode somehow?
2009-08-26 17:09:55 +03:00
if ( pos . x ! = - 1 )
{
2016-02-10 06:10:32 +02:00
if ( walls [ what ] )
blitAt ( walls [ what ] , pos . x , pos . y , to ) ;
2009-08-26 17:09:55 +03:00
}
2009-08-24 15:55:05 +03:00
}
2009-09-20 15:47:40 +03:00
2013-04-04 21:20:25 +03:00
CatapultProjectileInfo : : CatapultProjectileInfo ( Point from , Point dest )
{
facA = 0.005 ; // seems to be constant
// system of 2 linear equations, solutions of which are missing coefficients
// for quadratic equation a*x*x + b*x + c
double eq [ 2 ] [ 3 ] = {
{ static_cast < double > ( from . x ) , 1.0 , from . y - facA * from . x * from . x } ,
{ static_cast < double > ( dest . x ) , 1.0 , dest . y - facA * dest . x * dest . x }
} ;
// solve system via determinants
double det = eq [ 0 ] [ 0 ] * eq [ 1 ] [ 1 ] - eq [ 1 ] [ 0 ] * eq [ 0 ] [ 1 ] ;
double detB = eq [ 0 ] [ 2 ] * eq [ 1 ] [ 1 ] - eq [ 1 ] [ 2 ] * eq [ 0 ] [ 1 ] ;
double detC = eq [ 0 ] [ 0 ] * eq [ 1 ] [ 2 ] - eq [ 1 ] [ 0 ] * eq [ 0 ] [ 2 ] ;
facB = detB / det ;
facC = detC / det ;
// make sure that parabola is correct e.g. passes through from and dest
assert ( fabs ( calculateY ( from . x ) - from . y ) < 1.0 ) ;
assert ( fabs ( calculateY ( dest . x ) - dest . y ) < 1.0 ) ;
}
2011-12-22 16:05:19 +03:00
double CatapultProjectileInfo : : calculateY ( double x )
2011-05-30 17:16:34 +03:00
{
2013-04-04 21:20:25 +03:00
return facA * pow ( x , 2.0 ) + facB * x + facC ;
2011-05-30 17:16:34 +03:00
}
2013-07-19 19:35:16 +03:00
void CBattleInterface : : showAll ( SDL_Surface * to )
{
show ( to ) ;
}
void CBattleInterface : : show ( SDL_Surface * to )
{
assert ( to ) ;
SDL_Rect buf ;
SDL_GetClipRect ( to , & buf ) ;
SDL_SetClipRect ( to , & pos ) ;
+ + animCount ;
showBackground ( to ) ;
showBattlefieldObjects ( to ) ;
showProjectiles ( to ) ;
updateBattleAnimations ( ) ;
SDL_SetClipRect ( to , & buf ) ; //restoring previous clip_rect
showInterface ( to ) ;
//activation of next stack
2013-11-03 15:51:25 +03:00
if ( pendingAnims . empty ( ) & & stackToActivate ! = nullptr )
2013-07-19 19:35:16 +03:00
{
activateStack ( ) ;
//we may have changed active interface (another side in hot-seat),
// so we can't continue drawing with old setting.
show ( to ) ;
}
}
void CBattleInterface : : showBackground ( SDL_Surface * to )
{
if ( activeStack ! = nullptr & & creAnims [ activeStack - > ID ] - > isIdle ( ) ) //show everything with range
{
// FIXME: any *real* reason to keep this separate? Speed difference can't be that big
blitAt ( backgroundWithHexes , pos . x , pos . y , to ) ;
}
else
{
showBackgroundImage ( to ) ;
showAbsoluteObstacles ( to ) ;
}
showHighlightedHexes ( to ) ;
}
void CBattleInterface : : showBackgroundImage ( SDL_Surface * to )
{
blitAt ( background , pos . x , pos . y , to ) ;
if ( settings [ " battle " ] [ " cellBorders " ] . Bool ( ) )
{
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellBorders , nullptr , to , & pos ) ;
}
}
void CBattleInterface : : showAbsoluteObstacles ( SDL_Surface * to )
{
//Blit absolute obstacles
for ( auto & oi : curInt - > cb - > battleGetAllObstacles ( ) )
if ( oi - > obstacleType = = CObstacleInstance : : ABSOLUTE_OBSTACLE )
blitAt ( getObstacleImage ( * oi ) , pos . x + oi - > getInfo ( ) . width , pos . y + oi - > getInfo ( ) . height , to ) ;
2013-08-01 01:24:44 +03:00
if ( siegeH & & siegeH - > town - > hasBuilt ( BuildingID : : CITADEL ) )
2016-02-13 16:10:14 +02:00
siegeH - > printPartOfWall ( to , SiegeHelper : : BACKGROUND_MOAT ) ;
2013-07-19 19:35:16 +03:00
}
void CBattleInterface : : showHighlightedHexes ( SDL_Surface * to )
{
for ( int b = 0 ; b < GameConstants : : BFIELD_SIZE ; + + b )
{
if ( bfield [ b ] - > strictHovered & & bfield [ b ] - > hovered )
{
if ( previouslyHoveredHex = = - 1 )
previouslyHoveredHex = b ; //something to start with
if ( currentlyHoveredHex = = - 1 )
currentlyHoveredHex = b ; //something to start with
if ( currentlyHoveredHex ! = b ) //repair hover info
{
previouslyHoveredHex = currentlyHoveredHex ;
currentlyHoveredHex = b ;
}
if ( settings [ " battle " ] [ " mouseShadow " ] . Bool ( ) )
{
if ( spellToCast ) //when casting spell
{
//calculating spell school level
2014-03-07 16:21:09 +03:00
const CSpell & spToCast = * CGI - > spellh - > objects [ spellToCast - > additionalInfo ] ;
2013-07-19 19:35:16 +03:00
ui8 schoolLevel = 0 ;
auto caster = activeStack - > attackerOwned ? attackingHeroInstance : defendingHeroInstance ;
if ( caster )
schoolLevel = caster - > getSpellSchoolLevel ( & spToCast ) ;
// printing shaded hex(es)
auto shaded = spToCast . rangeInHexes ( currentlyHoveredHex , schoolLevel , curInt - > cb - > battleGetMySide ( ) ) ;
for ( BattleHex shadedHex : shaded )
{
if ( ( shadedHex . getX ( ) ! = 0 ) & & ( shadedHex . getX ( ) ! = GameConstants : : BFIELD_WIDTH - 1 ) )
showHighlightedHex ( to , shadedHex ) ;
}
}
else if ( active ) //always highlight pointed hex
{
if ( currentlyHoveredHex . getX ( ) ! = 0
& & currentlyHoveredHex . getX ( ) ! = GameConstants : : BFIELD_WIDTH - 1 )
showHighlightedHex ( to , currentlyHoveredHex ) ;
}
}
}
}
if ( activeStack & & settings [ " battle " ] [ " stackRange " ] . Bool ( ) )
{
std : : set < BattleHex > set = curInt - > cb - > battleGetAttackedHexes ( activeStack , currentlyHoveredHex , attackingHex ) ;
for ( BattleHex hex : set )
showHighlightedHex ( to , hex ) ;
// display the movement shadow of the stack at b (i.e. stack under mouse)
const CStack * const shere = curInt - > cb - > battleGetStackByPos ( currentlyHoveredHex , false ) ;
if ( shere & & shere ! = activeStack & & shere - > alive ( ) )
{
std : : vector < BattleHex > v = curInt - > cb - > battleGetAvailableHexes ( shere , true ) ;
for ( BattleHex hex : v )
showHighlightedHex ( to , hex ) ;
}
}
}
void CBattleInterface : : showHighlightedHex ( SDL_Surface * to , BattleHex hex )
{
int x = 14 + ( hex . getY ( ) % 2 = = 0 ? 22 : 0 ) + 44 * ( hex . getX ( ) ) + pos . x ;
int y = 86 + 42 * hex . getY ( ) + pos . y ;
SDL_Rect temp_rect = genRect ( cellShade - > h , cellShade - > w , x , y ) ;
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellShade , nullptr , to , & temp_rect ) ;
}
void CBattleInterface : : showProjectiles ( SDL_Surface * to )
{
assert ( to ) ;
std : : list < std : : list < ProjectileInfo > : : iterator > toBeDeleted ;
for ( auto it = projectiles . begin ( ) ; it ! = projectiles . end ( ) ; + + it )
{
// Check if projectile is already visible (shooter animation did the shot)
if ( ! it - > shotDone )
{
2013-07-21 13:10:38 +03:00
// frame we're waiting for is reached OR animation has already finished
if ( creAnims [ it - > stackID ] - > getCurrentFrame ( ) > = it - > animStartDelay | |
creAnims [ it - > stackID ] - > isShooting ( ) = = false )
2013-07-19 19:35:16 +03:00
{
//at this point projectile should become visible
creAnims [ it - > stackID ] - > pause ( ) ; // pause animation
it - > shotDone = true ;
}
else
continue ; // wait...
}
SDL_Surface * image = idToProjectile [ it - > creID ] - > ourImages [ it - > frameNum ] . bitmap ;
SDL_Rect dst ;
dst . h = image - > h ;
dst . w = image - > w ;
dst . x = it - > x - dst . w / 2 ;
dst . y = it - > y - dst . h / 2 ;
if ( it - > reverse )
{
2013-11-06 16:42:58 +03:00
SDL_Surface * rev = CSDL_Ext : : verticalFlip ( image ) ;
2013-07-19 19:35:16 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( rev , nullptr , to , & dst ) ;
SDL_FreeSurface ( rev ) ;
}
else
{
CSDL_Ext : : blit8bppAlphaTo24bpp ( image , nullptr , to , & dst ) ;
}
// Update projectile
+ + it - > step ;
if ( it - > step = = it - > lastStep )
{
toBeDeleted . insert ( toBeDeleted . end ( ) , it ) ;
}
else
{
if ( it - > catapultInfo )
{
// Parabolic shot of the trajectory, as follows: f(x) = ax^2 + bx + c
it - > x + = it - > dx ;
it - > y = it - > catapultInfo - > calculateY ( it - > x ) ;
+ + ( it - > frameNum ) ;
it - > frameNum % = idToProjectile [ it - > creID ] - > ourImages . size ( ) ;
}
else
{
// Normal projectile, just add the calculated "deltas" to the x and y positions.
it - > x + = it - > dx ;
it - > y + = it - > dy ;
}
}
}
for ( auto & elem : toBeDeleted )
{
// resume animation
creAnims [ elem - > stackID ] - > play ( ) ;
projectiles . erase ( elem ) ;
}
}
void CBattleInterface : : showBattlefieldObjects ( SDL_Surface * to )
{
auto showHexEntry = [ & ] ( BattleObjectsByHex : : HexData & hex )
{
showPiecesOfWall ( to , hex . walls ) ;
showObstacles ( to , hex . obstacles ) ;
showAliveStacks ( to , hex . alive ) ;
showBattleEffects ( to , hex . effects ) ;
} ;
BattleObjectsByHex objects = sortObjectsByHex ( ) ;
2013-07-21 13:10:38 +03:00
// dead stacks should be blit first
showStacks ( to , objects . beforeAll . dead ) ;
for ( auto & data : objects . hex )
showStacks ( to , data . dead ) ;
showStacks ( to , objects . afterAll . dead ) ;
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
if ( attackingHero )
attackingHero - > show ( to ) ;
if ( defendingHero )
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
for ( auto & data : objects . hex )
showHexEntry ( data ) ;
// objects that must be blit *after* everything else - e.g. bottom tower or some spell effects
showHexEntry ( objects . afterAll ) ;
}
void CBattleInterface : : showAliveStacks ( SDL_Surface * to , std : : vector < const CStack * > stacks )
{
auto isAmountBoxVisible = [ & ] ( const CStack * stack ) - > bool
{
if ( stack - > hasBonusOfType ( Bonus : : SIEGE_WEAPON ) ) // siege weapons are always singular
return false ;
if ( curInt - > curAction )
{
if ( curInt - > curAction - > stackNumber = = stack - > ID ) // stack is currently taking an action
return false ;
if ( curInt - > curAction - > actionType = = Battle : : WALK_AND_ATTACK & & // stack is being targeted
stack - > position = = curInt - > curAction - > additionalInfo )
return false ;
if ( curInt - > curAction - > destinationTile = = stack - > position ) // stack is moving
return false ;
}
return true ;
} ;
auto getEffectsPositivness = [ & ] ( const CStack * stack ) - > int
{
int pos = 0 ;
for ( auto & spellId : stack - > activeSpells ( ) )
{
2014-03-07 16:21:09 +03:00
pos + = CGI - > spellh - > objects [ spellId ] - > positiveness ;
2013-07-19 19:35:16 +03:00
}
return pos ;
} ;
auto getAmountBoxBackground = [ & ] ( int positivness ) - > SDL_Surface *
{
if ( positivness > 0 )
return amountPositive ;
if ( positivness < 0 )
return amountNegative ;
return amountEffNeutral ;
} ;
showStacks ( to , stacks ) ; // Actual display of all stacks
for ( auto & stack : stacks )
{
assert ( stack ) ;
//printing amount
if ( isAmountBoxVisible ( stack ) )
{
const BattleHex nextPos = stack - > position + ( stack - > attackerOwned ? 1 : - 1 ) ;
const bool edge = stack - > position % GameConstants : : BFIELD_WIDTH = = ( stack - > attackerOwned ? GameConstants : : BFIELD_WIDTH - 2 : 1 ) ;
const bool moveInside = ! edge & & ! stackCountOutsideHexes [ nextPos ] ;
int xAdd = ( stack - > attackerOwned ? 220 : 202 ) +
( stack - > doubleWide ( ) ? 44 : 0 ) * ( stack - > attackerOwned ? + 1 : - 1 ) +
( moveInside ? amountNormal - > w + 10 : 0 ) * ( stack - > attackerOwned ? - 1 : + 1 ) ;
int yAdd = 260 + ( ( stack - > attackerOwned | | moveInside ) ? 0 : - 15 ) ;
//blitting amount background box
SDL_Surface * amountBG = amountNormal ;
if ( ! stack - > getSpellBonuses ( ) - > empty ( ) )
amountBG = getAmountBoxBackground ( getEffectsPositivness ( stack ) ) ;
SDL_Rect temp_rect = genRect ( amountBG - > h , amountBG - > w , creAnims [ stack - > ID ] - > pos . x + xAdd , creAnims [ stack - > ID ] - > pos . y + yAdd ) ;
SDL_BlitSurface ( amountBG , nullptr , to , & temp_rect ) ;
//blitting amount
Point textPos ( creAnims [ stack - > ID ] - > pos . x + xAdd + amountNormal - > w / 2 ,
creAnims [ stack - > ID ] - > pos . y + yAdd + amountNormal - > h / 2 ) ;
graphics - > fonts [ FONT_TINY ] - > renderTextCenter ( to , makeNumberShort ( stack - > count ) , Colors : : WHITE , textPos ) ;
}
}
}
void CBattleInterface : : showStacks ( SDL_Surface * to , std : : vector < const CStack * > stacks )
{
for ( const CStack * stack : stacks )
{
creAnims [ stack - > ID ] - > nextFrame ( to , creDir [ stack - > ID ] ) ; // do actual blit
creAnims [ stack - > ID ] - > incrementFrame ( float ( GH . mainFPSmng - > getElapsedMilliseconds ( ) ) / 1000 ) ;
}
}
2015-12-29 04:43:33 +02:00
void CBattleInterface : : showObstacles ( SDL_Surface * to , std : : vector < std : : shared_ptr < const CObstacleInstance > > & obstacles )
2013-07-19 19:35:16 +03:00
{
for ( auto & obstacle : obstacles )
{
SDL_Surface * toBlit = getObstacleImage ( * obstacle ) ;
Point p = getObstaclePosition ( toBlit , * obstacle ) ;
blitAt ( toBlit , p . x , p . y , to ) ;
}
}
void CBattleInterface : : showBattleEffects ( SDL_Surface * to , const std : : vector < const BattleEffect * > & battleEffects )
{
for ( auto & elem : battleEffects )
{
int currentFrame = floor ( elem - > currentFrame ) ;
currentFrame % = elem - > anim - > ourImages . size ( ) ;
SDL_Surface * bitmapToBlit = elem - > anim - > ourImages [ currentFrame ] . bitmap ;
SDL_Rect temp_rect = genRect ( bitmapToBlit - > h , bitmapToBlit - > w , elem - > x , elem - > y ) ;
SDL_BlitSurface ( bitmapToBlit , nullptr , to , & temp_rect ) ;
}
}
void CBattleInterface : : showInterface ( SDL_Surface * to )
{
blitAt ( menu , pos . x , 556 + pos . y , to ) ;
if ( tacticsMode )
{
btactNext - > showAll ( to ) ;
btactEnd - > showAll ( to ) ;
}
else
{
console - > showAll ( to ) ;
bConsoleUp - > showAll ( to ) ;
bConsoleDown - > showAll ( to ) ;
}
//showing buttons
bOptions - > showAll ( to ) ;
bSurrender - > showAll ( to ) ;
bFlee - > showAll ( to ) ;
bAutofight - > showAll ( to ) ;
bSpell - > showAll ( to ) ;
bWait - > showAll ( to ) ;
bDefence - > showAll ( to ) ;
//showing in-game console
LOCPLINT - > cingconsole - > show ( to ) ;
Rect posWithQueue = Rect ( pos . x , pos . y , 800 , 600 ) ;
if ( settings [ " battle " ] [ " showQueue " ] . Bool ( ) )
{
if ( ! queue - > embedded )
{
posWithQueue . y - = queue - > pos . h ;
posWithQueue . h + = queue - > pos . h ;
}
//showing queue
if ( ! bresult )
queue - > showAll ( to ) ;
else
queue - > blitBg ( to ) ;
}
//printing border around interface
if ( screen - > w ! = 800 | | screen - > h ! = 600 )
{
CMessage : : drawBorder ( curInt - > playerID , to , posWithQueue . w + 28 , posWithQueue . h + 28 , posWithQueue . x - 14 , posWithQueue . y - 15 ) ;
}
}
BattleObjectsByHex CBattleInterface : : sortObjectsByHex ( )
{
auto getCurrentPosition = [ & ] ( const CStack * stack ) - > BattleHex
{
for ( auto & anim : pendingAnims )
{
// certainly ugly workaround but fixes quite annoying bug
// stack position will be updated only *after* movement is finished
// before this - stack is always at its initial position. Thus we need to find
// its current position. Which can be found only in this class
if ( CMovementAnimation * move = dynamic_cast < CMovementAnimation * > ( anim . first ) )
{
if ( move - > stack = = stack )
return move - > nextHex ;
}
}
return stack - > position ;
} ;
BattleObjectsByHex sorted ;
// Sort creatures
for ( auto & stack : curInt - > cb - > battleGetAllStacks ( ) )
{
if ( creAnims . find ( stack - > ID ) = = creAnims . end ( ) ) //e.g. for summoned but not yet handled stacks
continue ;
if ( stack - > position < 0 ) // turret shooters are handled separately
continue ;
if ( ! creAnims [ stack - > ID ] - > isDead ( ) )
{
2013-07-21 13:10:38 +03:00
if ( ! creAnims [ stack - > ID ] - > isMoving ( ) )
2013-07-19 19:35:16 +03:00
sorted . hex [ stack - > position ] . alive . push_back ( stack ) ;
else
{
// flying creature - just blit them over everyone else
if ( stack - > hasBonusOfType ( Bonus : : FLYING ) )
sorted . afterAll . alive . push_back ( stack ) ;
else //try to find current location
sorted . hex [ getCurrentPosition ( stack ) ] . alive . push_back ( stack ) ;
}
}
else
sorted . hex [ stack - > position ] . dead . push_back ( stack ) ;
}
// Sort battle effects (spells)
for ( auto & battleEffect : battleEffects )
{
if ( battleEffect . position . isValid ( ) )
sorted . hex [ battleEffect . position ] . effects . push_back ( & battleEffect ) ;
else
sorted . afterAll . effects . push_back ( & battleEffect ) ;
}
// Sort obstacles
for ( auto & obstacle : curInt - > cb - > battleGetAllObstacles ( ) )
{
if ( obstacle - > obstacleType ! = CObstacleInstance : : ABSOLUTE_OBSTACLE
& & obstacle - > obstacleType ! = CObstacleInstance : : MOAT )
{
sorted . hex [ obstacle - > pos ] . obstacles . push_back ( obstacle ) ;
}
}
// Sort wall parts
if ( siegeH )
{
2016-02-13 16:10:14 +02:00
sorted . beforeAll . walls . push_back ( SiegeHelper : : BACKGROUND_WALL ) ;
sorted . hex [ 135 ] . walls . push_back ( SiegeHelper : : KEEP ) ;
sorted . afterAll . walls . push_back ( SiegeHelper : : BOTTOM_TOWER ) ;
sorted . hex [ 182 ] . walls . push_back ( SiegeHelper : : BOTTOM_WALL ) ;
sorted . hex [ 130 ] . walls . push_back ( SiegeHelper : : WALL_BELLOW_GATE ) ;
2016-02-14 13:38:24 +02:00
sorted . hex [ 78 ] . walls . push_back ( SiegeHelper : : WALL_OVER_GATE ) ;
2016-02-13 16:10:14 +02:00
sorted . hex [ 12 ] . walls . push_back ( SiegeHelper : : UPPER_WALL ) ;
sorted . beforeAll . walls . push_back ( SiegeHelper : : UPPER_TOWER ) ;
sorted . hex [ 94 ] . walls . push_back ( SiegeHelper : : GATE ) ;
sorted . hex [ 112 ] . walls . push_back ( SiegeHelper : : GATE_ARCH ) ;
sorted . hex [ 165 ] . walls . push_back ( SiegeHelper : : BOTTOM_STATIC_WALL ) ;
sorted . hex [ 45 ] . walls . push_back ( SiegeHelper : : UPPER_STATIC_WALL ) ;
2013-10-27 16:05:01 +03:00
2013-08-01 01:24:44 +03:00
if ( siegeH & & siegeH - > town - > hasBuilt ( BuildingID : : CITADEL ) )
{
2016-02-13 16:10:14 +02:00
sorted . beforeAll . walls . push_back ( SiegeHelper : : MOAT ) ;
//sorted.beforeAll.walls.push_back(SiegeHelper::BACKGROUND_MOAT); // blit as absolute obstacle
sorted . hex [ 135 ] . walls . push_back ( SiegeHelper : : KEEP_BATTLEMENT ) ;
2013-08-01 01:24:44 +03:00
}
if ( siegeH & & siegeH - > town - > hasBuilt ( BuildingID : : CASTLE ) )
{
2016-02-13 16:10:14 +02:00
sorted . afterAll . walls . push_back ( SiegeHelper : : BOTTOM_BATTLEMENT ) ;
sorted . beforeAll . walls . push_back ( SiegeHelper : : UPPER_BATTLEMENT ) ;
2013-08-01 01:24:44 +03:00
}
2013-07-19 19:35:16 +03:00
}
return sorted ;
}
void CBattleInterface : : updateBattleAnimations ( )
{
//handle animations
for ( auto & elem : pendingAnims )
{
if ( ! elem . first ) //this animation should be deleted
continue ;
if ( ! elem . second )
{
elem . second = elem . first - > init ( ) ;
}
if ( elem . second & & elem . first )
elem . first - > nextFrame ( ) ;
}
//delete anims
int preSize = pendingAnims . size ( ) ;
for ( auto it = pendingAnims . begin ( ) ; it ! = pendingAnims . end ( ) ; + + it )
{
if ( it - > first = = nullptr )
{
pendingAnims . erase ( it ) ;
it = pendingAnims . begin ( ) ;
break ;
}
}
2013-11-03 15:51:25 +03:00
if ( preSize > 0 & & pendingAnims . empty ( ) )
2013-07-19 19:35:16 +03:00
{
//anims ended
2013-07-21 13:10:38 +03:00
blockUI ( activeStack = = nullptr ) ;
2013-07-19 19:35:16 +03:00
animsAreDisplayed . setn ( false ) ;
}
}
SDL_Surface * CBattleInterface : : getObstacleImage ( const CObstacleInstance & oi )
{
int frameIndex = ( animCount + 1 ) * 25 / getAnimSpeed ( ) ;
switch ( oi . obstacleType )
{
case CObstacleInstance : : USUAL :
return vstd : : circularAt ( idToObstacle . find ( oi . ID ) - > second - > ourImages , frameIndex ) . bitmap ;
case CObstacleInstance : : ABSOLUTE_OBSTACLE :
return idToAbsoluteObstacle . find ( oi . ID ) - > second ;
case CObstacleInstance : : QUICKSAND :
return vstd : : circularAt ( quicksand - > ourImages , frameIndex ) . bitmap ;
case CObstacleInstance : : LAND_MINE :
return vstd : : circularAt ( landMine - > ourImages , frameIndex ) . bitmap ;
case CObstacleInstance : : FIRE_WALL :
return vstd : : circularAt ( fireWall - > ourImages , frameIndex ) . bitmap ;
case CObstacleInstance : : FORCE_FIELD :
{
auto & forceField = dynamic_cast < const SpellCreatedObstacle & > ( oi ) ;
if ( forceField . getAffectedTiles ( ) . size ( ) > 2 )
return vstd : : circularAt ( bigForceField [ forceField . casterSide ] - > ourImages , frameIndex ) . bitmap ;
else
return vstd : : circularAt ( smallForceField [ forceField . casterSide ] - > ourImages , frameIndex ) . bitmap ;
}
case CObstacleInstance : : MOAT : //moat is blitted by SiegeHelper, this shouldn't be called
default :
assert ( 0 ) ;
return nullptr ;
}
}
Point CBattleInterface : : getObstaclePosition ( SDL_Surface * image , const CObstacleInstance & obstacle )
{
int offset = image - > h % 42 ;
if ( obstacle . obstacleType = = CObstacleInstance : : USUAL )
{
if ( obstacle . getInfo ( ) . blockedTiles . front ( ) < 0 | | offset > 37 ) //second or part is for holy ground ID=62,65,63
offset - = 42 ;
}
else if ( obstacle . obstacleType = = CObstacleInstance : : QUICKSAND )
{
offset - = 42 ;
}
Rect r = hexPosition ( obstacle . pos ) ;
r . y + = 42 - image - > h + offset ;
return r . topLeft ( ) ;
}
void CBattleInterface : : redrawBackgroundWithHexes ( const CStack * activeStack )
{
attackableHexes . clear ( ) ;
if ( activeStack )
occupyableHexes = curInt - > cb - > battleGetAvailableHexes ( activeStack , true , & attackableHexes ) ;
curInt - > cb - > battleGetStackCountOutsideHexes ( stackCountOutsideHexes ) ;
//preparating background graphic with hexes and shaded hexes
blitAt ( background , 0 , 0 , backgroundWithHexes ) ;
//draw absolute obstacles (cliffs and so on)
for ( auto & oi : curInt - > cb - > battleGetAllObstacles ( ) )
{
if ( oi - > obstacleType = = CObstacleInstance : : ABSOLUTE_OBSTACLE /* || oi.obstacleType == CObstacleInstance::MOAT*/ )
blitAt ( getObstacleImage ( * oi ) , oi - > getInfo ( ) . width , oi - > getInfo ( ) . height , backgroundWithHexes ) ;
}
if ( settings [ " battle " ] [ " cellBorders " ] . Bool ( ) )
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellBorders , nullptr , backgroundWithHexes , nullptr ) ;
if ( settings [ " battle " ] [ " stackRange " ] . Bool ( ) )
{
std : : vector < BattleHex > hexesToShade = occupyableHexes ;
hexesToShade . insert ( hexesToShade . end ( ) , attackableHexes . begin ( ) , attackableHexes . end ( ) ) ;
for ( BattleHex hex : hexesToShade )
{
int i = hex . getY ( ) ; //row
int j = hex . getX ( ) - 1 ; //column
int x = 58 + ( i % 2 = = 0 ? 22 : 0 ) + 44 * j ;
int y = 86 + 42 * i ;
SDL_Rect temp_rect = genRect ( cellShade - > h , cellShade - > w , x , y ) ;
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellShade , nullptr , backgroundWithHexes , & temp_rect ) ;
}
}
}
void CBattleInterface : : showPiecesOfWall ( SDL_Surface * to , std : : vector < int > pieces )
{
if ( ! siegeH )
return ;
for ( auto piece : pieces )
{
if ( piece < 15 ) // not a tower - just print
siegeH - > printPartOfWall ( to , piece ) ;
else // tower. find if tower is built and not destroyed - stack is present
{
// PieceID StackID
// 15 = keep, -2
// 16 = lower, -3
// 17 = upper, -4
// tower. check if tower is alive - stack is found
int stackPos = 13 - piece ;
const CStack * turret = nullptr ;
2013-09-27 22:42:17 +03:00
for ( auto & stack : curInt - > cb - > battleGetAllStacks ( true ) )
2013-07-19 19:35:16 +03:00
{
if ( stack - > position = = stackPos )
{
turret = stack ;
break ;
}
}
if ( turret )
{
std : : vector < const CStack * > stackList ( 1 , turret ) ;
showStacks ( to , stackList ) ;
siegeH - > printPartOfWall ( to , piece ) ;
}
}
}
2013-11-02 23:07:45 +03:00
}