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
# include "../CGameInfo.h"
2013-04-07 14:52:07 +03:00
# include "../gui/SDL_Extensions.h"
2011-12-14 00:35:28 +03:00
# include "../CAdvmapInterface.h"
# include "../CAnimation.h"
2013-07-06 19:10:20 +03:00
# include "../CBitmapHandler.h"
2011-12-14 00:35:28 +03:00
# include "../../lib/CObjectHandler.h"
# include "../../lib/CHeroHandler.h"
# include "../CDefHandler.h"
# include "../../lib/CSpellHandler.h"
# include "../CMusicHandler.h"
# include "../CMessage.h"
# include "../../CCallback.h"
# include "../../lib/BattleState.h"
# include "../../lib/CGeneralTextHandler.h"
2011-12-17 21:59:59 +03:00
# include "CCreatureAnimation.h"
2011-12-14 00:35:28 +03:00
# include "../Graphics.h"
# include "../CSpellWindow.h"
2012-09-29 13:59:43 +03:00
# include "../../lib/CConfigHandler.h"
2011-12-14 00:35:28 +03:00
# include "../../lib/CondSh.h"
# include "../../lib/NetPacks.h"
# include "../CPlayerInterface.h"
# include "../CCreatureWindow.h"
# include "../CVideoHandler.h"
# include "../../lib/CTownHandler.h"
2013-04-07 13:48:07 +03:00
# include "../../lib/mapping/CMap.h"
2011-12-14 00:35:28 +03:00
2011-12-22 16:05:19 +03:00
# include "CBattleAnimations.h"
# include "CBattleInterfaceClasses.h"
2011-12-14 00:35:28 +03:00
2013-04-07 14:52:07 +03:00
# include "../gui/CCursorHandler.h"
# include "../gui/CGuiHandler.h"
2013-04-04 17:58:54 +03:00
# include "../CMT.h"
2011-12-14 00:35:28 +03:00
2009-01-15 19:01:08 +02:00
# ifndef __GNUC__
const double M_PI = 3.14159265358979323846 ;
# else
# define _USE_MATH_DEFINES
# include <cmath>
# endif
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
using namespace boost : : assign ;
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 )
{
if ( float ( rand ( ) % 100 ) < creature - > animation . timeBetweenFidgets * 10 )
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
}
2013-06-23 00:47:51 +03:00
CBattleInterface : : CBattleInterface ( const CCreatureSet * army1 , const CCreatureSet * army2 ,
CGHeroInstance * hero1 , CGHeroInstance * hero2 ,
const SDL_Rect & myRect ,
shared_ptr < CPlayerInterface > att , 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
curInt = defenderInt ;
}
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-06-23 00:47:51 +03:00
if ( attackerInt & & attackerInt - > cb - > battleGetTacticDist ( ) ) //hot-seat -> check tactics for both players (defender may be local human)
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-06-23 15:36:18 +03:00
tacticsMode = static_cast < bool > ( tacticianInterface ) ; //if we found interface of player with tactics, then enter tactics mode
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 ;
std : : vector < const CStack * > stacks = curInt - > cb - > battleGetAllStacks ( ) ;
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
{
const std : : string bgName = vstd : : pickRandomElementOf ( graphics - > battleBacks [ bfieldType ] , rand ) ;
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
2013-07-02 18:23:32 +03:00
bOptions = new CAdventureMapButton ( CGI - > generaltexth - > zelp [ 381 ] . first , CGI - > generaltexth - > zelp [ 381 ] . second , boost : : bind ( & CBattleInterface : : bOptionsf , this ) , 3 , 561 , " icm003.def " , SDLK_o ) ;
bSurrender = new CAdventureMapButton ( CGI - > generaltexth - > zelp [ 379 ] . first , CGI - > generaltexth - > zelp [ 379 ] . second , boost : : bind ( & CBattleInterface : : bSurrenderf , this ) , 54 , 561 , " icm001.def " , SDLK_s ) ;
bFlee = new CAdventureMapButton ( CGI - > generaltexth - > zelp [ 380 ] . first , CGI - > generaltexth - > zelp [ 380 ] . second , boost : : bind ( & CBattleInterface : : bFleef , this ) , 105 , 561 , " icm002.def " , SDLK_r ) ;
2013-06-17 18:45:55 +03:00
bFlee - > block ( ! curInt - > cb - > getMyColor ( ) | | ! curInt - > cb - > battleCanFlee ( ) ) ;
bSurrender - > block ( ! curInt - > cb - > getMyColor ( ) | | curInt - > cb - > battleGetSurrenderCost ( ) < 0 ) ;
2013-07-02 18:23:32 +03:00
bAutofight = new CAdventureMapButton ( CGI - > generaltexth - > zelp [ 382 ] . first , CGI - > generaltexth - > zelp [ 382 ] . second , boost : : bind ( & CBattleInterface : : bAutofightf , this ) , 157 , 561 , " icm004.def " , SDLK_a ) ;
bSpell = new CAdventureMapButton ( CGI - > generaltexth - > zelp [ 385 ] . first , CGI - > generaltexth - > zelp [ 385 ] . second , boost : : bind ( & CBattleInterface : : bSpellf , this ) , 645 , 561 , " icm005.def " , SDLK_c ) ;
2011-12-14 00:35:28 +03:00
bSpell - > block ( true ) ;
2013-07-02 18:23:32 +03:00
bWait = new CAdventureMapButton ( CGI - > generaltexth - > zelp [ 386 ] . first , CGI - > generaltexth - > zelp [ 386 ] . second , boost : : bind ( & CBattleInterface : : bWaitf , this ) , 696 , 561 , " icm006.def " , SDLK_w ) ;
bDefence = new CAdventureMapButton ( CGI - > generaltexth - > zelp [ 387 ] . first , CGI - > generaltexth - > zelp [ 387 ] . second , boost : : bind ( & CBattleInterface : : bDefencef , this ) , 747 , 561 , " icm007.def " , SDLK_d ) ;
2011-12-14 00:35:28 +03:00
bDefence - > assignedKeys . insert ( SDLK_SPACE ) ;
2013-07-02 18:23:32 +03:00
bConsoleUp = new CAdventureMapButton ( std : : string ( ) , std : : string ( ) , boost : : bind ( & CBattleInterface : : bConsoleUpf , this ) , 624 , 561 , " ComSlide.def " , SDLK_UP ) ;
bConsoleDown = new CAdventureMapButton ( std : : string ( ) , std : : string ( ) , boost : : bind ( & CBattleInterface : : bConsoleDownf , this ) , 624 , 580 , " ComSlide.def " , SDLK_DOWN ) ;
2011-12-14 00:35:28 +03:00
bConsoleDown - > setOffset ( 2 ) ;
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
{
2013-07-02 18:23:32 +03:00
btactNext = new CAdventureMapButton ( std : : string ( ) , std : : string ( ) , boost : : bind ( & CBattleInterface : : bTacticNextStack , this , ( CStack * ) nullptr ) , 213 , 560 , " icm011.def " , SDLK_SPACE ) ;
btactEnd = new CAdventureMapButton ( std : : string ( ) , std : : string ( ) , boost : : bind ( & CBattleInterface : : bEndTacticPhase , this ) , 419 , 560 , " icm012.def " , SDLK_RETURN ) ;
2011-12-14 00:35:28 +03:00
bDefence - > block ( true ) ;
bWait - > block ( true ) ;
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 )
2012-04-23 22:56:37 +03:00
{
2013-06-29 16:05:48 +03:00
SDL_SetColorKey ( _n . bitmap , SDL_SRCCOLORKEY , SDL_MapRGB ( _n . bitmap - > format , 0 , 255 , 255 ) ) ;
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 ;
2013-06-23 00:47:51 +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
}
2012-06-02 18:16:54 +03:00
void CBattleInterface : : showAll ( SDL_Surface * to )
{
show ( to ) ;
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : show ( SDL_Surface * to )
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
std : : vector < const CStack * > stacks = curInt - > cb - > battleGetAllStacks ( ) ; //used in a few places
+ + animCount ;
if ( ! to ) //"evaluating" to
to = screen ;
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
SDL_Rect buf ;
SDL_GetClipRect ( to , & buf ) ;
SDL_SetClipRect ( to , & pos ) ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
//printing background and hexes
2013-06-26 14:18:27 +03:00
if ( activeStack ! = nullptr & & creAnims [ activeStack - > ID ] - > getType ( ) ! = CCreatureAnim : : MOVING ) //show everything with range
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
blitAt ( backgroundWithHexes , pos . x , pos . y , to ) ;
2009-09-10 14:28:34 +03:00
}
else
{
2011-12-14 00:35:28 +03:00
//showing background
blitAt ( background , pos . x , pos . y , to ) ;
2012-01-12 18:23:00 +03:00
if ( settings [ " battle " ] [ " cellBorders " ] . Bool ( ) )
2011-12-14 00:35:28 +03:00
{
2013-06-26 14:18:27 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellBorders , nullptr , to , & pos ) ;
2011-12-14 00:35:28 +03:00
}
2012-05-15 19:29:40 +03:00
//Blit absolute obstacles
2013-06-29 16:05:48 +03:00
for ( auto & oi : curInt - > cb - > battleGetAllObstacles ( ) )
2012-05-18 23:50:16 +03:00
if ( oi - > obstacleType = = CObstacleInstance : : ABSOLUTE_OBSTACLE )
blitAt ( imageOfObstacle ( * oi ) , pos . x + oi - > getInfo ( ) . width , pos . y + oi - > getInfo ( ) . height , to ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
//printing hovered cell
for ( int b = 0 ; b < GameConstants : : BFIELD_SIZE ; + + b )
2009-09-10 14:28:34 +03:00
{
2012-06-09 19:45:45 +03:00
if ( bfield [ b ] - > strictHovered & & bfield [ b ] - > hovered )
2011-12-14 00:35:28 +03:00
{
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 ;
}
//print shade
if ( spellToCast ) //when casting spell
{
//calculating spell school level
const CSpell & spToCast = * CGI - > spellh - > spells [ spellToCast - > additionalInfo ] ;
ui8 schoolLevel = 0 ;
2012-04-18 12:01:08 +03:00
if ( activeStack - > attackerOwned )
2011-12-14 00:35:28 +03:00
{
if ( attackingHeroInstance )
schoolLevel = attackingHeroInstance - > getSpellSchoolLevel ( & spToCast ) ;
}
else
{
2012-04-18 12:01:08 +03:00
if ( defendingHeroInstance )
2011-12-14 00:35:28 +03:00
schoolLevel = defendingHeroInstance - > getSpellSchoolLevel ( & spToCast ) ;
}
2012-05-18 23:50:16 +03:00
2011-12-14 00:35:28 +03:00
//obtaining range and printing it
2012-05-18 23:50:16 +03:00
auto shaded = spToCast . rangeInHexes ( b , schoolLevel , curInt - > cb - > battleGetMySide ( ) ) ;
2013-06-29 16:05:48 +03:00
for ( auto shadedHex : shaded ) //for spells with range greater then one hex
2011-12-14 00:35:28 +03:00
{
2012-05-18 23:50:16 +03:00
if ( settings [ " battle " ] [ " mouseShadow " ] . Bool ( ) & & ( shadedHex % GameConstants : : BFIELD_WIDTH ! = 0 ) & & ( shadedHex % GameConstants : : BFIELD_WIDTH ! = 16 ) )
2011-12-14 00:35:28 +03:00
{
2012-05-18 23:50:16 +03:00
int x = 14 + ( ( shadedHex / GameConstants : : BFIELD_WIDTH ) % 2 = = 0 ? 22 : 0 ) + 44 * ( shadedHex % GameConstants : : BFIELD_WIDTH ) + pos . x ;
int y = 86 + 42 * ( shadedHex / GameConstants : : BFIELD_WIDTH ) + pos . y ;
2011-12-14 00:35:28 +03:00
SDL_Rect temp_rect = genRect ( cellShade - > h , cellShade - > w , x , y ) ;
2013-06-26 14:18:27 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellShade , nullptr , to , & temp_rect ) ;
2011-12-14 00:35:28 +03:00
}
}
}
2012-01-12 18:23:00 +03:00
else if ( settings [ " battle " ] [ " mouseShadow " ] . Bool ( ) ) //when not casting spell
2011-12-14 00:35:28 +03:00
{ //TODO: do not check it every frame
if ( activeStack ) //highlight all attackable hexes
2012-02-16 20:10:58 +03:00
{
2011-12-22 16:05:19 +03:00
std : : set < BattleHex > set = curInt - > cb - > battleGetAttackedHexes ( activeStack , currentlyHoveredHex , attackingHex ) ;
2013-06-29 16:05:48 +03:00
for ( BattleHex hex : set )
2011-12-14 00:35:28 +03:00
{
int x = 14 + ( ( hex / GameConstants : : BFIELD_WIDTH ) % 2 = = 0 ? 22 : 0 ) + 44 * ( hex % GameConstants : : BFIELD_WIDTH ) + pos . x ;
int y = 86 + 42 * ( hex / GameConstants : : BFIELD_WIDTH ) + pos . y ;
SDL_Rect temp_rect = genRect ( cellShade - > h , cellShade - > w , x , y ) ;
2013-06-26 14:18:27 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellShade , nullptr , to , & temp_rect ) ;
2011-12-14 00:35:28 +03:00
}
}
2012-05-22 13:15:16 +03:00
2012-06-01 20:23:26 +03:00
//patch by ench0: show enemy stack movement shadow
2013-06-26 14:18:27 +03:00
// activeStack == nullptr means it is opponent's turn...
2012-06-01 20:23:26 +03:00
if ( activeStack & & settings [ " battle " ] [ " stackRange " ] . Bool ( ) )
2012-05-22 13:15:16 +03:00
{
2012-05-25 14:49:56 +03:00
// display the movement shadow of the stack at b (i.e. stack under mouse)
const CStack * const shere = curInt - > cb - > battleGetStackByPos ( b , false ) ;
if ( shere & & shere ! = activeStack & & shere - > alive ( ) )
2012-05-22 13:15:16 +03:00
{
std : : vector < BattleHex > v = curInt - > cb - > battleGetAvailableHexes ( shere , true ) ;
2013-06-29 16:05:48 +03:00
for ( BattleHex hex : v )
2012-05-22 13:15:16 +03:00
{
int x = 14 + ( ( hex / GameConstants : : BFIELD_WIDTH ) % 2 = = 0 ? 22 : 0 ) + 44 * ( hex % GameConstants : : BFIELD_WIDTH ) + pos . x ;
int y = 86 + 42 * ( hex / GameConstants : : BFIELD_WIDTH ) + pos . y ;
SDL_Rect temp_rect = genRect ( cellShade - > h , cellShade - > w , x , y ) ;
2013-06-26 14:18:27 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellShade , nullptr , to , & temp_rect ) ;
2012-05-22 13:15:16 +03:00
}
}
}
2012-06-01 20:23:26 +03:00
2011-12-14 00:35:28 +03:00
//always highlight pointed hex
int x = 14 + ( ( b / GameConstants : : BFIELD_WIDTH ) % 2 = = 0 ? 22 : 0 ) + 44 * ( b % GameConstants : : BFIELD_WIDTH ) + pos . x ;
int y = 86 + 42 * ( b / GameConstants : : BFIELD_WIDTH ) + pos . y ;
SDL_Rect temp_rect = genRect ( cellShade - > h , cellShade - > w , x , y ) ;
2013-06-26 14:18:27 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellShade , nullptr , to , & temp_rect ) ;
2011-12-14 00:35:28 +03:00
}
}
2009-09-10 14:28:34 +03:00
}
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
SDL_SetClipRect ( to , & buf ) ; //restoring previous clip_rect
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
//prevents blitting outside this window
SDL_GetClipRect ( to , & buf ) ;
SDL_SetClipRect ( to , & pos ) ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
//preparing obstacles to be shown
2012-05-18 23:50:16 +03:00
auto obstacles = curInt - > cb - > battleGetAllObstacles ( ) ;
2011-12-22 16:05:19 +03:00
std : : multimap < BattleHex , int > hexToObstacle ;
2012-04-23 22:56:37 +03:00
2011-12-14 00:35:28 +03:00
for ( size_t b = 0 ; b < obstacles . size ( ) ; + + b )
2009-09-10 14:28:34 +03:00
{
2012-05-18 23:50:16 +03:00
const auto & oi = obstacles [ b ] ;
if ( oi - > obstacleType ! = CObstacleInstance : : ABSOLUTE_OBSTACLE & & oi - > obstacleType ! = CObstacleInstance : : MOAT )
2012-04-23 22:56:37 +03:00
{
//BattleHex position = CGI->heroh->obstacles.find(obstacles[b].ID)->second.getMaxBlocked(obstacles[b].pos);
2012-05-18 23:50:16 +03:00
hexToObstacle . insert ( std : : make_pair ( oi - > pos , b ) ) ;
2012-04-23 22:56:37 +03:00
}
2009-09-10 14:28:34 +03:00
}
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
////showing units //a lot of work...
std : : vector < const CStack * > stackAliveByHex [ GameConstants : : BFIELD_SIZE ] ;
//double loop because dead stacks should be printed first
2013-06-29 16:05:48 +03:00
for ( auto & stack : stacks )
2009-09-10 14:28:34 +03:00
{
2013-06-29 16:05:48 +03:00
const CStack * s = stack ;
2011-12-14 00:35:28 +03:00
if ( creAnims . find ( s - > ID ) = = creAnims . end ( ) ) //e.g. for summoned but not yet handled stacks
continue ;
2013-07-06 19:10:20 +03:00
if ( creAnims [ s - > ID ] - > getType ( ) ! = CCreatureAnim : : DEAD & & s - > position > = 0 ) //don't show turrets here
2011-12-14 00:35:28 +03:00
stackAliveByHex [ s - > position ] . push_back ( s ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
std : : vector < const CStack * > stackDeadByHex [ GameConstants : : BFIELD_SIZE ] ;
2013-06-29 16:05:48 +03:00
for ( auto & stack : stacks )
2009-09-10 14:28:34 +03:00
{
2013-06-29 16:05:48 +03:00
const CStack * s = stack ;
2011-12-14 00:35:28 +03:00
if ( creAnims . find ( s - > ID ) = = creAnims . end ( ) ) //e.g. for summoned but not yet handled stacks
continue ;
2013-07-06 19:10:20 +03:00
if ( creAnims [ s - > ID ] - > isDead ( ) )
2011-12-14 00:35:28 +03:00
stackDeadByHex [ s - > position ] . push_back ( s ) ;
2009-09-10 14:28:34 +03:00
}
2009-10-18 17:39:53 +03:00
2011-12-14 00:35:28 +03:00
//handle animations
2013-06-29 16:05:48 +03:00
for ( auto & elem : pendingAnims )
2009-11-29 04:46:30 +02:00
{
2013-06-29 16:05:48 +03:00
if ( ! elem . first ) //this animation should be deleted
2011-12-14 00:35:28 +03:00
continue ;
2009-09-10 14:28:34 +03:00
2013-06-29 16:05:48 +03:00
if ( ! elem . second )
2011-12-14 00:35:28 +03:00
{
2013-06-29 16:05:48 +03:00
elem . second = elem . first - > init ( ) ;
2011-12-14 00:35:28 +03:00
}
2013-06-29 16:05:48 +03:00
if ( elem . second & & elem . first )
elem . first - > nextFrame ( ) ;
2011-12-14 00:35:28 +03:00
}
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
//delete anims
int preSize = pendingAnims . size ( ) ;
2013-06-29 16:05:48 +03:00
for ( auto it = pendingAnims . begin ( ) ; it ! = pendingAnims . end ( ) ; + + it )
2009-09-10 14:28:34 +03:00
{
2013-06-26 14:18:27 +03:00
if ( it - > first = = nullptr )
2011-12-14 00:35:28 +03:00
{
pendingAnims . erase ( it ) ;
it = pendingAnims . begin ( ) ;
break ;
}
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
if ( preSize > 0 & & pendingAnims . size ( ) = = 0 )
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
//action finished, restore the interface
if ( ! active )
activate ( ) ;
2011-01-09 19:41:46 +02:00
2012-05-18 23:50:16 +03:00
bool changedStack = false ;
2011-12-14 00:35:28 +03:00
//activation of next stack
2013-06-26 14:18:27 +03:00
if ( pendingAnims . size ( ) = = 0 & & stackToActivate ! = nullptr )
2011-12-14 00:35:28 +03:00
{
activateStack ( ) ;
2012-05-18 23:50:16 +03:00
changedStack = true ;
2011-12-14 00:35:28 +03:00
}
//anims ended
animsAreDisplayed . setn ( false ) ;
2012-05-18 23:50:16 +03:00
if ( changedStack )
{
//we may have changed active interface (another side in hot-seat),
// so we can't continue drawing with old setting. So we call ourselves again and end.
SDL_SetClipRect ( to , & buf ) ; //restoring previous clip_rect
show ( to ) ;
return ;
}
2009-09-10 14:28:34 +03:00
}
2013-07-10 13:45:51 +03:00
//Get all the spell effects that need to be drawn with the stack
std : : vector < const BattleEffect * > battleEffectByHex [ GameConstants : : BFIELD_SIZE ] ;
std : : vector < const BattleEffect * > topBattleEffects ;
for ( auto & battleEffect : battleEffects )
{
const BattleEffect * e = & battleEffect ;
if ( e - > position . isValid ( ) )
battleEffectByHex [ e - > position ] . push_back ( e ) ;
else
topBattleEffects . push_back ( e ) ;
}
2013-06-29 16:05:48 +03:00
for ( auto & elem : stackDeadByHex ) //showing dead stacks
2011-01-09 19:41:46 +02:00
{
2013-06-29 16:05:48 +03:00
for ( size_t v = 0 ; v < elem . size ( ) ; + + v )
2011-12-14 00:35:28 +03:00
{
2013-07-07 22:44:08 +03:00
creAnims [ elem [ v ] - > ID ] - > nextFrame ( to , creDir [ elem [ v ] - > ID ] ) ;
2013-07-06 19:10:20 +03:00
creAnims [ elem [ v ] - > ID ] - > incrementFrame ( float ( GH . mainFPSmng - > getElapsedMilliseconds ( ) ) / 1000 ) ;
2011-12-14 00:35:28 +03:00
}
2011-01-08 20:33:40 +02:00
}
2011-12-14 00:35:28 +03:00
std : : vector < const CStack * > flyingStacks ; //flying stacks should be displayed later, over other stacks and obstacles
if ( ! siegeH )
2009-09-10 14:28:34 +03:00
{
2013-07-10 13:45:51 +03:00
for ( int b = 0 ; b < GameConstants : : BFIELD_SIZE ; + + b ) //showing alive stacks and spells
2011-12-14 00:35:28 +03:00
{
showObstacles ( & hexToObstacle , obstacles , b , to ) ;
2012-05-05 00:16:39 +03:00
showAliveStacks ( stackAliveByHex , b , & flyingStacks , to ) ;
2013-07-10 13:45:51 +03:00
showBattleEffects ( battleEffectByHex [ b ] , to ) ;
2011-12-14 00:35:28 +03:00
}
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
// Siege drawing
else
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
for ( int i = 0 ; i < 4 ; i + + )
{
// xMin, xMax => go from hex x pos to hex x pos
// yMin, yMax => go from hex y pos to hex y pos
// xMove => 0: left side, 1: right side
// xMoveDir => 0: decrement, 1: increment, alters every second hex line either xMin or xMax depending on xMove
int xMin , xMax , yMin , yMax , xMove , xMoveDir = 0 ;
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
switch ( i )
{
// display units shown at the upper left side
2012-02-16 20:10:58 +03:00
case 0 :
2011-12-14 00:35:28 +03:00
xMin = 0 ;
yMin = 0 ;
xMax = 11 ;
yMax = 4 ;
xMove = 1 ;
break ;
// display wall/units shown at the upper wall area/right upper side
case 1 :
xMin = 12 ;
yMin = 0 ;
xMax = 16 ;
yMax = 4 ;
xMove = 0 ;
break ;
// display units shown at the lower wall area/right lower side
case 2 :
xMin = 10 ;
yMin = 5 ;
xMax = 16 ;
yMax = 10 ;
xMove = 0 ;
xMoveDir = 1 ;
break ;
// display units shown at the left lower side
case 3 :
xMin = 0 ;
yMin = 5 ;
xMax = 9 ;
yMax = 10 ;
xMove = 1 ;
xMoveDir = 1 ;
break ;
}
2009-09-11 15:46:26 +03:00
2011-12-14 00:35:28 +03:00
int runNum = 0 ;
for ( int j = yMin ; j < = yMax ; j + + )
{
if ( runNum > 0 )
{
if ( xMin = = xMax )
2012-02-16 20:10:58 +03:00
xMax = xMin = ( ( runNum % 2 ) = = 0 ) ? ( xMin + ( xMoveDir = = 0 ? - 1 : 1 ) ) : xMin ;
2011-12-14 00:35:28 +03:00
else if ( xMove = = 1 )
2012-02-16 20:10:58 +03:00
xMax = ( ( runNum % 2 ) = = 0 ) ? ( xMax + ( xMoveDir = = 0 ? - 1 : 1 ) ) : xMax ;
2011-12-14 00:35:28 +03:00
else if ( xMove = = 0 )
2012-02-16 20:10:58 +03:00
xMin = ( ( runNum % 2 ) = = 0 ) ? ( xMin + ( xMoveDir = = 0 ? - 1 : 1 ) ) : xMin ;
2011-12-14 00:35:28 +03:00
}
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
for ( int k = xMin ; k < = xMax ; k + + )
{
int hex = j * 17 + k ;
showObstacles ( & hexToObstacle , obstacles , hex , to ) ;
2012-05-05 00:16:39 +03:00
showAliveStacks ( stackAliveByHex , hex , & flyingStacks , to ) ;
2013-07-10 13:45:51 +03:00
showBattleEffects ( battleEffectByHex [ hex ] , to ) ;
2011-12-14 00:35:28 +03:00
showPieceOfWall ( to , hex , stacks ) ;
}
+ + runNum ;
}
}
2011-05-26 17:47:45 +03:00
}
2012-02-16 20:10:58 +03:00
2013-06-29 16:05:48 +03:00
for ( auto & flyingStack : flyingStacks ) //showing flying stacks
showAliveStack ( flyingStack , to ) ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
//units shown
2011-05-26 17:47:45 +03:00
2011-12-14 00:35:28 +03:00
// Show projectiles
projectileShowHelper ( to ) ;
2011-05-30 17:16:34 +03:00
2011-12-14 00:35:28 +03:00
//showing spell effects
2013-07-10 13:45:51 +03:00
showBattleEffects ( topBattleEffects , to ) ;
2011-05-30 17:16:34 +03:00
2011-12-14 00:35:28 +03:00
SDL_SetClipRect ( to , & buf ) ; //restoring previous clip_rect
2011-05-30 17:16:34 +03:00
2011-12-14 00:35:28 +03:00
//showing menu background and console
blitAt ( menu , pos . x , 556 + pos . y , to ) ;
2012-02-16 20:10:58 +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 - > showAll ( to ) ;
btactEnd - > showAll ( to ) ;
2009-09-10 14:28:34 +03:00
}
else
{
2011-12-14 00:35:28 +03:00
console - > showAll ( to ) ;
bConsoleUp - > showAll ( to ) ;
bConsoleDown - > showAll ( to ) ;
2009-09-10 14:28:34 +03:00
}
2011-05-26 17:47:45 +03:00
2011-12-14 00:35:28 +03:00
//showing buttons
bOptions - > showAll ( to ) ;
bSurrender - > showAll ( to ) ;
bFlee - > showAll ( to ) ;
bAutofight - > showAll ( to ) ;
bSpell - > showAll ( to ) ;
bWait - > showAll ( to ) ;
bDefence - > showAll ( to ) ;
2009-09-24 16:44:55 +03:00
2011-12-14 00:35:28 +03:00
//showing in-game console
LOCPLINT - > cingconsole - > show ( to ) ;
2009-09-10 14:28:34 +03:00
2011-12-22 16:05:19 +03:00
Rect posWithQueue = Rect ( pos . x , pos . y , 800 , 600 ) ;
2009-09-10 14:28:34 +03:00
2012-01-12 18:23:00 +03:00
if ( settings [ " battle " ] [ " showQueue " ] . Bool ( ) )
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
if ( ! queue - > embedded )
{
posWithQueue . y - = queue - > pos . h ;
posWithQueue . h + = queue - > pos . h ;
}
//showing queue
if ( ! bresult )
queue - > showAll ( to ) ;
else
2012-08-28 19:28:21 +03:00
queue - > blitBg ( to ) ;
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
//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 ) ;
}
2009-09-10 14:28:34 +03:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : showAliveStacks ( std : : vector < const CStack * > * aliveStacks , int hex , std : : vector < const CStack * > * flyingStacks , SDL_Surface * to )
2009-09-10 14:28:34 +03:00
{
2011-12-14 00:35:28 +03:00
//showing hero animations
if ( hex = = 0 )
if ( attackingHero )
attackingHero - > show ( to ) ;
2009-09-10 14:28:34 +03:00
2011-12-14 00:35:28 +03:00
if ( hex = = 16 )
if ( defendingHero )
defendingHero - > show ( to ) ;
2012-02-16 20:10:58 +03:00
2013-06-29 16:05:48 +03:00
for ( auto & elem : aliveStacks [ hex ] )
2011-12-14 00:35:28 +03:00
{
2013-06-29 16:05:48 +03:00
const CStack * s = elem ;
2009-08-01 14:55:40 +03:00
2013-07-06 19:10:20 +03:00
if ( ! s - > hasBonusOfType ( Bonus : : FLYING ) | | creAnims [ s - > ID ] - > getType ( ) ! = CCreatureAnim : : DEAD )
2011-12-14 00:35:28 +03:00
showAliveStack ( s , to ) ;
else
flyingStacks - > push_back ( s ) ;
}
2009-09-16 13:36:50 +03:00
}
2012-05-18 23:50:16 +03:00
void CBattleInterface : : showObstacles ( std : : multimap < BattleHex , int > * hexToObstacle , std : : vector < shared_ptr < const CObstacleInstance > > & obstacles , int hex , SDL_Surface * to )
2009-01-15 19:01:08 +02:00
{
2013-07-07 22:44:08 +03:00
auto obstRange = hexToObstacle - > equal_range ( hex ) ;
2011-05-25 02:17:57 +03:00
2013-06-29 16:05:48 +03:00
for ( auto it = obstRange . first ; it ! = obstRange . second ; + + it )
2009-09-20 15:47:40 +03:00
{
2012-05-18 23:50:16 +03:00
const CObstacleInstance & curOb = * obstacles [ it - > second ] ;
2012-05-05 00:16:39 +03:00
SDL_Surface * toBlit = imageOfObstacle ( curOb ) ;
Point p = whereToBlitObstacleImage ( toBlit , curOb ) ;
blitAt ( toBlit , p . x , p . y , to ) ;
2010-08-04 14:18:13 +03:00
}
2011-12-14 00:35:28 +03:00
}
2009-09-20 15:47:40 +03:00
2013-07-10 13:45:51 +03:00
void CBattleInterface : : showBattleEffects ( const std : : vector < const BattleEffect * > & battleEffects , SDL_Surface * to )
{
for ( auto & elem : battleEffects )
{
SDL_Surface * bitmapToBlit = elem - > anim - > ourImages [ ( elem - > frame ) % elem - > anim - > ourImages . size ( ) ] . bitmap ;
SDL_Rect temp_rect = genRect ( bitmapToBlit - > h , bitmapToBlit - > w , elem - > x , elem - > y ) ;
SDL_BlitSurface ( bitmapToBlit , nullptr , to , & temp_rect ) ;
}
}
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 :
break ;
attackingHex = myNumber + 1 ; //right
case 4 :
break ;
attackingHex = myNumber + GameConstants : : BFIELD_WIDTH + zigzagCorrection ; //bottom right
case 5 :
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 ( ) )
{
2013-07-02 18:23:32 +03:00
CFunctionList < void ( ) > ony = boost : : 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
char buffer [ 1000 ] ;
sprintf ( buffer , CGI - > generaltexth - > allTexts [ 340 ] . c_str ( ) , heroName . c_str ( ) ) ; //The Shackles of War are present. %s can not retreat!
//printing message
curInt - > showInfoDialog ( std : : string ( buffer ) , comps ) ;
}
}
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 ;
2013-06-22 17:47:20 +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
{
2013-06-23 14:25:48 +03:00
curInt - > isAutoFightOn = true ;
deactivate ( ) ;
bAutofight - > activate ( ) ;
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 ; ;
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.
LOCPLINT - > showInfoDialog ( boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 683 ] )
% 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
// Turret positions are read out of the /config/wall_pos.txt
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-07-06 19:10:20 +03:00
creAnims [ stack - > ID ] - > pos . h = siegeH - > town - > town - > clientInfo . siegeShooterCropHeight ;
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 ) ;
2012-02-18 20:39:47 +03:00
2011-12-14 00:35:28 +03:00
}
void CBattleInterface : : stackRemoved ( int stackID )
{
delete creAnims [ stackID ] ;
creAnims . erase ( stackID ) ;
creDir . erase ( stackID ) ;
2012-02-22 20:43:59 +03:00
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
{
2013-06-29 16:05:48 +03:00
if ( ! attackedInfo . cloneKilled ) //FIXME: play dead animation for cloned creature before it vanishes
addNewAnim ( new CDefenceAnimation ( attackedInfo , this ) ) ;
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
}
2012-02-18 20:39:47 +03:00
if ( attackedInfos . front ( ) . cloneKilled ) //FIXME: cloned stack is already removed
return ;
2011-12-14 00:35:28 +03:00
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 ) ;
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
}
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 ) ) ;
}
waitForAnims ( ) ;
}
2011-01-18 19:23:31 +02:00
2011-12-14 00:35:28 +03:00
void CBattleInterface : : newRoundFirst ( int round )
{
//handle regeneration
std : : vector < const CStack * > stacks = curInt - > cb - > battleGetStacks ( ) ; //gets only alive stacks
2013-06-29 16:05:48 +03:00
// for(const CStack *s : stacks)
2011-12-14 00:35:28 +03:00
// {
// }
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 ] ) ;
//unlock spellbook
//bSpell->block(!curInt->cb->battleCanCastSpell());
//don't unlock spellbook - this should be done when we have axctive creature
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
}
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
{
2012-04-03 02:23:14 +03:00
if ( ! siegeH | | tacticsMode )
2011-12-14 00:35:28 +03:00
return false ;
2012-08-26 12:07:48 +03:00
int wallUnder = curInt - > cb - > battleHexToWallPart ( hex ) ;
2012-08-27 23:45:58 +03:00
if ( wallUnder < 0 ) //invalid or indestructible
2011-12-14 00:35:28 +03:00
return false ;
2012-08-26 12:07:48 +03:00
return curInt - > cb - > battleGetWallState ( wallUnder ) < EWallState : : DESTROYED ;
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
{
2013-06-29 16:05:48 +03:00
for ( auto it = ca . attackedParts . begin ( ) ; it ! = ca . attackedParts . end ( ) ; + + it )
2011-10-08 09:59:36 +03:00
{
2011-12-14 00:35:28 +03:00
const CStack * stack = curInt - > cb - > battleGetStackByID ( ca . attacker ) ;
2013-06-26 14:18:27 +03:00
addNewAnim ( new CShootingAnimation ( this , stack , it - > first . second , nullptr , true , it - > second ) ) ;
2009-11-13 23:01:33 +02:00
2011-12-14 00:35:28 +03:00
SDL_FreeSurface ( siegeH - > walls [ it - > first . first + 2 ] ) ;
siegeH - > walls [ it - > first . first + 2 ] = BitmapHandler : : loadBitmap (
siegeH - > getSiegeName ( it - > first . first + 2 , curInt - > cb - > battleGetWallState ( it - > first . first ) ) ) ;
}
waitForAnims ( ) ;
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
{
2011-12-14 00:35:28 +03:00
const CSpell & spell = * CGI - > spellh - > spells [ sc - > id ] ;
2009-08-26 17:09:55 +03:00
2011-12-14 00:35:28 +03:00
//spell opening battle is cast when no stack is active
2013-06-26 14:18:27 +03:00
if ( sc - > castedByHero & & ( activeStack = = nullptr | | sc - > side = = ! activeStack - > attackerOwned ) )
2011-12-14 00:35:28 +03:00
bSpell - > block ( true ) ;
2009-08-26 17:09:55 +03:00
2011-12-14 00:35:28 +03:00
std : : vector < std : : string > anims ; //for magic arrow and ice bolt
2009-08-26 17:09:55 +03:00
2011-12-14 00:35:28 +03:00
if ( vstd : : contains ( CCS - > soundh - > spellSounds , & spell ) )
CCS - > soundh - > playSound ( CCS - > soundh - > spellSounds [ & spell ] ) ;
2009-08-26 17:09:55 +03:00
2011-12-14 00:35:28 +03:00
switch ( sc - > id )
2009-08-26 17:09:55 +03:00
{
2013-02-11 02:24:57 +03:00
case SpellID : : MAGIC_ARROW :
2009-08-26 17:09:55 +03:00
{
2011-12-14 00:35:28 +03:00
//initialization of anims
anims . push_back ( " C20SPX0.DEF " ) ; anims . push_back ( " C20SPX1.DEF " ) ; anims . push_back ( " C20SPX2.DEF " ) ; anims . push_back ( " C20SPX3.DEF " ) ; anims . push_back ( " C20SPX4.DEF " ) ;
2009-08-26 17:09:55 +03:00
}
2013-02-11 02:24:57 +03:00
case SpellID : : ICE_BOLT :
2009-08-26 17:09:55 +03:00
{
2011-12-14 00:35:28 +03:00
if ( anims . size ( ) = = 0 ) //initialization of anims
2009-08-26 17:09:55 +03:00
{
2011-12-14 00:35:28 +03:00
anims . push_back ( " C08SPW0.DEF " ) ; anims . push_back ( " C08SPW1.DEF " ) ; anims . push_back ( " C08SPW2.DEF " ) ; anims . push_back ( " C08SPW3.DEF " ) ; anims . push_back ( " C08SPW4.DEF " ) ;
2009-08-26 17:09:55 +03:00
}
2011-12-14 00:35:28 +03:00
} //end of ice bolt only part
{ //common ice bolt and magic arrow part
//initial variables
std : : string animToDisplay ;
2011-12-22 16:05:19 +03:00
Point srccoord = ( sc - > side ? Point ( 770 , 60 ) : Point ( 30 , 60 ) ) + pos ;
2013-07-07 22:44:08 +03:00
Point destcoord = CClickableHex : : getXYUnitAnim ( sc - > tile , curInt - > cb - > battleGetStackByPos ( sc - > tile ) , this ) ; //position attacked by arrow
2011-12-14 00:35:28 +03:00
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 = false ;
if ( angle < 0 )
2009-08-26 17:09:55 +03:00
{
2011-12-14 00:35:28 +03:00
Vflip = true ;
angle = - angle ;
2009-08-26 17:09:55 +03:00
}
2011-12-14 00:35:28 +03:00
//choosing animation by angle
if ( angle > 1.50 )
animToDisplay = anims [ 0 ] ;
else if ( angle > 1.20 )
animToDisplay = anims [ 1 ] ;
else if ( angle > 0.90 )
animToDisplay = anims [ 2 ] ;
else if ( angle > 0.60 )
animToDisplay = anims [ 3 ] ;
else
animToDisplay = anims [ 4 ] ;
2009-08-26 17:09:55 +03:00
2011-12-14 00:35:28 +03:00
//displaying animation
CDefEssential * animDef = CDefHandler : : giveDefEss ( animToDisplay ) ;
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
int steps = sqrt ( static_cast < double > ( ( destcoord . x - srccoord . x ) * ( destcoord . x - srccoord . x ) + ( destcoord . y - srccoord . y ) * ( destcoord . y - srccoord . y ) ) ) / 40 ;
if ( steps < = 0 )
steps = 1 ;
2009-08-26 17:09:55 +03:00
2011-12-14 00:35:28 +03:00
int dx = ( destcoord . x - srccoord . x - animDef - > ourImages [ 0 ] . bitmap - > w ) / steps , dy = ( destcoord . y - srccoord . y - animDef - > ourImages [ 0 ] . bitmap - > h ) / steps ;
delete animDef ;
addNewAnim ( new CSpellEffectAnimation ( this , animToDisplay , srccoord . x , srccoord . y , dx , dy , Vflip ) ) ;
break ; //for 15 and 16 cases
}
2013-02-11 02:24:57 +03:00
case SpellID : : LIGHTNING_BOLT :
case SpellID : : TITANS_LIGHTNING_BOLT :
case SpellID : : THUNDERBOLT :
case SpellID : : CHAIN_LIGHTNING : //TODO: zigzag effect
2013-06-29 16:05:48 +03:00
for ( auto & elem : sc - > affectedCres ) //in case we have multiple targets
2012-04-18 18:57:49 +03:00
{
2013-06-29 16:05:48 +03:00
displayEffect ( 1 , curInt - > cb - > battleGetStackByID ( elem , false ) - > position ) ;
displayEffect ( spell . mainEffectAnim , curInt - > cb - > battleGetStackByID ( elem , false ) - > position ) ;
2012-04-18 18:57:49 +03:00
}
2011-12-14 00:35:28 +03:00
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : DISPEL :
case SpellID : : CURE :
case SpellID : : RESURRECTION :
case SpellID : : ANIMATE_DEAD :
case SpellID : : DISPEL_HELPFUL_SPELLS :
case SpellID : : SACRIFICE : //TODO: animation upon killed stack
2013-06-29 16:05:48 +03:00
for ( auto & elem : sc - > affectedCres )
2009-09-04 17:11:42 +03:00
{
2013-06-29 16:05:48 +03:00
displayEffect ( spell . mainEffectAnim , curInt - > cb - > battleGetStackByID ( elem , false ) - > position ) ;
2011-12-14 00:35:28 +03:00
}
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : SUMMON_FIRE_ELEMENTAL :
case SpellID : : SUMMON_EARTH_ELEMENTAL :
case SpellID : : SUMMON_WATER_ELEMENTAL :
case SpellID : : SUMMON_AIR_ELEMENTAL :
case SpellID : : CLONE :
case SpellID : : REMOVE_OBSTACLE :
2012-04-28 22:40:27 +03:00
addNewAnim ( new CDummyAnimation ( this , 2 ) ) ; //interface won't return until animation is played. TODO: make it smarter?
2011-12-14 00:35:28 +03:00
break ;
} //switch(sc->id)
2009-09-04 17:11:42 +03:00
2013-02-17 21:53:01 +03:00
if ( spell . isDamageSpell ( ) & & sc - > affectedCres . empty ( ) ) //for example Inferno that causes no BattleStackAttacked
{
displayEffect ( spell . mainEffectAnim , sc - > tile ) ;
}
2011-12-14 00:35:28 +03:00
//support for resistance
2013-06-29 16:05:48 +03:00
for ( auto & elem : sc - > resisted )
2011-12-14 00:35:28 +03:00
{
2013-06-29 16:05:48 +03:00
int tile = curInt - > cb - > battleGetStackByID ( elem ) - > position ;
2011-12-14 00:35:28 +03:00
displayEffect ( 78 , tile ) ;
}
2010-12-06 01:10:02 +02:00
2011-12-14 00:35:28 +03:00
//displaying message in console
bool customSpell = false ;
bool plural = false ; //add singular / plural form of creature text if this is true
int textID = 0 ;
if ( sc - > affectedCres . size ( ) = = 1 )
{
std : : string text = CGI - > generaltexth - > allTexts [ 195 ] ;
if ( sc - > castedByHero )
{
2013-01-20 23:29:35 +03:00
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetHeroInfo ( sc - > side ) . name ) ;
2011-12-14 00:35:28 +03:00
boost : : algorithm : : replace_first ( text , " %s " , CGI - > spellh - > spells [ sc - > id ] - > name ) ; //spell name
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) , false ) - > getCreature ( ) - > namePl ) ; //target
}
else
{
switch ( sc - > id )
2009-09-04 17:11:42 +03:00
{
2013-02-11 02:24:57 +03:00
case SpellID : : STONE_GAZE :
2011-12-14 00:35:28 +03:00
customSpell = true ;
plural = true ;
textID = 558 ;
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : POISON :
2011-12-14 00:35:28 +03:00
customSpell = true ;
plural = true ;
textID = 561 ;
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : BIND :
2011-12-14 00:35:28 +03:00
customSpell = true ;
text = CGI - > generaltexth - > allTexts [ 560 ] ;
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) , false ) - > getCreature ( ) - > namePl ) ;
break ; //Roots and vines bind the %s to the ground!
2013-02-11 02:24:57 +03:00
case SpellID : : DISEASE :
2011-12-14 00:35:28 +03:00
customSpell = true ;
plural = true ;
textID = 553 ;
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : PARALYZE :
2011-12-14 00:35:28 +03:00
customSpell = true ;
plural = true ;
textID = 563 ;
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : AGE :
2009-09-04 17:11:42 +03:00
{
2011-12-14 00:35:28 +03:00
customSpell = true ;
if ( curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) ) - > count > 1 )
2011-05-15 20:18:36 +03:00
{
2011-12-14 00:35:28 +03:00
text = CGI - > generaltexth - > allTexts [ 552 ] ;
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) ) - > type - > namePl ) ;
2011-05-15 20:18:36 +03:00
}
2011-12-14 00:35:28 +03:00
else
2011-05-15 20:18:36 +03:00
{
2011-12-14 00:35:28 +03:00
text = CGI - > generaltexth - > allTexts [ 551 ] ;
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) ) - > type - > nameSing ) ;
2011-05-15 20:18:36 +03:00
}
2012-02-16 20:10:58 +03:00
//The %s shrivel with age, and lose %d hit points."
2011-12-14 00:35:28 +03:00
TBonusListPtr bl = curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) , false ) - > getBonuses ( Selector : : type ( Bonus : : STACK_HEALTH ) ) ;
bl - > remove_if ( Selector : : source ( Bonus : : SPELL_EFFECT , 75 ) ) ;
boost : : algorithm : : replace_first ( text , " %d " , boost : : lexical_cast < std : : string > ( bl - > totalValue ( ) / 2 ) ) ;
2009-09-05 17:10:26 +03:00
}
2011-12-14 00:35:28 +03:00
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : THUNDERBOLT :
2011-12-14 00:35:28 +03:00
text = CGI - > generaltexth - > allTexts [ 367 ] ;
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) ) - > type - > namePl ) ;
console - > addText ( text ) ;
text = CGI - > generaltexth - > allTexts [ 343 ] . substr ( 1 , CGI - > generaltexth - > allTexts [ 343 ] . size ( ) - 1 ) ; //Does %d points of damage.
boost : : algorithm : : replace_first ( text , " %d " , boost : : lexical_cast < std : : string > ( sc - > dmgToDisplay ) ) ; //no more text afterwards
console - > addText ( text ) ;
customSpell = true ;
text = " " ; //yeah, it's a terrible mess
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : DISPEL_HELPFUL_SPELLS :
2011-12-14 00:35:28 +03:00
text = CGI - > generaltexth - > allTexts [ 555 ] ;
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) ) - > type - > namePl ) ;
customSpell = true ;
break ;
2013-02-11 02:24:57 +03:00
case SpellID : : DEATH_STARE :
2011-12-14 00:35:28 +03:00
customSpell = true ;
if ( sc - > dmgToDisplay )
{
if ( sc - > dmgToDisplay > 1 )
{
text = CGI - > generaltexth - > allTexts [ 119 ] ; //%d %s die under the terrible gaze of the %s.
boost : : algorithm : : replace_first ( text , " %d " , boost : : lexical_cast < std : : string > ( sc - > dmgToDisplay ) ) ;
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) , false ) - > getCreature ( ) - > namePl ) ;
}
else
{
text = CGI - > generaltexth - > allTexts [ 118 ] ; //One %s dies under the terrible gaze of the %s.
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) ) - > type - > nameSing ) ;
}
boost : : algorithm : : replace_first ( text , " %s " , CGI - > creh - > creatures [ sc - > attackerType ] - > namePl ) ; //casting stack
}
else
text = " " ;
break ;
default :
2012-02-16 20:10:58 +03:00
text = CGI - > generaltexth - > allTexts [ 565 ] ; //The %s casts %s
2011-12-14 00:35:28 +03:00
boost : : algorithm : : replace_first ( text , " %s " , CGI - > creh - > creatures [ sc - > attackerType ] - > namePl ) ; //casting stack
2011-05-15 20:18:36 +03:00
}
2011-12-14 00:35:28 +03:00
if ( plural )
{
if ( curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) ) - > count > 1 )
{
text = CGI - > generaltexth - > allTexts [ textID + 1 ] ;
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) ) - > getName ( ) ) ;
}
else
{
text = CGI - > generaltexth - > allTexts [ textID ] ;
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetStackByID ( * sc - > affectedCres . begin ( ) ) - > getName ( ) ) ;
}
}
}
if ( ! customSpell & & ! sc - > dmgToDisplay )
boost : : algorithm : : replace_first ( text , " %s " , CGI - > spellh - > spells [ sc - > id ] - > name ) ; //simple spell name
if ( text . size ( ) )
console - > addText ( text ) ;
}
else
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
std : : string text = CGI - > generaltexth - > allTexts [ 196 ] ;
if ( sc - > castedByHero )
2009-01-15 19:01:08 +02:00
{
2013-01-20 23:29:35 +03:00
boost : : algorithm : : replace_first ( text , " %s " , curInt - > cb - > battleGetHeroInfo ( sc - > side ) . name ) ;
2011-12-14 00:35:28 +03:00
}
else if ( sc - > attackerType < CGI - > creh - > creatures . size ( ) )
{
boost : : algorithm : : replace_first ( text , " %s " , CGI - > creh - > creatures [ sc - > attackerType ] - > namePl ) ; //creature caster
}
else
{
//TODO artifacts that cast spell; scripts some day
2012-06-10 15:01:56 +03:00
boost : : algorithm : : replace_first ( text , " %s " , " Something " ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
boost : : algorithm : : replace_first ( text , " %s " , CGI - > spellh - > spells [ sc - > id ] - > name ) ;
console - > addText ( text ) ;
}
if ( sc - > dmgToDisplay & & ! customSpell )
{
std : : string dmgInfo = CGI - > generaltexth - > allTexts [ 376 ] ;
boost : : algorithm : : replace_first ( dmgInfo , " %s " , CGI - > spellh - > spells [ sc - > id ] - > name ) ; //simple spell name
boost : : algorithm : : replace_first ( dmgInfo , " %d " , boost : : lexical_cast < std : : string > ( sc - > dmgToDisplay ) ) ;
console - > addText ( dmgInfo ) ; //todo: casualties (?)
}
waitForAnims ( ) ;
//mana absorption
if ( sc - > manaGained )
{
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
{
2011-12-14 00:35:28 +03:00
int effID = sse . effect . back ( ) . sid ;
if ( effID ! = - 1 ) //can be -1 for defensive stance effect
2009-01-15 19:01:08 +02:00
{
2013-06-29 16:05:48 +03:00
for ( auto & elem : sse . stacks )
2009-01-15 19:01:08 +02:00
{
2013-07-10 13:45:51 +03:00
bool areaEffect ( CGI - > spellh - > spells [ effID ] - > getTargetType ( ) = = CSpell : : ETargetType : : NO_TARGET ) ;
displayEffect ( CGI - > spellh - > spells [ effID ] - > mainEffectAnim , curInt - > cb - > battleGetStackByID ( elem ) - > position , areaEffect ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
}
else if ( sse . stacks . size ( ) = = 1 & & sse . effect . size ( ) = = 2 )
{
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
char txt [ 4000 ] ;
BonusList defenseBonuses = * ( stack - > getBonuses ( Selector : : typeSubtype ( Bonus : : PRIMARY_SKILL , PrimarySkill : : DEFENSE ) ) ) ;
defenseBonuses . remove_if ( Selector : : durationType ( Bonus : : STACK_GETS_TURN ) ) ; //remove bonuses gained from defensive stance
int val = stack - > Defense ( ) - defenseBonuses . totalValue ( ) ;
sprintf ( txt , CGI - > generaltexth - > allTexts [ txtid ] . c_str ( ) , ( stack - > count ! = 1 ) ? stack - > getCreature ( ) - > namePl . c_str ( ) : stack - > getCreature ( ) - > nameSing . c_str ( ) , val ) ;
console - > addText ( txt ) ;
2009-01-15 19:01:08 +02:00
}
2012-02-16 20:10:58 +03:00
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
2011-12-14 00:35:28 +03:00
void CBattleInterface : : castThisSpell ( int spellID )
{
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 ;
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 ;
2012-04-18 12:01:08 +03:00
sp = CGI - > spellh - > spells [ spellID ] ;
2011-12-14 00:35:28 +03:00
spellSelMode = ANY_LOCATION ;
2012-04-18 12:01:08 +03:00
if ( sp - > getTargetType ( ) = = CSpell : : CREATURE )
2011-12-14 00:35:28 +03:00
{
2012-04-18 12:01:08 +03:00
spellSelMode = selectionTypeByPositiveness ( * sp ) ;
2011-12-14 00:35:28 +03:00
}
2012-04-18 12:01:08 +03:00
if ( sp - > getTargetType ( ) = = CSpell : : CREATURE_EXPERT_MASSIVE )
2011-12-14 00:35:28 +03:00
{
2012-04-18 12:01:08 +03:00
if ( castingHero & & castingHero - > getSpellSchoolLevel ( sp ) < 3 )
spellSelMode = selectionTypeByPositiveness ( * sp ) ;
2009-01-15 19:01:08 +02:00
else
2011-12-14 00:35:28 +03:00
spellSelMode = NO_LOCATION ;
2009-01-15 19:01:08 +02:00
}
2012-04-18 12:01:08 +03:00
if ( sp - > getTargetType ( ) = = CSpell : : OBSTACLE )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
spellSelMode = OBSTACLE ;
2012-04-28 18:18:21 +03:00
} //FIXME: Remove Obstacle has range X, unfortunatelly :(
else if ( sp - > range [ castingHero - > getSpellSchoolLevel ( sp ) ] = = " X " ) //spell has no range
2011-12-14 00:35:28 +03:00
{
spellSelMode = NO_LOCATION ;
2009-01-15 19:01:08 +02:00
}
2012-04-18 12:01:08 +03:00
if ( sp - > range [ castingHero - > getSpellSchoolLevel ( sp ) ] . size ( ) > 1 ) //spell has many-hex range
2009-09-20 15:47:40 +03:00
{
2011-12-14 00:35:28 +03:00
spellSelMode = ANY_LOCATION ;
2009-09-20 15:47:40 +03:00
}
2011-12-14 00:35:28 +03:00
2013-02-11 02:24:57 +03:00
if ( spellID = = SpellID : : FIRE_WALL | | spellID = = SpellID : : FORCE_FIELD )
2012-05-18 23:50:16 +03:00
{
spellSelMode = FREE_LOCATION ;
}
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
{
2013-07-10 13:45:51 +03:00
addNewAnim ( new CSpellEffectAnimation ( this , effect , destTile , 0 , 0 , false , areaEffect ) ) ;
2011-12-14 00:35:28 +03:00
}
2011-01-14 20:08:01 +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
}
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 ( ) ) ;
}
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-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
myTurn = true ;
2013-06-23 00:47:51 +03:00
if ( ! ! attackerInt & & defenderInt ) //hotseat -> need to pick which interface "takes over" as active
2011-12-14 00:35:28 +03:00
curInt = attackerInt - > playerID = = s - > owner ? attackerInt : defenderInt ;
2009-09-20 15:47:40 +03:00
2011-12-14 00:35:28 +03:00
queue - > update ( ) ;
redrawBackgroundWithHexes ( activeStack ) ;
2013-06-16 01:09:15 +03:00
bWait - > block ( s - > waited ( ) | | tacticsMode ) ; //block waiting button if stack has been already waiting
2011-12-14 00:35:28 +03:00
//block cast spell button if hero doesn't have a spellbook
2012-08-26 12:07:48 +03:00
ESpellCastProblem : : ESpellCastProblem spellcastingProblem ;
bool canCastSpells = curInt - > cb - > battleCanCastSpell ( & spellcastingProblem ) ;
bSpell - > block ( ! canCastSpells & & spellcastingProblem ! = ESpellCastProblem : : MAGIC_IS_BLOCKED ) ; //if magic is blocked, we leave button active, so the message can be displayed (cf bug #97)
2013-06-26 14:18:27 +03:00
bSurrender - > block ( ( curInt = = attackerInt ? defendingHeroInstance : attackingHeroInstance ) = = nullptr ) ;
2011-12-14 00:35:28 +03:00
bFlee - > block ( ! curInt - > cb - > battleCanFlee ( ) ) ;
bSurrender - > block ( curInt - > cb - > battleGetSurrenderCost ( ) < 0 ) ;
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
2011-12-14 00:35:28 +03:00
if ( ! pendingAnims . size ( ) & & ! active )
activate ( ) ;
2012-04-17 17:50:23 +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
2011-12-14 00:35:28 +03:00
delete spellToCast ;
2013-06-26 14:18:27 +03:00
spellToCast = nullptr ;
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 )
{
possibleActions + = MOVE_TACTICS , CHOOSE_TACTICS_STACK ;
}
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
{
2012-04-28 16:01:39 +03:00
spell = CGI - > spellh - > spells [ spellBonus - > subtype ] ;
if ( spell - > isRisingSpell ( ) )
2012-04-17 11:46:09 +03:00
{
2012-04-28 16:01:39 +03:00
possibleActions . push_back ( RISING_SPELL ) ;
}
//possibleActions.push_back (NO_LOCATION);
//possibleActions.push_back (ANY_LOCATION);
2012-05-07 19:04:43 +03:00
//TODO: allow stacks cast aimed spells
2012-04-28 16:01:39 +03:00
//possibleActions.push_back (OTHER_SPELL);
else
{
switch ( spellBonus - > subtype )
{
2013-02-11 02:24:57 +03:00
case SpellID : : REMOVE_OBSTACLE :
2012-04-28 16:01:39 +03:00
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
2011-12-14 00:35:28 +03:00
void CBattleInterface : : showAliveStack ( const CStack * stack , SDL_Surface * to )
{
int ID = stack - > ID ;
if ( creAnims . find ( ID ) = = creAnims . end ( ) ) //eg. for summoned but not yet handled stacks
2011-02-14 22:31:53 +02:00
return ;
2012-02-16 20:10:58 +03:00
2013-07-07 22:44:08 +03:00
creAnims [ ID ] - > nextFrame ( to , creDir [ ID ] ) ;
2013-07-06 19:10:20 +03:00
creAnims [ ID ] - > incrementFrame ( float ( GH . mainFPSmng - > getElapsedMilliseconds ( ) ) / 1000 ) ;
2009-09-24 16:23:52 +03:00
2011-12-14 00:35:28 +03:00
//printing amount
if ( stack - > count > 0 //don't print if stack is not alive
& & ( ! curInt - > curAction
| | ( curInt - > curAction - > stackNumber ! = ID //don't print if stack is currently taking an action
2013-02-04 00:05:44 +03:00
& & ( curInt - > curAction - > actionType ! = Battle : : WALK_AND_ATTACK | | stack - > position ! = curInt - > curAction - > additionalInfo ) //nor if it's an object of attack
2011-12-14 00:35:28 +03:00
& & ( curInt - > curAction - > destinationTile ! = stack - > position ) //nor if it's on destination tile for current action
)
)
& & ! stack - > hasBonusOfType ( Bonus : : SIEGE_WEAPON ) //and not a war machine...
)
2009-09-24 16:23:52 +03:00
{
2011-12-22 16:05:19 +03:00
const BattleHex nextPos = stack - > position + ( stack - > attackerOwned ? 1 : - 1 ) ;
2011-12-14 00:35:28 +03:00
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
2013-06-26 14:18:27 +03:00
SDL_Surface * amountBG = nullptr ;
2011-12-14 00:35:28 +03:00
TBonusListPtr spellEffects = stack - > getSpellBonuses ( ) ;
if ( ! spellEffects - > size ( ) )
{
amountBG = amountNormal ;
}
2009-09-24 16:23:52 +03:00
else
2011-12-14 00:35:28 +03:00
{
int pos = 0 ; //determining total positiveness of effects
std : : vector < si32 > spellIds = stack - > activeSpells ( ) ;
2013-06-29 16:05:48 +03:00
for ( auto & spellId : spellIds )
2011-12-14 00:35:28 +03:00
{
2013-06-29 16:05:48 +03:00
pos + = CGI - > spellh - > spells [ spellId ] - > positiveness ;
2011-12-14 00:35:28 +03:00
}
if ( pos > 0 )
{
amountBG = amountPositive ;
}
else if ( pos < 0 )
{
amountBG = amountNegative ;
}
else
{
amountBG = amountEffNeutral ;
}
}
SDL_Rect temp_rect = genRect ( amountNormal - > h , amountNormal - > w , creAnims [ ID ] - > pos . x + xAdd , creAnims [ ID ] - > pos . y + yAdd ) ;
2013-06-26 14:18:27 +03:00
SDL_BlitSurface ( amountBG , nullptr , to , & temp_rect ) ;
2011-12-14 00:35:28 +03:00
//blitting amount
2012-12-19 20:24:53 +03:00
Point textPos ( creAnims [ ID ] - > pos . x + xAdd + 15 , creAnims [ ID ] - > pos . y + yAdd + 5 ) ;
graphics - > fonts [ FONT_TINY ] - > renderTextCenter ( to , makeNumberShort ( stack - > count ) , Colors : : WHITE , textPos ) ;
2010-03-07 19:40:33 +02:00
}
2009-09-24 16:23:52 +03:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : showPieceOfWall ( SDL_Surface * to , int hex , const std : : vector < const CStack * > & stacks )
2011-02-24 15:57:47 +02:00
{
2011-12-14 00:35:28 +03:00
if ( ! siegeH )
return ;
2011-02-24 15:57:47 +02:00
2012-02-16 20:10:58 +03:00
# ifdef CPP11_USE_INITIALIZERS_LIST
//note - std::list<int> must be specified to avoid type deduction by gcc (may not work in other compilers)
static const std : : map < int , std : : list < int > > hexToPart = {
{ 12 , std : : list < int > { 8 , 1 , 7 } } , { 45 , std : : list < int > { 12 , 6 } } ,
{ 101 , std : : list < int > { 10 } } , { 118 , std : : list < int > { 2 } } ,
{ 165 , std : : list < int > { 11 } } , { 186 , std : : list < int > { 3 } } } ;
# else
2012-06-02 18:16:54 +03:00
using namespace boost : : assign ;
2012-09-24 02:10:56 +03:00
std : : map < int , std : : list < int > > hexToPart ;
hexToPart [ 12 ] = list_of < int > ( 8 ) ( 1 ) ( 7 ) ;
hexToPart [ 45 ] = list_of < int > ( 12 ) ( 6 ) ;
hexToPart [ 101 ] = list_of < int > ( 10 ) ;
hexToPart [ 118 ] = list_of < int > ( 2 ) ;
hexToPart [ 165 ] = list_of < int > ( 11 ) ;
hexToPart [ 186 ] = list_of < int > ( 3 ) ;
2012-02-16 20:10:58 +03:00
# endif
2012-09-24 02:10:56 +03:00
2013-06-29 16:05:48 +03:00
auto it = hexToPart . find ( hex ) ;
2011-12-14 00:35:28 +03:00
if ( it ! = hexToPart . end ( ) )
2009-01-15 19:01:08 +02:00
{
2013-06-29 16:05:48 +03:00
for ( int wallNum : it - > second )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
siegeH - > printPartOfWall ( to , wallNum ) ;
//print creature in turret
int posToSeek = - 1 ;
switch ( wallNum )
{
case 3 : //bottom turret
posToSeek = - 3 ;
break ;
case 8 : //upper turret
posToSeek = - 4 ;
break ;
case 2 : //keep
posToSeek = - 2 ;
break ;
}
if ( posToSeek ! = - 1 )
2009-01-15 19:01:08 +02:00
{
2013-06-26 14:18:27 +03:00
const CStack * turret = nullptr ;
2011-12-14 00:35:28 +03:00
2013-06-29 16:05:48 +03:00
for ( const CStack * s : stacks )
2009-02-28 18:44:56 +02:00
{
2011-12-14 00:35:28 +03:00
if ( s - > position = = posToSeek )
{
turret = s ;
break ;
}
2009-02-28 18:44:56 +02:00
}
2011-12-14 00:35:28 +03:00
if ( turret )
2009-02-09 16:50:32 +02:00
{
2011-12-14 00:35:28 +03:00
showAliveStack ( turret , to ) ;
//blitting creature cover
switch ( posToSeek )
{
case - 3 : //bottom turret
siegeH - > printPartOfWall ( to , 16 ) ;
break ;
case - 4 : //upper turret
siegeH - > printPartOfWall ( to , 17 ) ;
break ;
case - 2 : //keep
siegeH - > printPartOfWall ( to , 15 ) ;
break ;
}
2009-02-09 16:50:32 +02:00
}
2011-12-14 00:35:28 +03:00
2009-02-09 16:50:32 +02:00
}
2009-01-15 19:01:08 +02:00
}
}
2011-12-14 00:35:28 +03:00
// Damaged wall below gate have to be drawn earlier than a non-damaged wall below gate.
if ( ( hex = = 112 & & curInt - > cb - > battleGetWallState ( 3 ) = = 3 ) | | ( hex = = 147 & & curInt - > cb - > battleGetWallState ( 3 ) ! = 3 ) )
siegeH - > printPartOfWall ( to , 5 ) ;
// Damaged bottom wall have to be drawn earlier than a non-damaged bottom wall.
if ( ( hex = = 165 & & curInt - > cb - > battleGetWallState ( 4 ) = = 3 ) | | ( hex = = 185 & & curInt - > cb - > battleGetWallState ( 4 ) ! = 3 ) )
siegeH - > printPartOfWall ( to , 4 ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : redrawBackgroundWithHexes ( const CStack * activeStack )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
attackableHexes . clear ( ) ;
if ( activeStack )
occupyableHexes = curInt - > cb - > battleGetAvailableHexes ( activeStack , true , & attackableHexes ) ;
2012-01-12 18:23:00 +03:00
curInt - > cb - > battleGetStackCountOutsideHexes ( stackCountOutsideHexes ) ;
2011-12-14 00:35:28 +03:00
//preparating background graphic with hexes and shaded hexes
blitAt ( background , 0 , 0 , backgroundWithHexes ) ;
2012-04-23 22:56:37 +03:00
//draw absolute obstacles (cliffs and so on)
2013-06-29 16:05:48 +03:00
for ( auto & oi : curInt - > cb - > battleGetAllObstacles ( ) )
2012-05-18 23:50:16 +03:00
{
if ( oi - > obstacleType = = CObstacleInstance : : ABSOLUTE_OBSTACLE /* || oi.obstacleType == CObstacleInstance::MOAT*/ )
blitAt ( imageOfObstacle ( * oi ) , oi - > getInfo ( ) . width , oi - > getInfo ( ) . height , backgroundWithHexes ) ;
}
2012-04-23 22:56:37 +03:00
2012-01-12 18:23:00 +03:00
if ( settings [ " battle " ] [ " cellBorders " ] . Bool ( ) )
2013-06-26 14:18:27 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellBorders , nullptr , backgroundWithHexes , nullptr ) ;
2009-09-19 12:57:37 +03:00
2012-01-12 18:23:00 +03:00
if ( settings [ " battle " ] [ " stackRange " ] . Bool ( ) )
2009-01-15 19:01:08 +02:00
{
2011-12-22 16:05:19 +03:00
std : : vector < BattleHex > hexesToShade = occupyableHexes ;
2011-12-14 00:35:28 +03:00
hexesToShade . insert ( hexesToShade . end ( ) , attackableHexes . begin ( ) , attackableHexes . end ( ) ) ;
2013-06-29 16:05:48 +03:00
for ( BattleHex hex : hexesToShade )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
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 ) ;
2013-06-26 14:18:27 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( cellShade , nullptr , backgroundWithHexes , & temp_rect ) ;
2009-01-15 19:01:08 +02:00
}
}
}
2011-12-14 00:35:28 +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
{
2012-05-18 23:50:16 +03:00
char tabh [ 200 ] = { 0 } ;
2011-12-14 00:35:28 +03:00
int end = 0 ;
if ( attacker ) //ignore if stacks were killed by spell
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
end = sprintf ( tabh , CGI - > generaltexth - > allTexts [ attacker - > count > 1 ? 377 : 376 ] . c_str ( ) ,
( attacker - > count > 1 ? attacker - > getCreature ( ) - > namePl . c_str ( ) : attacker - > getCreature ( ) - > nameSing . c_str ( ) ) , dmg ) ;
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
{
2011-12-14 00:35:28 +03:00
if ( killed > 1 )
{
sprintf ( tabh + end , CGI - > generaltexth - > allTexts [ 379 ] . c_str ( ) , killed ,
multiple ? CGI - > generaltexth - > allTexts [ 43 ] . c_str ( ) : defender - > getCreature ( ) - > namePl . c_str ( ) ) ; // creatures perish
}
else //killed == 1
{
2012-02-16 20:10:58 +03:00
sprintf ( tabh + end , CGI - > generaltexth - > allTexts [ 378 ] . c_str ( ) ,
2011-12-14 00:35:28 +03:00
multiple ? CGI - > generaltexth - > allTexts [ 42 ] . c_str ( ) : defender - > getCreature ( ) - > nameSing . c_str ( ) ) ; // creature perishes
}
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
console - > addText ( std : : string ( tabh ) ) ;
2009-01-15 19:01:08 +02:00
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : projectileShowHelper ( SDL_Surface * to )
2009-01-15 19:01:08 +02:00
{
2013-06-26 14:18:27 +03:00
if ( to = = nullptr )
2011-12-14 00:35:28 +03:00
to = screen ;
2011-12-22 16:05:19 +03:00
std : : list < std : : list < ProjectileInfo > : : iterator > toBeDeleted ;
2013-07-06 19:10:20 +03:00
for ( auto it = projectiles . begin ( ) ; it ! = projectiles . end ( ) ; + + it )
2009-09-06 12:13:16 +03:00
{
2013-07-06 19:10:20 +03:00
// Check if projectile is already visible (shooter animation did the shot)
if ( ! it - > shotDone )
2009-09-06 12:13:16 +03:00
{
2013-07-06 19:10:20 +03:00
if ( creAnims [ it - > stackID ] - > getCurrentFrame ( ) > it - > animStartDelay )
{
//at this point projectile should become visible
creAnims [ it - > stackID ] - > pause ( ) ; // pause animation
it - > shotDone = true ;
}
2011-12-14 00:35:28 +03:00
else
2013-07-06 19:10:20 +03:00
continue ; // wait...
2009-09-06 12:13:16 +03:00
}
2011-12-14 00:35:28 +03:00
SDL_Rect dst ;
dst . h = idToProjectile [ it - > creID ] - > ourImages [ it - > frameNum ] . bitmap - > h ;
dst . w = idToProjectile [ it - > creID ] - > ourImages [ it - > frameNum ] . bitmap - > w ;
2013-04-04 16:18:38 +03:00
dst . x = it - > x - dst . w / 2 ;
dst . y = it - > y - dst . h / 2 ;
2012-02-16 20:10:58 +03:00
2011-12-14 00:35:28 +03:00
if ( it - > reverse )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
SDL_Surface * rev = CSDL_Ext : : rotate01 ( idToProjectile [ it - > creID ] - > ourImages [ it - > frameNum ] . bitmap ) ;
2013-06-26 14:18:27 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( rev , nullptr , to , & dst ) ;
2011-12-14 00:35:28 +03:00
SDL_FreeSurface ( rev ) ;
2009-01-15 19:01:08 +02:00
}
else
{
2013-06-26 14:18:27 +03:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( idToProjectile [ it - > creID ] - > ourImages [ it - > frameNum ] . bitmap , nullptr , to , & dst ) ;
2011-12-14 00:35:28 +03:00
}
// Update projectile
+ + it - > step ;
if ( it - > step = = it - > lastStep )
{
toBeDeleted . insert ( toBeDeleted . end ( ) , it ) ;
2009-09-21 11:29:41 +03:00
}
2011-12-14 00:35:28 +03:00
else
2009-09-21 11:29:41 +03:00
{
2011-12-14 00:35:28 +03:00
if ( it - > catapultInfo )
2009-09-21 11:29:41 +03:00
{
2011-12-14 00:35:28 +03:00
// Parabolic shot of the trajectory, as follows: f(x) = ax^2 + bx + c
it - > x + = it - > dx ;
2013-04-04 21:20:25 +03:00
it - > y = it - > catapultInfo - > calculateY ( it - > x ) ;
2013-04-04 16:18:38 +03:00
+ + ( it - > frameNum ) ;
it - > frameNum % = idToProjectile [ it - > creID ] - > ourImages . size ( ) ;
2009-09-21 11:29:41 +03:00
}
else
{
2011-12-14 00:35:28 +03:00
// Normal projectile, just add the calculated "deltas" to the x and y positions.
it - > x + = it - > dx ;
it - > y + = it - > dy ;
2009-09-21 11:29:41 +03:00
}
2009-01-15 19:01:08 +02:00
}
}
2013-06-29 16:05:48 +03:00
for ( auto & elem : toBeDeleted )
2009-01-15 19:01:08 +02:00
{
2013-07-06 19:10:20 +03:00
// resume animation
creAnims [ elem - > stackID ] - > play ( ) ;
2013-06-29 16:05:48 +03:00
projectiles . erase ( elem ) ;
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 : : 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
bTacticNextStack ( stack ) ;
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... " ;
activate ( ) ;
}
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
}
2011-12-14 00:35:28 +03:00
void CBattleInterface : : startAction ( const BattleAction * action )
2009-01-15 19:01:08 +02:00
{
2013-07-06 19:10:20 +03:00
setActiveStack ( nullptr ) ;
setHoveredStack ( nullptr ) ;
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 ) ;
bDefence - > block ( false ) ;
bWait - > block ( false ) ;
if ( active )
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
if ( btactEnd & & btactNext ) //if the other side had tactics, there are no buttons
2009-01-15 19:01:08 +02:00
{
2011-12-14 00:35:28 +03:00
btactEnd - > deactivate ( ) ;
btactNext - > deactivate ( ) ;
bConsoleDown - > activate ( ) ;
bConsoleUp - > activate ( ) ;
2009-01-15 19:01:08 +02:00
}
}
2011-12-14 00:35:28 +03:00
redraw ( ) ;
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
{
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
const CStack * stack = curInt - > cb - > battleGetStackByID ( action - > stackNumber ) ;
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
2011-12-14 00:35:28 +03:00
if ( active )
deactivate ( ) ;
char txt [ 400 ] ;
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 )
{
sprintf ( txt , CGI - > generaltexth - > allTexts [ txtid ] . c_str ( ) , ( stack - > count ! = 1 ) ? stack - > getCreature ( ) - > namePl . c_str ( ) : stack - > getCreature ( ) - > nameSing . c_str ( ) , 0 ) ;
console - > addText ( txt ) ;
}
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 ) ;
2011-12-14 00:35:28 +03:00
btactEnd - > block ( true ) ;
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 ) ;
stacksOfMine . erase ( std : : remove_if ( stacksOfMine . begin ( ) , stacksOfMine . end ( ) , & immobile ) , stacksOfMine . end ( ) ) ;
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
// This function handles mouse move over hexes and l-clicking on them.
// First we decide what happens if player clicks on this hex and set appropriately
// consoleMsg, cursorFrame/Type and prepare lambda realizeAction.
//
// 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?
2012-03-31 00:36:07 +03:00
//used when l-clicking -> action to be called upon the click
std : : function < void ( ) > realizeAction ;
//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 )
{
2012-05-26 14:01:31 +03:00
//opening creature window shouldn't affect myTurn...
if ( currentAction ! = CREATURE_INFO )
{
myTurn = false ; //tends to crash with empty calls
}
2012-03-31 00:36:07 +03:00
realizeAction ( ) ;
CCS - > curh - > changeGraphic ( ECursor : : COMBAT , ECursor : : COMBAT_POINTER ) ;
this - > console - > alterText ( " " ) ;
}
} ;
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 ;
2012-04-17 11:46: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
{
2012-04-17 11:46:09 +03:00
bool legalAction = false ; //this action is legal and can't be performed
2012-04-18 16:24:18 +03:00
bool notLegal = false ; //this action is not legal and should display message
2012-04-17 11:46:09 +03:00
2012-04-16 20:12:39 +03:00
switch ( action )
{
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
{
2012-04-18 12:01:08 +03:00
if ( shere & & ! ourStack & & shere - > alive ( ) )
2012-04-17 17:50:23 +03:00
{
2012-04-18 12:01:08 +03:00
if ( isTileAttackable ( myNumber ) )
{
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 ;
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 :
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 ;
case RISING_SPELL :
2012-04-28 22:40:27 +03:00
if ( shere & & shere - > canBeHealed ( ) & & ourStack & & isCastingPossibleHere ( sactive , shere , myNumber ) ) //TODO: at least one stack has to be raised by resurrection / animate dead
2012-04-16 20:12:39 +03:00
legalAction = true ;
break ;
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 )
2013-02-11 02:24:57 +03:00
skill = sactive - > valOfBonuses ( Selector : : typeSubtype ( Bonus : : SPELLCASTER , SpellID : : TELEPORT ) ) ;
2012-04-18 12:01:08 +03:00
else
skill = getActiveHero ( ) - > getSpellSchoolLevel ( CGI - > spellh - > spells [ spellToCast - > additionalInfo ] ) ;
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
{
2013-02-11 23:08:53 +03:00
if ( curInt - > cb - > battleGetStackByPos ( hex , false ) | | ! ! 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 ( ) )
legalAction = true ;
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 ;
else if ( localActions . size ( ) ) //if not possible, select first avaliable action 9they are sorted by suggested priority)
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 :
2012-04-16 20:12:39 +03:00
case RISING_SPELL :
2012-04-18 18:57:49 +03:00
sp = CGI - > spellh - > spells [ creatureCasting ? creatureSpellToCast : spellToCast - > additionalInfo ] ; //necessary if creature has random Genie spell at same time
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 :
2012-04-28 22:40:27 +03:00
selectedStack = shere ; //remember firts 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 :
sp = CGI - > spellh - > spells [ 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 ;
2012-04-18 18:57:49 +03:00
case RANDOM_GENIE_SPELL : //we assume that teleport / sacrifice will never be avaliable 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
isCastingPossible = true ;
break ;
case OBSTACLE :
consoleMsg = CGI - > generaltexth - > allTexts [ 550 ] ;
isCastingPossible = true ;
2012-04-28 18:18:21 +03:00
break ;
2012-04-28 22:40:27 +03:00
case SACRIFICE :
cursorFrame = ECursor : : COMBAT_SACRIFICE ;
consoleMsg = ( boost : : format ( CGI - > generaltexth - > allTexts [ 549 ] ) % shere - > getName ( ) ) . str ( ) ; //sacrifice the %s
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 ;
2013-02-04 00:05:44 +03: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 ( ) ;
realizeAction = [ = ] { GH . pushInt ( createCreWindow ( shere , true ) ) ; } ;
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
{
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 RISING_SPELL :
case RANDOM_GENIE_SPELL :
cursorFrame = ECursor : : COMBAT_BLOCKED ;
consoleMsg = CGI - > generaltexth - > allTexts [ 23 ] ;
break ;
case TELEPORT :
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
{
2012-04-16 20:12:39 +03:00
cursorType = ECursor : : SPELLBOOK ;
cursorFrame = 0 ;
if ( consoleMsg . empty ( ) & & sp )
consoleMsg = boost : : str ( boost : : format ( CGI - > generaltexth - > allTexts [ 26 ] ) % sp - > name ) ; //Cast %s
2012-04-17 11:46: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
{
2013-02-11 02:24:57 +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
}
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
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 ;
2012-04-16 20:12:39 +03:00
if ( spellID > = 0 )
sp = CGI - > spellh - > spells [ spellID ] ;
2012-04-22 16:28:46 +03:00
if ( sp )
2012-04-16 20:12:39 +03:00
{
if ( creatureCasting )
isCastingPossible = ( curInt - > cb - > battleCanCreatureCastThisSpell ( sp , myNumber ) = = ESpellCastProblem : : OK ) ;
else
isCastingPossible = ( curInt - > cb - > battleCanCastThisSpell ( sp , myNumber ) = = ESpellCastProblem : : OK ) ;
}
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
SDL_Surface * CBattleInterface : : imageOfObstacle ( const CObstacleInstance & oi ) const
{
2013-07-06 19:10:20 +03:00
int frameIndex = ( animCount + 1 ) * 25 / getAnimSpeed ( ) ;
2012-05-05 00:16:39 +03:00
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 ;
2012-05-18 23:50:16 +03:00
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 ;
}
2012-09-17 20:25:54 +03:00
case CObstacleInstance : : MOAT : //moat is blitted by SiegeHelper, this shouldn't be called
2012-05-05 00:16:39 +03:00
default :
assert ( 0 ) ;
2012-09-17 20:25:54 +03:00
return nullptr ;
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
Point whereTo = whereToBlitObstacleImage ( imageOfObstacle ( oi ) , oi ) ;
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
}
Point CBattleInterface : : whereToBlitObstacleImage ( SDL_Surface * image , const CObstacleInstance & obstacle ) const
{
int offset = image - > h % 42 ;
if ( obstacle . obstacleType = = CObstacleInstance : : USUAL )
{
2012-05-26 14:01:31 +03:00
if ( obstacle . getInfo ( ) . blockedTiles . front ( ) < 0 | | offset > 37 ) //second or part is for holy ground ID=62,65,63
2012-05-05 00:16:39 +03:00
offset - = 42 ;
}
else if ( obstacle . obstacleType = = CObstacleInstance : : QUICKSAND )
{
offset - = 42 ;
}
Rect r = hexPosition ( obstacle . pos ) ;
r . y + = 42 - image - > h + offset ;
return r . topLeft ( ) ;
}
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
2013-06-24 17:35:27 +03:00
boost : : thread aiThread ( [ & ]
2013-06-23 00:47:51 +03:00
{
2013-06-29 16:05:48 +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
{
givenCommand - > setn ( ba ) ;
}
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
} ) ;
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 )
2010-10-31 00:53:41 +03: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
{
2009-09-01 16:54:13 +03: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
}
}
std : : string CBattleInterface : : SiegeHelper : : getSiegeName ( ui16 what , ui16 additInfo ) const
{
2009-09-01 16:54:13 +03:00
if ( what = = 2 | | what = = 3 | | what = = 8 )
2012-10-05 21:03:49 +03:00
vstd : : amin ( additInfo , 2 ) ;
else
vstd : : amin ( additInfo , 3 ) ;
std : : string & prefix = town - > town - > clientInfo . siegePrefix ;
std : : string addit = boost : : lexical_cast < std : : string > ( additInfo ) ;
2009-08-26 17:09:55 +03:00
switch ( what )
{
case 0 : //background
2012-10-05 21:03:49 +03:00
return prefix + " BACK.BMP " ;
2009-08-26 17:09:55 +03:00
case 1 : //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
{
case 5 : case 4 : case 1 : case 6 :
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
}
}
2009-08-26 17:09:55 +03:00
case 2 : //keep
2012-10-05 21:03:49 +03:00
return prefix + " MAN " + addit + " .BMP " ;
2009-08-26 17:09:55 +03:00
case 3 : //bottom tower
2012-10-05 21:03:49 +03:00
return prefix + " TW1 " + addit + " .BMP " ;
2009-08-26 17:09:55 +03:00
case 4 : //bottom wall
2012-10-05 21:03:49 +03:00
return prefix + " WA1 " + addit + " .BMP " ;
2009-08-26 17:09:55 +03:00
case 5 : //below gate
2012-10-05 21:03:49 +03:00
return prefix + " WA3 " + addit + " .BMP " ;
2009-08-26 17:09:55 +03:00
case 6 : //over gate
2012-10-05 21:03:49 +03:00
return prefix + " WA4 " + addit + " .BMP " ;
2009-08-26 17:09:55 +03:00
case 7 : //upper wall
2012-10-05 21:03:49 +03:00
return prefix + " WA6 " + addit + " .BMP " ;
2009-08-26 17:09:55 +03:00
case 8 : //upper tower
2012-10-05 21:03:49 +03:00
return prefix + " TW2 " + addit + " .BMP " ;
2009-08-26 17:09:55 +03:00
case 9 : //gate
2012-10-05 21:03:49 +03:00
return prefix + " DRW " + addit + " .BMP " ;
2009-08-26 17:09:55 +03:00
case 10 : //gate arch
2012-10-05 21:03:49 +03:00
return prefix + " ARCH.BMP " ;
2009-08-26 17:09:55 +03:00
case 11 : //bottom static wall
2012-10-05 21:03:49 +03:00
return prefix + " WA2.BMP " ;
2009-08-26 17:09:55 +03:00
case 12 : //upper static wall
2012-10-05 21:03:49 +03:00
return prefix + " WA5.BMP " ;
2009-09-02 17:10:19 +03:00
case 13 : //moat
2012-10-05 21:03:49 +03:00
return prefix + " MOAT.BMP " ;
2009-09-02 17:10:19 +03:00
case 14 : //mlip
2012-10-05 21:03:49 +03:00
return prefix + " MLIP.BMP " ;
2009-09-04 17:11:42 +03:00
case 15 : //keep creature cover
2012-10-05 21:03:49 +03:00
return prefix + " MANC.BMP " ;
2009-09-04 17:11:42 +03:00
case 16 : //bottom turret creature cover
2012-10-05 21:03:49 +03:00
return prefix + " TW1C.BMP " ;
2009-09-04 17:11:42 +03:00
case 17 : //upper turret creature cover
2012-10-05 21:03:49 +03:00
return prefix + " TW2C.BMP " ;
2009-08-26 17:09:55 +03:00
default :
return " " ;
}
}
2012-02-16 20:10:58 +03:00
/// What: 1. background wall, 2. keep, 3. bottom tower, 4. bottom wall, 5. wall below gate,
/// 6. wall over gate, 7. upper wall, 8. upper tower, 9. gate, 10. gate arch, 11. bottom static wall, 12. upper static wall, 13. moat, 14. mlip,
2011-04-30 22:52:35 +03:00
/// 15. keep turret cover, 16. lower turret cover, 17. upper turret cover
2009-08-26 17:09:55 +03:00
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 ) ;
2012-02-16 20:10:58 +03:00
2011-04-30 22:52:35 +03:00
if ( what > = 1 & & what < = 17 )
2009-08-26 17:09:55 +03:00
{
2012-10-05 21:03:49 +03:00
pos . x = owner - > siegeH - > town - > town - > clientInfo . siegePositions [ what ] . x + owner - > pos . x ;
pos . y = owner - > siegeH - > town - > town - > clientInfo . siegePositions [ what ] . y + owner - > pos . y ;
2011-04-30 22:52:35 +03:00
}
2009-08-26 17:09:55 +03:00
if ( pos . x ! = - 1 )
{
2009-09-01 16:54:13 +03:00
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
}