2022-11-17 23:57:51 +02:00
/*
2022-12-11 23:16:23 +02:00
* BattleFieldController . cpp , part of VCMI engine
2022-11-17 23:57:51 +02:00
*
* 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
*
*/
# include "StdInc.h"
2022-12-09 13:38:46 +02:00
# include "BattleFieldController.h"
2022-11-28 16:43:38 +02:00
2022-12-09 13:38:46 +02:00
# include "BattleInterface.h"
# include "BattleActionsController.h"
# include "BattleInterfaceClasses.h"
# include "BattleEffectsController.h"
# include "BattleSiegeController.h"
# include "BattleStacksController.h"
# include "BattleObstacleController.h"
# include "BattleProjectileController.h"
# include "BattleRenderer.h"
2022-11-28 16:43:38 +02:00
2022-11-17 23:57:51 +02:00
# include "../CGameInfo.h"
2022-11-28 16:43:38 +02:00
# include "../CPlayerInterface.h"
2023-02-01 20:42:06 +02:00
# include "../render/Canvas.h"
# include "../render/IImage.h"
2023-02-21 15:44:18 +02:00
# include "../renderSDL/SDL_Extensions.h"
2022-11-17 23:57:51 +02:00
# include "../gui/CGuiHandler.h"
2023-01-05 19:34:37 +02:00
# include "../gui/CursorHandler.h"
2023-02-01 20:42:06 +02:00
# include "../adventureMap/CInGameConsole.h"
2022-11-28 16:43:38 +02:00
# include "../../CCallback.h"
2022-11-17 23:57:51 +02:00
# include "../../lib/BattleFieldHandler.h"
# include "../../lib/CConfigHandler.h"
# include "../../lib/CStack.h"
# include "../../lib/spells/ISpellMechanics.h"
2022-12-13 13:58:16 +02:00
BattleFieldController : : BattleFieldController ( BattleInterface & owner ) :
2022-12-19 01:12:26 +02:00
owner ( owner )
2022-11-17 23:57:51 +02:00
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE ;
2022-12-21 17:02:53 +02:00
strongInterest = true ;
2022-11-17 23:57:51 +02:00
//preparing cells and hexes
2023-02-21 15:44:18 +02:00
cellBorder = IImage : : createFromFile ( " CCELLGRD.BMP " , EImageBlitMode : : COLORKEY ) ;
2022-11-25 11:46:47 +02:00
cellShade = IImage : : createFromFile ( " CCELLSHD.BMP " ) ;
2023-04-04 22:34:56 +03:00
cellUnitMovementHighlight = IImage : : createFromFile ( " UnitMovementHighlight.PNG " , EImageBlitMode : : COLORKEY ) ;
cellUnitMaxMovementHighlight = IImage : : createFromFile ( " UnitMaxMovementHighlight.PNG " , EImageBlitMode : : COLORKEY ) ;
2022-11-17 23:57:51 +02:00
2022-12-13 13:58:16 +02:00
if ( ! owner . siegeController )
2022-11-17 23:57:51 +02:00
{
2022-12-13 13:58:16 +02:00
auto bfieldType = owner . curInt - > cb - > battleGetBattlefieldType ( ) ;
2022-11-17 23:57:51 +02:00
if ( bfieldType = = BattleField : : NONE )
logGlobal - > error ( " Invalid battlefield returned for current battle " ) ;
else
2023-02-21 15:44:18 +02:00
background = IImage : : createFromFile ( bfieldType . getInfo ( ) - > graphics , EImageBlitMode : : OPAQUE ) ;
2022-11-17 23:57:51 +02:00
}
else
{
2022-12-13 13:58:16 +02:00
std : : string backgroundName = owner . siegeController - > getBattleBackgroundName ( ) ;
2023-02-21 15:44:18 +02:00
background = IImage : : createFromFile ( backgroundName , EImageBlitMode : : OPAQUE ) ;
2022-11-17 23:57:51 +02:00
}
2023-02-21 15:44:18 +02:00
2022-12-21 17:02:53 +02:00
pos . w = background - > width ( ) ;
pos . h = background - > height ( ) ;
2022-11-17 23:57:51 +02:00
2022-12-11 22:09:57 +02:00
backgroundWithHexes = std : : make_unique < Canvas > ( Point ( background - > width ( ) , background - > height ( ) ) ) ;
2022-11-17 23:57:51 +02:00
2023-03-29 13:03:40 +03:00
updateAccessibleHexes ( ) ;
2023-05-13 23:38:57 +03:00
addUsedEvents ( LCLICK | RCLICK | MOVE | TIME ) ;
2023-05-13 18:00:14 +03:00
}
void BattleFieldController : : activate ( )
{
2022-12-21 17:02:53 +02:00
LOCPLINT - > cingconsole - > pos = this - > pos ;
2023-05-13 18:00:14 +03:00
CIntObject : : activate ( ) ;
2022-12-21 17:02:53 +02:00
}
void BattleFieldController : : createHeroes ( )
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE ;
// create heroes as part of our constructor for correct positioning inside battlefield
if ( owner . attackingHeroInstance )
owner . attackingHero = std : : make_shared < BattleHero > ( owner , owner . attackingHeroInstance , false ) ;
if ( owner . defendingHeroInstance )
owner . defendingHero = std : : make_shared < BattleHero > ( owner , owner . defendingHeroInstance , true ) ;
}
2023-02-02 15:49:23 +02:00
void BattleFieldController : : mouseMoved ( const Point & cursorPosition )
2022-12-21 17:02:53 +02:00
{
2023-02-02 15:49:23 +02:00
if ( ! pos . isInside ( cursorPosition ) )
2023-01-29 17:52:19 +02:00
{
owner . actionsController - > onHoverEnded ( ) ;
return ;
}
2022-12-21 17:02:53 +02:00
2023-01-29 17:52:19 +02:00
BattleHex selectedHex = getHoveredHex ( ) ;
2023-01-21 19:42:44 +02:00
owner . actionsController - > onHexHovered ( selectedHex ) ;
2022-11-17 23:57:51 +02:00
}
2023-01-29 14:34:47 +02:00
void BattleFieldController : : clickLeft ( tribool down , bool previousState )
{
if ( ! down )
{
BattleHex selectedHex = getHoveredHex ( ) ;
if ( selectedHex ! = BattleHex : : INVALID )
2023-01-29 17:52:19 +02:00
owner . actionsController - > onHexLeftClicked ( selectedHex ) ;
2023-01-29 14:34:47 +02:00
}
}
void BattleFieldController : : clickRight ( tribool down , bool previousState )
{
if ( down )
{
BattleHex selectedHex = getHoveredHex ( ) ;
2023-01-29 17:52:19 +02:00
if ( selectedHex ! = BattleHex : : INVALID )
owner . actionsController - > onHexRightClicked ( selectedHex ) ;
2023-01-29 14:34:47 +02:00
}
}
2022-12-21 17:02:53 +02:00
2022-12-11 22:09:57 +02:00
void BattleFieldController : : renderBattlefield ( Canvas & canvas )
2022-12-02 17:49:38 +02:00
{
2023-01-05 14:16:01 +02:00
Canvas clippedCanvas ( canvas , pos ) ;
showBackground ( clippedCanvas ) ;
2022-12-02 17:49:38 +02:00
2022-12-09 13:26:17 +02:00
BattleRenderer renderer ( owner ) ;
2022-12-02 17:49:38 +02:00
2023-01-05 14:16:01 +02:00
renderer . execute ( clippedCanvas ) ;
2022-12-02 17:49:38 +02:00
2023-05-13 23:38:57 +03:00
owner . projectilesController - > render ( clippedCanvas ) ;
2022-12-02 17:49:38 +02:00
}
2022-12-11 22:09:57 +02:00
void BattleFieldController : : showBackground ( Canvas & canvas )
2022-11-27 23:42:18 +02:00
{
2023-04-27 20:43:20 +03:00
if ( owner . stacksController - > getActiveStack ( ) ! = nullptr ) //&& creAnims[stacksController->getActiveStack()->unitId()]->isIdle() //show everything with range
2022-11-27 23:42:18 +02:00
showBackgroundImageWithHexes ( canvas ) ;
else
showBackgroundImage ( canvas ) ;
showHighlightedHexes ( canvas ) ;
}
2022-12-11 22:09:57 +02:00
void BattleFieldController : : showBackgroundImage ( Canvas & canvas )
2022-11-17 23:57:51 +02:00
{
2023-01-05 14:16:01 +02:00
canvas . draw ( background , Point ( 0 , 0 ) ) ;
2022-11-25 11:46:47 +02:00
2023-01-05 14:16:01 +02:00
owner . obstacleController - > showAbsoluteObstacles ( canvas ) ;
2022-12-13 13:58:16 +02:00
if ( owner . siegeController )
2023-01-05 14:16:01 +02:00
owner . siegeController - > showAbsoluteObstacles ( canvas ) ;
2022-11-27 23:42:18 +02:00
2022-11-17 23:57:51 +02:00
if ( settings [ " battle " ] [ " cellBorders " ] . Bool ( ) )
2023-02-21 15:44:18 +02:00
{
for ( int i = 0 ; i < GameConstants : : BFIELD_SIZE ; + + i )
{
if ( i % GameConstants : : BFIELD_WIDTH = = 0 )
continue ;
if ( i % GameConstants : : BFIELD_WIDTH = = GameConstants : : BFIELD_WIDTH - 1 )
continue ;
canvas . draw ( cellBorder , hexPositionLocal ( i ) . topLeft ( ) ) ;
}
}
2022-11-17 23:57:51 +02:00
}
2022-12-11 22:09:57 +02:00
void BattleFieldController : : showBackgroundImageWithHexes ( Canvas & canvas )
2022-11-17 23:57:51 +02:00
{
2023-01-05 14:16:01 +02:00
canvas . draw ( * backgroundWithHexes . get ( ) , Point ( 0 , 0 ) ) ;
2022-11-17 23:57:51 +02:00
}
2022-12-09 13:26:17 +02:00
void BattleFieldController : : redrawBackgroundWithHexes ( )
2022-11-17 23:57:51 +02:00
{
2022-12-13 13:58:16 +02:00
const CStack * activeStack = owner . stacksController - > getActiveStack ( ) ;
2022-11-25 11:46:47 +02:00
std : : vector < BattleHex > attackableHexes ;
2023-03-22 22:51:00 +02:00
if ( activeStack )
2023-04-21 00:24:37 +03:00
occupiableHexes = owner . curInt - > cb - > battleGetAvailableHexes ( activeStack , false , true , & attackableHexes ) ;
2022-11-17 23:57:51 +02:00
2023-03-22 22:51:00 +02:00
// prepare background graphic with hexes and shaded hexes
2022-11-25 11:46:47 +02:00
backgroundWithHexes - > draw ( background , Point ( 0 , 0 ) ) ;
2023-01-05 14:16:01 +02:00
owner . obstacleController - > showAbsoluteObstacles ( * backgroundWithHexes ) ;
2023-03-22 22:51:00 +02:00
if ( owner . siegeController )
2023-01-05 14:16:01 +02:00
owner . siegeController - > showAbsoluteObstacles ( * backgroundWithHexes ) ;
2022-11-17 23:57:51 +02:00
2023-04-06 00:04:40 +03:00
// show shaded hexes for active's stack valid movement and the hexes that it can attack
2023-03-22 22:51:00 +02:00
if ( settings [ " battle " ] [ " stackRange " ] . Bool ( ) )
2022-11-17 23:57:51 +02:00
{
2023-04-06 00:04:40 +03:00
std : : vector < BattleHex > hexesToShade = occupiableHexes ;
2022-11-17 23:57:51 +02:00
hexesToShade . insert ( hexesToShade . end ( ) , attackableHexes . begin ( ) , attackableHexes . end ( ) ) ;
2023-03-22 22:51:00 +02:00
for ( BattleHex hex : hexesToShade )
2022-11-17 23:57:51 +02:00
{
2023-04-06 00:04:40 +03:00
showHighlightedHex ( * backgroundWithHexes , cellShade , hex , false ) ;
2022-11-17 23:57:51 +02:00
}
}
2023-03-22 22:51:00 +02:00
// draw cell borders
2022-11-17 23:57:51 +02:00
if ( settings [ " battle " ] [ " cellBorders " ] . Bool ( ) )
2023-02-21 15:44:18 +02:00
{
2023-03-22 22:51:00 +02:00
for ( int i = 0 ; i < GameConstants : : BFIELD_SIZE ; + + i )
2023-02-21 15:44:18 +02:00
{
2023-03-22 22:51:00 +02:00
if ( i % GameConstants : : BFIELD_WIDTH = = 0 )
2023-02-21 15:44:18 +02:00
continue ;
2023-03-22 22:51:00 +02:00
if ( i % GameConstants : : BFIELD_WIDTH = = GameConstants : : BFIELD_WIDTH - 1 )
2023-02-21 15:44:18 +02:00
continue ;
backgroundWithHexes - > draw ( cellBorder , hexPositionLocal ( i ) . topLeft ( ) ) ;
}
}
2022-11-17 23:57:51 +02:00
}
2023-04-04 22:34:56 +03:00
void BattleFieldController : : showHighlightedHex ( Canvas & canvas , std : : shared_ptr < IImage > highlight , BattleHex hex , bool darkBorder )
2022-11-17 23:57:51 +02:00
{
2023-01-05 14:16:01 +02:00
Point hexPos = hexPositionLocal ( hex ) . topLeft ( ) ;
2022-11-25 11:46:47 +02:00
2023-04-04 22:34:56 +03:00
canvas . draw ( highlight , hexPos ) ;
2023-03-22 22:51:00 +02:00
if ( ! darkBorder & & settings [ " battle " ] [ " cellBorders " ] . Bool ( ) )
canvas . draw ( cellBorder , hexPos ) ;
}
std : : set < BattleHex > BattleFieldController : : getHighlightedHexesForActiveStack ( )
2022-11-17 23:57:51 +02:00
{
2022-11-25 11:46:47 +02:00
std : : set < BattleHex > result ;
2023-03-22 22:51:00 +02:00
if ( ! owner . stacksController - > getActiveStack ( ) )
2022-11-25 11:46:47 +02:00
return result ;
2023-03-22 22:51:00 +02:00
if ( ! settings [ " battle " ] [ " stackRange " ] . Bool ( ) )
2022-11-25 11:46:47 +02:00
return result ;
auto hoveredHex = getHoveredHex ( ) ;
2022-12-13 13:58:16 +02:00
std : : set < BattleHex > set = owner . curInt - > cb - > battleGetAttackedHexes ( owner . stacksController - > getActiveStack ( ) , hoveredHex , attackingHex ) ;
2022-11-25 11:46:47 +02:00
for ( BattleHex hex : set )
result . insert ( hex ) ;
2023-03-22 22:51:00 +02:00
return result ;
}
std : : set < BattleHex > BattleFieldController : : getMovementRangeForHoveredStack ( )
{
std : : set < BattleHex > result ;
if ( ! owner . stacksController - > getActiveStack ( ) )
return result ;
2023-04-27 20:21:06 +03:00
if ( ! settings [ " battle " ] [ " movementHighlightOnHover " ] . Bool ( ) & & ! GH . isKeyboardShiftDown ( ) )
2023-03-22 22:51:00 +02:00
return result ;
auto hoveredHex = getHoveredHex ( ) ;
// add possible movement hexes for stack under mouse
2023-01-20 15:16:28 +02:00
const CStack * const hoveredStack = owner . curInt - > cb - > battleGetStackByPos ( hoveredHex , true ) ;
2023-03-23 23:03:18 +02:00
if ( hoveredStack )
2022-11-17 23:57:51 +02:00
{
2023-04-21 00:24:37 +03:00
std : : vector < BattleHex > v = owner . curInt - > cb - > battleGetAvailableHexes ( hoveredStack , true , true , nullptr ) ;
2022-11-25 11:46:47 +02:00
for ( BattleHex hex : v )
result . insert ( hex ) ;
}
return result ;
}
2023-03-22 22:51:00 +02:00
std : : set < BattleHex > BattleFieldController : : getHighlightedHexesForSpellRange ( )
2022-11-25 11:46:47 +02:00
{
std : : set < BattleHex > result ;
auto hoveredHex = getHoveredHex ( ) ;
if ( ! settings [ " battle " ] [ " mouseShadow " ] . Bool ( ) )
return result ;
2023-01-29 14:34:47 +02:00
const spells : : Caster * caster = nullptr ;
const CSpell * spell = nullptr ;
spells : : Mode mode = owner . actionsController - > getCurrentCastMode ( ) ;
2023-03-25 00:48:14 +02:00
spell = owner . actionsController - > getCurrentSpell ( hoveredHex ) ;
2023-01-29 14:34:47 +02:00
caster = owner . actionsController - > getCurrentSpellcaster ( ) ;
if ( caster & & spell ) //when casting spell
{
2022-11-25 11:46:47 +02:00
// printing shaded hex(es)
2022-12-13 13:58:16 +02:00
spells : : BattleCast event ( owner . curInt - > cb . get ( ) , caster , mode , spell ) ;
2023-03-22 22:51:00 +02:00
auto shadedHexes = spell - > battleMechanics ( & event ) - > rangeInHexes ( hoveredHex ) ;
2022-11-25 11:46:47 +02:00
2023-03-22 22:51:00 +02:00
for ( BattleHex shadedHex : shadedHexes )
2022-11-17 23:57:51 +02:00
{
2022-11-25 11:46:47 +02:00
if ( ( shadedHex . getX ( ) ! = 0 ) & & ( shadedHex . getX ( ) ! = GameConstants : : BFIELD_WIDTH - 1 ) )
result . insert ( shadedHex ) ;
2022-11-17 23:57:51 +02:00
}
}
2022-12-18 19:00:06 +02:00
return result ;
}
2023-04-21 00:24:37 +03:00
std : : set < BattleHex > BattleFieldController : : getHighlightedHexesForMovementTarget ( )
2022-12-18 19:00:06 +02:00
{
const CStack * stack = owner . stacksController - > getActiveStack ( ) ;
auto hoveredHex = getHoveredHex ( ) ;
2023-03-22 22:51:00 +02:00
if ( ! stack )
return { } ;
2022-12-18 19:00:06 +02:00
2023-04-21 00:24:37 +03:00
std : : vector < BattleHex > availableHexes = owner . curInt - > cb - > battleGetAvailableHexes ( stack , false , false , nullptr ) ;
2022-12-19 01:12:26 +02:00
2023-03-22 22:51:00 +02:00
auto hoveredStack = owner . curInt - > cb - > battleGetStackByPos ( hoveredHex , true ) ;
if ( owner . curInt - > cb - > battleCanAttack ( stack , hoveredStack , hoveredHex ) )
{
if ( isTileAttackable ( hoveredHex ) )
2022-12-18 19:00:06 +02:00
{
2023-03-22 22:51:00 +02:00
BattleHex attackFromHex = fromWhichHexAttack ( hoveredHex ) ;
if ( stack - > doubleWide ( ) )
return { attackFromHex , stack - > occupiedHex ( attackFromHex ) } ;
2022-12-19 01:12:26 +02:00
else
2023-03-22 22:51:00 +02:00
return { attackFromHex } ;
2022-12-18 19:00:06 +02:00
}
2023-03-22 22:51:00 +02:00
}
if ( vstd : : contains ( availableHexes , hoveredHex ) )
{
if ( stack - > doubleWide ( ) )
return { hoveredHex , stack - > occupiedHex ( hoveredHex ) } ;
else
return { hoveredHex } ;
}
if ( stack - > doubleWide ( ) )
{
for ( auto const & hex : availableHexes )
2022-12-18 19:00:06 +02:00
{
2023-03-22 22:51:00 +02:00
if ( stack - > occupiedHex ( hex ) = = hoveredHex )
return { hoveredHex , hex } ;
2022-12-18 19:00:06 +02:00
}
2022-11-25 11:46:47 +02:00
}
2023-03-22 22:51:00 +02:00
2022-12-19 01:12:26 +02:00
return { } ;
2022-11-25 11:46:47 +02:00
}
2022-12-11 22:09:57 +02:00
void BattleFieldController : : showHighlightedHexes ( Canvas & canvas )
2022-11-25 11:46:47 +02:00
{
2023-04-04 22:34:56 +03:00
std : : set < BattleHex > hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack ( ) ;
2023-03-22 22:51:00 +02:00
std : : set < BattleHex > hoveredSpellHexes = getHighlightedHexesForSpellRange ( ) ;
2023-04-21 00:24:37 +03:00
std : : set < BattleHex > hoveredMoveHexes = getHighlightedHexesForMovementTarget ( ) ;
2023-01-29 14:34:47 +02:00
2023-03-22 22:51:00 +02:00
if ( getHoveredHex ( ) = = BattleHex : : INVALID )
2023-01-29 14:34:47 +02:00
return ;
2023-03-22 22:51:00 +02:00
auto const & hoveredMouseHexes = owner . actionsController - > currentActionSpellcasting ( getHoveredHex ( ) ) ? hoveredSpellHexes : hoveredMoveHexes ;
2023-01-29 14:34:47 +02:00
2023-04-04 22:34:56 +03:00
for ( int hex = 0 ; hex < GameConstants : : BFIELD_SIZE ; + + hex )
2023-01-29 14:34:47 +02:00
{
2023-04-04 22:34:56 +03:00
bool stackMovement = hoveredStackMovementRangeHexes . count ( hex ) ;
bool mouse = hoveredMouseHexes . count ( hex ) ;
2022-11-25 11:46:47 +02:00
2023-04-04 22:34:56 +03:00
if ( stackMovement & & mouse ) // area where hovered stackMovement can move shown with highlight. Because also affected by mouse cursor, shade as well
2022-11-25 11:46:47 +02:00
{
2023-04-04 22:34:56 +03:00
showHighlightedHex ( canvas , cellUnitMovementHighlight , hex , false ) ;
showHighlightedHex ( canvas , cellShade , hex , true ) ;
2022-11-25 11:46:47 +02:00
}
2023-04-04 22:34:56 +03:00
if ( ! stackMovement & & mouse ) // hexes affected only at mouse cursor shown as shaded
2022-11-17 23:57:51 +02:00
{
2023-04-04 22:34:56 +03:00
showHighlightedHex ( canvas , cellShade , hex , true ) ;
2022-11-25 11:46:47 +02:00
}
2023-04-04 22:34:56 +03:00
if ( stackMovement & & ! mouse ) // hexes where hovered stackMovement can move shown with highlight
2022-11-25 11:46:47 +02:00
{
2023-04-04 22:34:56 +03:00
showHighlightedHex ( canvas , cellUnitMovementHighlight , hex , false ) ;
2022-11-17 23:57:51 +02:00
}
}
}
2022-12-09 13:26:17 +02:00
Rect BattleFieldController : : hexPositionLocal ( BattleHex hex ) const
2022-11-17 23:57:51 +02:00
{
2022-11-25 11:46:47 +02:00
int x = 14 + ( ( hex . getY ( ) ) % 2 = = 0 ? 22 : 0 ) + 44 * hex . getX ( ) ;
int y = 86 + 42 * hex . getY ( ) ;
int w = cellShade - > width ( ) ;
int h = cellShade - > height ( ) ;
2022-11-17 23:57:51 +02:00
return Rect ( x , y , w , h ) ;
}
2022-12-11 23:16:23 +02:00
Rect BattleFieldController : : hexPositionAbsolute ( BattleHex hex ) const
2022-11-25 11:46:47 +02:00
{
2022-12-21 17:02:53 +02:00
return hexPositionLocal ( hex ) + pos . topLeft ( ) ;
2022-11-25 11:46:47 +02:00
}
2022-12-09 13:26:17 +02:00
bool BattleFieldController : : isPixelInHex ( Point const & position )
2022-11-17 23:57:51 +02:00
{
2022-11-25 11:46:47 +02:00
return ! cellShade - > isTransparent ( position ) ;
2022-11-17 23:57:51 +02:00
}
2022-12-09 13:26:17 +02:00
BattleHex BattleFieldController : : getHoveredHex ( )
2022-11-17 23:57:51 +02:00
{
2023-01-29 14:34:47 +02:00
Point hoverPos = GH . getCursorPosition ( ) ;
2023-01-29 17:52:19 +02:00
if ( owner . attackingHero )
{
if ( owner . attackingHero - > pos . isInside ( hoverPos ) )
return BattleHex : : HERO_ATTACKER ;
}
if ( owner . defendingHero )
{
if ( owner . attackingHero - > pos . isInside ( hoverPos ) )
return BattleHex : : HERO_DEFENDER ;
}
2023-01-29 14:34:47 +02:00
for ( int h = 0 ; h < GameConstants : : BFIELD_SIZE ; + + h )
{
Rect hexPosition = hexPositionAbsolute ( h ) ;
if ( ! hexPosition . isInside ( hoverPos ) )
continue ;
if ( isPixelInHex ( hoverPos - hexPosition . topLeft ( ) ) )
return h ;
}
2022-11-17 23:57:51 +02:00
return BattleHex : : INVALID ;
}
2022-12-09 13:26:17 +02:00
void BattleFieldController : : setBattleCursor ( BattleHex myNumber )
2022-11-17 23:57:51 +02:00
{
2022-12-19 01:12:26 +02:00
Point cursorPos = CCS - > curh - > position ( ) ;
std : : vector < Cursor : : Combat > sectorCursor = {
Cursor : : Combat : : HIT_SOUTHEAST ,
Cursor : : Combat : : HIT_SOUTHWEST ,
Cursor : : Combat : : HIT_WEST ,
Cursor : : Combat : : HIT_NORTHWEST ,
Cursor : : Combat : : HIT_NORTHEAST ,
Cursor : : Combat : : HIT_EAST ,
Cursor : : Combat : : HIT_SOUTH ,
Cursor : : Combat : : HIT_NORTH ,
} ;
auto direction = static_cast < size_t > ( selectAttackDirection ( myNumber , cursorPos ) ) ;
assert ( direction ! = - 1 ) ;
if ( direction ! = - 1 )
CCS - > curh - > set ( sectorCursor [ direction ] ) ;
}
2022-11-17 23:57:51 +02:00
2022-12-19 01:12:26 +02:00
BattleHex : : EDir BattleFieldController : : selectAttackDirection ( BattleHex myNumber , const Point & cursorPos )
{
2022-12-13 13:58:16 +02:00
const bool doubleWide = owner . stacksController - > getActiveStack ( ) - > doubleWide ( ) ;
2022-12-19 01:12:26 +02:00
auto neighbours = myNumber . allNeighbouringTiles ( ) ;
// 0 1
// 5 x 2
// 4 3
2022-11-17 23:57:51 +02:00
2022-12-19 01:12:26 +02:00
// if true - our current stack can move into this hex (and attack)
std : : array < bool , 8 > attackAvailability ;
2022-11-17 23:57:51 +02:00
if ( doubleWide )
{
2022-12-19 01:12:26 +02:00
// For double-hexes we need to ensure that both hexes needed for this direction are occupyable:
// | -0- | -1- | -2- | -3- | -4- | -5- | -6- | -7-
// | o o - | - o o | - - | - - | - - | - - | o o | - -
// | - x - | - x - | - x o o| - x - | - x - |o o x - | - x - | - x -
// | - - | - - | - - | - o o | o o - | - - | - - | o o
for ( size_t i : { 1 , 2 , 3 } )
2023-04-06 00:04:40 +03:00
attackAvailability [ i ] = vstd : : contains ( occupiableHexes , neighbours [ i ] ) & & vstd : : contains ( occupiableHexes , neighbours [ i ] . cloneInDirection ( BattleHex : : RIGHT , false ) ) ;
2022-12-19 01:12:26 +02:00
for ( size_t i : { 4 , 5 , 0 } )
2023-04-06 00:04:40 +03:00
attackAvailability [ i ] = vstd : : contains ( occupiableHexes , neighbours [ i ] ) & & vstd : : contains ( occupiableHexes , neighbours [ i ] . cloneInDirection ( BattleHex : : LEFT , false ) ) ;
2022-12-19 01:12:26 +02:00
2023-04-06 00:04:40 +03:00
attackAvailability [ 6 ] = vstd : : contains ( occupiableHexes , neighbours [ 0 ] ) & & vstd : : contains ( occupiableHexes , neighbours [ 1 ] ) ;
attackAvailability [ 7 ] = vstd : : contains ( occupiableHexes , neighbours [ 3 ] ) & & vstd : : contains ( occupiableHexes , neighbours [ 4 ] ) ;
2022-11-17 23:57:51 +02:00
}
else
{
2022-12-19 01:12:26 +02:00
for ( size_t i = 0 ; i < 6 ; + + i )
2023-04-06 00:04:40 +03:00
attackAvailability [ i ] = vstd : : contains ( occupiableHexes , neighbours [ i ] ) ;
2022-11-17 23:57:51 +02:00
2022-12-19 01:12:26 +02:00
attackAvailability [ 6 ] = false ;
attackAvailability [ 7 ] = false ;
2022-11-17 23:57:51 +02:00
}
2022-12-19 01:12:26 +02:00
// Zero available tiles to attack from
if ( vstd : : find ( attackAvailability , true ) = = attackAvailability . end ( ) )
2022-11-17 23:57:51 +02:00
{
2022-12-19 01:12:26 +02:00
logGlobal - > error ( " Error: cannot find a hex to attack hex %d from! " , myNumber ) ;
return BattleHex : : NONE ;
2022-11-17 23:57:51 +02:00
}
2022-12-19 01:12:26 +02:00
// For each valid direction, select position to test against
std : : array < Point , 8 > testPoint ;
for ( size_t i = 0 ; i < 6 ; + + i )
if ( attackAvailability [ i ] )
testPoint [ i ] = hexPositionAbsolute ( neighbours [ i ] ) . center ( ) ;
// For bottom/top directions select central point, but move it a bit away from true center to reduce zones allocated to them
if ( attackAvailability [ 6 ] )
testPoint [ 6 ] = ( hexPositionAbsolute ( neighbours [ 0 ] ) . center ( ) + hexPositionAbsolute ( neighbours [ 1 ] ) . center ( ) ) / 2 + Point ( 0 , - 5 ) ;
if ( attackAvailability [ 7 ] )
testPoint [ 7 ] = ( hexPositionAbsolute ( neighbours [ 3 ] ) . center ( ) + hexPositionAbsolute ( neighbours [ 4 ] ) . center ( ) ) / 2 + Point ( 0 , 5 ) ;
// Compute distance between tested position & cursor position and pick nearest
std : : array < int , 8 > distance2 ;
for ( size_t i = 0 ; i < 8 ; + + i )
if ( attackAvailability [ i ] )
distance2 [ i ] = ( testPoint [ i ] . y - cursorPos . y ) * ( testPoint [ i ] . y - cursorPos . y ) + ( testPoint [ i ] . x - cursorPos . x ) * ( testPoint [ i ] . x - cursorPos . x ) ;
size_t nearest = - 1 ;
for ( size_t i = 0 ; i < 8 ; + + i )
if ( attackAvailability [ i ] & & ( nearest = = - 1 | | distance2 [ i ] < distance2 [ nearest ] ) )
nearest = i ;
assert ( nearest ! = - 1 ) ;
return BattleHex : : EDir ( nearest ) ;
2022-11-17 23:57:51 +02:00
}
2022-12-19 01:12:26 +02:00
BattleHex BattleFieldController : : fromWhichHexAttack ( BattleHex attackTarget )
2022-11-17 23:57:51 +02:00
{
2022-12-19 01:12:26 +02:00
BattleHex : : EDir direction = selectAttackDirection ( attackTarget , CCS - > curh - > position ( ) ) ;
const CStack * attacker = owner . stacksController - > getActiveStack ( ) ;
assert ( direction ! = BattleHex : : NONE ) ;
assert ( attacker ) ;
if ( ! attacker - > doubleWide ( ) )
2022-11-17 23:57:51 +02:00
{
2022-12-19 01:12:26 +02:00
assert ( direction ! = BattleHex : : BOTTOM ) ;
assert ( direction ! = BattleHex : : TOP ) ;
return attackTarget . cloneInDirection ( direction ) ;
}
else
{
// We need to find position of right hex of double-hex creature (or left for defending side)
// | TOP_LEFT |TOP_RIGHT | RIGHT |BOTTOM_RIGHT|BOTTOM_LEFT| LEFT | TOP |BOTTOM
// | o o - | - o o | - - | - - | - - | - - | o o | - -
// | - x - | - x - | - x o o| - x - | - x - |o o x - | - x - | - x -
// | - - | - - | - - | - o o | o o - | - - | - - | o o
switch ( direction )
2022-11-17 23:57:51 +02:00
{
2022-12-19 01:12:26 +02:00
case BattleHex : : TOP_LEFT :
case BattleHex : : LEFT :
case BattleHex : : BOTTOM_LEFT :
2022-11-17 23:57:51 +02:00
{
2023-04-27 20:43:20 +03:00
if ( attacker - > unitSide ( ) = = BattleSide : : ATTACKER )
2022-12-19 01:12:26 +02:00
return attackTarget . cloneInDirection ( direction ) ;
2022-11-17 23:57:51 +02:00
else
2022-12-19 01:12:26 +02:00
return attackTarget . cloneInDirection ( direction ) . cloneInDirection ( BattleHex : : LEFT ) ;
2022-11-17 23:57:51 +02:00
}
2022-12-19 01:12:26 +02:00
case BattleHex : : TOP_RIGHT :
case BattleHex : : RIGHT :
case BattleHex : : BOTTOM_RIGHT :
2022-11-17 23:57:51 +02:00
{
2023-04-27 20:43:20 +03:00
if ( attacker - > unitSide ( ) = = BattleSide : : ATTACKER )
2022-12-19 01:12:26 +02:00
return attackTarget . cloneInDirection ( direction ) . cloneInDirection ( BattleHex : : RIGHT ) ;
else
return attackTarget . cloneInDirection ( direction ) ;
2022-11-17 23:57:51 +02:00
}
2022-12-19 01:12:26 +02:00
case BattleHex : : TOP :
2022-11-17 23:57:51 +02:00
{
2023-04-27 20:43:20 +03:00
if ( attacker - > unitSide ( ) = = BattleSide : : ATTACKER )
2022-12-19 01:12:26 +02:00
return attackTarget . cloneInDirection ( BattleHex : : TOP_RIGHT ) ;
2022-11-17 23:57:51 +02:00
else
2022-12-19 01:12:26 +02:00
return attackTarget . cloneInDirection ( BattleHex : : TOP_LEFT ) ;
2022-11-17 23:57:51 +02:00
}
2022-12-19 01:12:26 +02:00
case BattleHex : : BOTTOM :
2022-11-17 23:57:51 +02:00
{
2023-04-27 20:43:20 +03:00
if ( attacker - > unitSide ( ) = = BattleSide : : ATTACKER )
2022-12-19 01:12:26 +02:00
return attackTarget . cloneInDirection ( BattleHex : : BOTTOM_RIGHT ) ;
else
return attackTarget . cloneInDirection ( BattleHex : : BOTTOM_LEFT ) ;
2022-11-17 23:57:51 +02:00
}
2022-12-19 01:12:26 +02:00
default :
assert ( 0 ) ;
2023-01-21 19:42:44 +02:00
return BattleHex : : INVALID ;
2022-11-17 23:57:51 +02:00
}
}
}
2022-12-09 13:26:17 +02:00
bool BattleFieldController : : isTileAttackable ( const BattleHex & number ) const
2022-11-17 23:57:51 +02:00
{
2023-04-06 00:04:40 +03:00
for ( auto & elem : occupiableHexes )
2022-11-17 23:57:51 +02:00
{
if ( BattleHex : : mutualPosition ( elem , number ) ! = - 1 | | elem = = number )
return true ;
}
return false ;
}
2023-03-29 13:03:40 +03:00
void BattleFieldController : : updateAccessibleHexes ( )
{
auto accessibility = owner . curInt - > cb - > getAccesibility ( ) ;
for ( int i = 0 ; i < accessibility . size ( ) ; i + + )
stackCountOutsideHexes [ i ] = ( accessibility [ i ] = = EAccessibility : : ACCESSIBLE | | ( accessibility [ i ] = = EAccessibility : : SIDE_COLUMN ) ) ;
}
2022-12-09 13:26:17 +02:00
bool BattleFieldController : : stackCountOutsideHex ( const BattleHex & number ) const
2022-11-17 23:57:51 +02:00
{
return stackCountOutsideHexes [ number ] ;
}
2022-12-21 17:02:53 +02:00
void BattleFieldController : : showAll ( SDL_Surface * to )
{
show ( to ) ;
}
2023-05-13 23:38:57 +03:00
void BattleFieldController : : tick ( uint32_t msPassed )
2022-12-21 17:02:53 +02:00
{
2023-03-29 13:03:40 +03:00
updateAccessibleHexes ( ) ;
2023-05-13 23:38:57 +03:00
owner . stacksController - > tick ( msPassed ) ;
owner . obstacleController - > tick ( msPassed ) ;
owner . projectilesController - > tick ( msPassed ) ;
}
2022-12-21 17:02:53 +02:00
2023-05-13 23:38:57 +03:00
void BattleFieldController : : show ( SDL_Surface * to )
{
2022-12-21 17:02:53 +02:00
Canvas canvas ( to ) ;
2023-02-21 15:44:18 +02:00
CSDL_Ext : : CClipRectGuard guard ( to , pos ) ;
2023-01-05 14:16:01 +02:00
2022-12-21 17:02:53 +02:00
renderBattlefield ( canvas ) ;
}