Merge pull request #2156 from krs0/feature/Highlight_For_Ranged_Full_Damage_Limit
Highlight Ranged Full Damage Limit
BIN
Mods/vcmi/Sprites/battle/rangeHighlights/green/empty.png
Normal file
After Width: | Height: | Size: 139 B |
BIN
Mods/vcmi/Sprites/battle/rangeHighlights/green/fullHex.png
Normal file
After Width: | Height: | Size: 457 B |
BIN
Mods/vcmi/Sprites/battle/rangeHighlights/green/left.png
Normal file
After Width: | Height: | Size: 184 B |
BIN
Mods/vcmi/Sprites/battle/rangeHighlights/green/leftHalf.png
Normal file
After Width: | Height: | Size: 307 B |
BIN
Mods/vcmi/Sprites/battle/rangeHighlights/green/top.png
Normal file
After Width: | Height: | Size: 245 B |
BIN
Mods/vcmi/Sprites/battle/rangeHighlights/green/topLeft.png
Normal file
After Width: | Height: | Size: 222 B |
BIN
Mods/vcmi/Sprites/battle/rangeHighlights/green/topLeftCorner.png
Normal file
After Width: | Height: | Size: 377 B |
After Width: | Height: | Size: 347 B |
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"basepath" : "battle/rangeHighlights/green/",
|
||||||
|
"images" :
|
||||||
|
[
|
||||||
|
{ "frame" : 00, "file" : "empty.png"}, // 000001 -> 00 empty frame
|
||||||
|
|
||||||
|
// load single edges
|
||||||
|
{ "frame" : 01, "file" : "topLeft.png"}, //000001 -> 01 topLeft
|
||||||
|
{ "frame" : 02, "file" : "topLeft.png"}, //000010 -> 02 topRight
|
||||||
|
{ "frame" : 03, "file" : "left.png"}, //000100 -> 04 right
|
||||||
|
{ "frame" : 04, "file" : "topLeft.png"}, //001000 -> 08 bottomRight
|
||||||
|
{ "frame" : 05, "file" : "topLeft.png"}, //010000 -> 16 bottomLeft
|
||||||
|
{ "frame" : 06, "file" : "left.png"}, //100000 -> 32 left
|
||||||
|
|
||||||
|
// load double edges
|
||||||
|
{ "frame" : 07, "file" : "top.png"}, //000011 -> 03 top
|
||||||
|
{ "frame" : 08, "file" : "top.png"}, //011000 -> 24 bottom
|
||||||
|
{ "frame" : 09, "file" : "topLeftHalfCorner.png"}, //000110 -> 06 topRightHalfCorner
|
||||||
|
{ "frame" : 10, "file" : "topLeftHalfCorner.png"}, //001100 -> 12 bottomRightHalfCorner
|
||||||
|
{ "frame" : 11, "file" : "topLeftHalfCorner.png"}, //110000 -> 48 bottomLeftHalfCorner
|
||||||
|
{ "frame" : 12, "file" : "topLeftHalfCorner.png"}, //100001 -> 33 topLeftHalfCorner
|
||||||
|
|
||||||
|
// load halves
|
||||||
|
{ "frame" : 13, "file" : "leftHalf.png"}, //001110 -> 14 rightHalf
|
||||||
|
{ "frame" : 14, "file" : "leftHalf.png"}, //110001 -> 49 leftHalf
|
||||||
|
|
||||||
|
// load corners
|
||||||
|
{ "frame" : 15, "file" : "topLeftCorner.png"}, //000111 -> 07 topRightCorner
|
||||||
|
{ "frame" : 16, "file" : "topLeftCorner.png"}, //011100 -> 28 bottomRightCorner
|
||||||
|
{ "frame" : 17, "file" : "topLeftCorner.png"}, //111000 -> 56 bottomLeftCorner
|
||||||
|
{ "frame" : 18, "file" : "topLeftCorner.png"} //100011 -> 35 topLeftCorner
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -104,6 +104,8 @@
|
|||||||
"vcmi.battleOptions.animationsSpeed6.help": "Set animation speed to instantaneous",
|
"vcmi.battleOptions.animationsSpeed6.help": "Set animation speed to instantaneous",
|
||||||
"vcmi.battleOptions.movementHighlightOnHover.hover": "Movement Highlight on Hover",
|
"vcmi.battleOptions.movementHighlightOnHover.hover": "Movement Highlight on Hover",
|
||||||
"vcmi.battleOptions.movementHighlightOnHover.help": "{Movement Highlight on Hover}\n\nHighlight unit's movement range when you hover over it.",
|
"vcmi.battleOptions.movementHighlightOnHover.help": "{Movement Highlight on Hover}\n\nHighlight unit's movement range when you hover over it.",
|
||||||
|
"vcmi.battleOptions.rangedFullDamageLimitHighlightOnHover.hover": "Ranged Full Damage Limit Highlight",
|
||||||
|
"vcmi.battleOptions.rangedFullDamageLimitHighlightOnHover.help": "{Ranged Full Damage Limit on Hover}\n\nHighlight ranged unit's full damage range limit when you hover over it.",
|
||||||
"vcmi.battleOptions.skipBattleIntroMusic.hover": "Skip Intro Music",
|
"vcmi.battleOptions.skipBattleIntroMusic.hover": "Skip Intro Music",
|
||||||
"vcmi.battleOptions.skipBattleIntroMusic.help": "{Skip Intro Music}\n\nAllow actions during the intro music that plays at the beginning of each battle",
|
"vcmi.battleOptions.skipBattleIntroMusic.help": "{Skip Intro Music}\n\nAllow actions during the intro music that plays at the beginning of each battle",
|
||||||
"vcmi.battleWindow.pressKeyToSkipIntro" : "Press any key to start battle immediately",
|
"vcmi.battleWindow.pressKeyToSkipIntro" : "Press any key to start battle immediately",
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "../gui/CGuiHandler.h"
|
#include "../gui/CGuiHandler.h"
|
||||||
#include "../gui/CursorHandler.h"
|
#include "../gui/CursorHandler.h"
|
||||||
#include "../adventureMap/CInGameConsole.h"
|
#include "../adventureMap/CInGameConsole.h"
|
||||||
|
#include "../client/render/CAnimation.h"
|
||||||
|
|
||||||
#include "../../CCallback.h"
|
#include "../../CCallback.h"
|
||||||
#include "../../lib/BattleFieldHandler.h"
|
#include "../../lib/BattleFieldHandler.h"
|
||||||
@ -36,6 +37,83 @@
|
|||||||
#include "../../lib/CStack.h"
|
#include "../../lib/CStack.h"
|
||||||
#include "../../lib/spells/ISpellMechanics.h"
|
#include "../../lib/spells/ISpellMechanics.h"
|
||||||
|
|
||||||
|
namespace HexMasks
|
||||||
|
{
|
||||||
|
// mask definitions that has set to 1 the edges present in the hex edges highlight image
|
||||||
|
/*
|
||||||
|
/\
|
||||||
|
0 1
|
||||||
|
/ \
|
||||||
|
| |
|
||||||
|
5 2
|
||||||
|
| |
|
||||||
|
\ /
|
||||||
|
4 3
|
||||||
|
\/
|
||||||
|
*/
|
||||||
|
enum HexEdgeMasks {
|
||||||
|
empty = 0b000000, // empty used when wanting to keep indexes the same but no highlight should be displayed
|
||||||
|
topLeft = 0b000001,
|
||||||
|
topRight = 0b000010,
|
||||||
|
right = 0b000100,
|
||||||
|
bottomRight = 0b001000,
|
||||||
|
bottomLeft = 0b010000,
|
||||||
|
left = 0b100000,
|
||||||
|
|
||||||
|
top = 0b000011,
|
||||||
|
bottom = 0b011000,
|
||||||
|
topRightHalfCorner = 0b000110,
|
||||||
|
bottomRightHalfCorner = 0b001100,
|
||||||
|
bottomLeftHalfCorner = 0b110000,
|
||||||
|
topLeftHalfCorner = 0b100001,
|
||||||
|
|
||||||
|
rightTopAndBottom = 0b001010, // special case, right half can be drawn instead of only top and bottom
|
||||||
|
leftTopAndBottom = 0b010001, // special case, left half can be drawn instead of only top and bottom
|
||||||
|
|
||||||
|
rightHalf = 0b001110,
|
||||||
|
leftHalf = 0b110001,
|
||||||
|
|
||||||
|
topRightCorner = 0b000111,
|
||||||
|
bottomRightCorner = 0b011100,
|
||||||
|
bottomLeftCorner = 0b111000,
|
||||||
|
topLeftCorner = 0b100011
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int, int> hexEdgeMaskToFrameIndex;
|
||||||
|
|
||||||
|
// Maps HexEdgesMask to "Frame" indexes for range highligt images
|
||||||
|
void initializeHexEdgeMaskToFrameIndex()
|
||||||
|
{
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::empty] = 0;
|
||||||
|
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::topLeft] = 1;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::topRight] = 2;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::right] = 3;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::bottomRight] = 4;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::bottomLeft] = 5;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::left] = 6;
|
||||||
|
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::top] = 7;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::bottom] = 8;
|
||||||
|
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::topRightHalfCorner] = 9;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner] = 10;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::bottomLeftHalfCorner] = 11;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::topLeftHalfCorner] = 12;
|
||||||
|
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::rightTopAndBottom] = 13;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::leftTopAndBottom] = 14;
|
||||||
|
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::rightHalf] = 13;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::leftHalf] = 14;
|
||||||
|
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::topRightCorner] = 15;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner] = 16;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::bottomLeftCorner] = 17;
|
||||||
|
hexEdgeMaskToFrameIndex[HexMasks::topLeftCorner] = 18;
|
||||||
|
}
|
||||||
|
|
||||||
BattleFieldController::BattleFieldController(BattleInterface & owner):
|
BattleFieldController::BattleFieldController(BattleInterface & owner):
|
||||||
owner(owner)
|
owner(owner)
|
||||||
{
|
{
|
||||||
@ -50,6 +128,12 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
|
|||||||
attackCursors = std::make_shared<CAnimation>("CRCOMBAT");
|
attackCursors = std::make_shared<CAnimation>("CRCOMBAT");
|
||||||
attackCursors->preload();
|
attackCursors->preload();
|
||||||
|
|
||||||
|
rangedFullDamageLimitImages = std::make_unique<CAnimation>("battle/rangeHighlights/rangeHighlightsGreen.json");
|
||||||
|
rangedFullDamageLimitImages->preload();
|
||||||
|
|
||||||
|
initializeHexEdgeMaskToFrameIndex();
|
||||||
|
flipRangedFullDamageLimitImagesIntoPositions();
|
||||||
|
|
||||||
if(!owner.siegeController)
|
if(!owner.siegeController)
|
||||||
{
|
{
|
||||||
auto bfieldType = owner.curInt->cb->battleGetBattlefieldType();
|
auto bfieldType = owner.curInt->cb->battleGetBattlefieldType();
|
||||||
@ -361,6 +445,132 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<BattleHex> BattleFieldController::getRangedFullDamageHexes()
|
||||||
|
{
|
||||||
|
std::vector<BattleHex> rangedFullDamageHexes; // used for return
|
||||||
|
|
||||||
|
// if not a hovered arcer unit -> return
|
||||||
|
auto hoveredHex = getHoveredHex();
|
||||||
|
const CStack * hoveredStack = owner.curInt->cb->battleGetStackByPos(hoveredHex, true);
|
||||||
|
|
||||||
|
if (!settings["battle"]["rangedFullDamageLimitHighlightOnHover"].Bool() && !GH.isKeyboardShiftDown())
|
||||||
|
return rangedFullDamageHexes;
|
||||||
|
|
||||||
|
if(!(hoveredStack && hoveredStack->isShooter()))
|
||||||
|
return rangedFullDamageHexes;
|
||||||
|
|
||||||
|
auto rangedFullDamageDistance = hoveredStack->getRangedFullDamageDistance();
|
||||||
|
|
||||||
|
// get only battlefield hexes that are in full range damage distance
|
||||||
|
std::set<BattleHex> fullRangeLimit;
|
||||||
|
for(auto i = 0; i < GameConstants::BFIELD_SIZE; i++)
|
||||||
|
{
|
||||||
|
BattleHex hex(i);
|
||||||
|
if(hex.isAvailable() && BattleHex::getDistance(hoveredHex, hex) <= rangedFullDamageDistance)
|
||||||
|
rangedFullDamageHexes.push_back(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rangedFullDamageHexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BattleHex> BattleFieldController::getRangedFullDamageLimitHexes(std::vector<BattleHex> rangedFullDamageHexes)
|
||||||
|
{
|
||||||
|
std::vector<BattleHex> rangedFullDamageLimitHexes; // used for return
|
||||||
|
|
||||||
|
// if not a hovered arcer unit -> return
|
||||||
|
auto hoveredHex = getHoveredHex();
|
||||||
|
const CStack * hoveredStack = owner.curInt->cb->battleGetStackByPos(hoveredHex, true);
|
||||||
|
|
||||||
|
if(!(hoveredStack && hoveredStack->isShooter()))
|
||||||
|
return rangedFullDamageLimitHexes;
|
||||||
|
|
||||||
|
auto rangedFullDamageDistance = hoveredStack->getRangedFullDamageDistance();
|
||||||
|
|
||||||
|
// from ranged full damage hexes get only the ones at the limit
|
||||||
|
for(auto & hex : rangedFullDamageHexes)
|
||||||
|
{
|
||||||
|
if(BattleHex::getDistance(hoveredHex, hex) == rangedFullDamageDistance)
|
||||||
|
rangedFullDamageLimitHexes.push_back(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rangedFullDamageLimitHexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<BattleHex::EDir>> BattleFieldController::getOutsideNeighbourDirectionsForLimitHexes(std::vector<BattleHex> rangedFullDamageHexes, std::vector<BattleHex> rangedFullDamageLimitHexes)
|
||||||
|
{
|
||||||
|
std::vector<std::vector<BattleHex::EDir>> output;
|
||||||
|
|
||||||
|
if(rangedFullDamageHexes.empty())
|
||||||
|
return output;
|
||||||
|
|
||||||
|
for(auto & hex : rangedFullDamageLimitHexes)
|
||||||
|
{
|
||||||
|
// get all neighbours and their directions
|
||||||
|
|
||||||
|
auto neighbouringTiles = hex.allNeighbouringTiles();
|
||||||
|
|
||||||
|
std::vector<BattleHex::EDir> outsideNeighbourDirections;
|
||||||
|
|
||||||
|
// for each neighbour add to output only the valid ones and only that are not found in rangedFullDamageHexes
|
||||||
|
for(auto direction = 0; direction < 6; direction++)
|
||||||
|
{
|
||||||
|
if(!neighbouringTiles[direction].isAvailable())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto it = std::find(rangedFullDamageHexes.begin(), rangedFullDamageHexes.end(), neighbouringTiles[direction]);
|
||||||
|
|
||||||
|
if(it == rangedFullDamageHexes.end())
|
||||||
|
outsideNeighbourDirections.push_back(BattleHex::EDir(direction)); // push direction
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push_back(outsideNeighbourDirections);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<IImage>> BattleFieldController::calculateRangedFullDamageHighlightImages(std::vector<std::vector<BattleHex::EDir>> rangedFullDamageLimitHexesNeighbourDirections)
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<IImage>> output; // if no image is to be shown an empty image is still added to help with traverssing the range
|
||||||
|
|
||||||
|
if(rangedFullDamageLimitHexesNeighbourDirections.empty())
|
||||||
|
return output;
|
||||||
|
|
||||||
|
for(auto & directions : rangedFullDamageLimitHexesNeighbourDirections)
|
||||||
|
{
|
||||||
|
std::bitset<6> mask;
|
||||||
|
|
||||||
|
// convert directions to mask
|
||||||
|
for(auto direction : directions)
|
||||||
|
mask.set(direction);
|
||||||
|
|
||||||
|
uint8_t imageKey = static_cast<uint8_t>(mask.to_ulong());
|
||||||
|
output.push_back(rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[imageKey]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleFieldController::flipRangedFullDamageLimitImagesIntoPositions()
|
||||||
|
{
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::topRight])->verticalFlip();
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::right])->verticalFlip();
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRight])->doubleFlip();
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomLeft])->horizontalFlip();
|
||||||
|
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottom])->horizontalFlip();
|
||||||
|
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::topRightHalfCorner])->verticalFlip();
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner])->doubleFlip();
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftHalfCorner])->horizontalFlip();
|
||||||
|
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::rightHalf])->verticalFlip();
|
||||||
|
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::topRightCorner])->verticalFlip();
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner])->doubleFlip();
|
||||||
|
rangedFullDamageLimitImages->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftCorner])->horizontalFlip();
|
||||||
|
}
|
||||||
|
|
||||||
void BattleFieldController::showHighlightedHexes(Canvas & canvas)
|
void BattleFieldController::showHighlightedHexes(Canvas & canvas)
|
||||||
{
|
{
|
||||||
std::set<BattleHex> hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack();
|
std::set<BattleHex> hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack();
|
||||||
@ -370,6 +580,12 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas)
|
|||||||
if(getHoveredHex() == BattleHex::INVALID)
|
if(getHoveredHex() == BattleHex::INVALID)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// calculate array with highlight images for ranged full damage limit
|
||||||
|
std::vector<BattleHex> rangedFullDamageHexes = getRangedFullDamageHexes();
|
||||||
|
std::vector<BattleHex> rangedFullDamageLimitHexes = getRangedFullDamageLimitHexes(rangedFullDamageHexes);
|
||||||
|
std::vector<std::vector<BattleHex::EDir>> rangedFullDamageLimitHexesNeighbourDirections = getOutsideNeighbourDirectionsForLimitHexes(rangedFullDamageHexes, rangedFullDamageLimitHexes);
|
||||||
|
std::vector<std::shared_ptr<IImage>> rangedFullDamageLimitHexesHighligts = calculateRangedFullDamageHighlightImages(rangedFullDamageLimitHexesNeighbourDirections);
|
||||||
|
|
||||||
auto const & hoveredMouseHexes = owner.actionsController->currentActionSpellcasting(getHoveredHex()) ? hoveredSpellHexes : hoveredMoveHexes;
|
auto const & hoveredMouseHexes = owner.actionsController->currentActionSpellcasting(getHoveredHex()) ? hoveredSpellHexes : hoveredMoveHexes;
|
||||||
|
|
||||||
for(int hex = 0; hex < GameConstants::BFIELD_SIZE; ++hex)
|
for(int hex = 0; hex < GameConstants::BFIELD_SIZE; ++hex)
|
||||||
@ -377,6 +593,16 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas)
|
|||||||
bool stackMovement = hoveredStackMovementRangeHexes.count(hex);
|
bool stackMovement = hoveredStackMovementRangeHexes.count(hex);
|
||||||
bool mouse = hoveredMouseHexes.count(hex);
|
bool mouse = hoveredMouseHexes.count(hex);
|
||||||
|
|
||||||
|
// calculate if hex is Ranged Full Damage Limit and its position in highlight array
|
||||||
|
bool isRangedFullDamageLimit = false;
|
||||||
|
int hexIndexInRangedFullDamageLimit = 0;
|
||||||
|
if(!rangedFullDamageLimitHexes.empty())
|
||||||
|
{
|
||||||
|
auto pos = std::find(rangedFullDamageLimitHexes.begin(), rangedFullDamageLimitHexes.end(), hex);
|
||||||
|
hexIndexInRangedFullDamageLimit = std::distance(rangedFullDamageLimitHexes.begin(), pos);
|
||||||
|
isRangedFullDamageLimit = pos != rangedFullDamageLimitHexes.end();
|
||||||
|
}
|
||||||
|
|
||||||
if(stackMovement && mouse) // area where hovered stackMovement can move shown with highlight. Because also affected by mouse cursor, shade as well
|
if(stackMovement && mouse) // area where hovered stackMovement can move shown with highlight. Because also affected by mouse cursor, shade as well
|
||||||
{
|
{
|
||||||
showHighlightedHex(canvas, cellUnitMovementHighlight, hex, false);
|
showHighlightedHex(canvas, cellUnitMovementHighlight, hex, false);
|
||||||
@ -390,6 +616,10 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas)
|
|||||||
{
|
{
|
||||||
showHighlightedHex(canvas, cellUnitMovementHighlight, hex, false);
|
showHighlightedHex(canvas, cellUnitMovementHighlight, hex, false);
|
||||||
}
|
}
|
||||||
|
if(isRangedFullDamageLimit)
|
||||||
|
{
|
||||||
|
showHighlightedHex(canvas, rangedFullDamageLimitHexesHighligts[hexIndexInRangedFullDamageLimit], hex, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ class BattleFieldController : public CIntObject
|
|||||||
std::shared_ptr<IImage> cellUnitMovementHighlight;
|
std::shared_ptr<IImage> cellUnitMovementHighlight;
|
||||||
std::shared_ptr<IImage> cellUnitMaxMovementHighlight;
|
std::shared_ptr<IImage> cellUnitMaxMovementHighlight;
|
||||||
std::shared_ptr<IImage> cellShade;
|
std::shared_ptr<IImage> cellShade;
|
||||||
|
std::unique_ptr<CAnimation> rangedFullDamageLimitImages;
|
||||||
|
|
||||||
std::shared_ptr<CAnimation> attackCursors;
|
std::shared_ptr<CAnimation> attackCursors;
|
||||||
|
|
||||||
@ -58,6 +59,23 @@ class BattleFieldController : public CIntObject
|
|||||||
std::set<BattleHex> getHighlightedHexesForSpellRange();
|
std::set<BattleHex> getHighlightedHexesForSpellRange();
|
||||||
std::set<BattleHex> getHighlightedHexesForMovementTarget();
|
std::set<BattleHex> getHighlightedHexesForMovementTarget();
|
||||||
|
|
||||||
|
/// get all hexes where a ranged unit can do full damage
|
||||||
|
std::vector<BattleHex> getRangedFullDamageHexes();
|
||||||
|
|
||||||
|
/// get only hexes at the limit of a ranged unit's full damage range
|
||||||
|
std::vector<BattleHex> getRangedFullDamageLimitHexes(std::vector<BattleHex> rangedFullDamageHexes);
|
||||||
|
|
||||||
|
/// get an array that has for each hex in range, an aray with all directions where an ouside neighbour hex exists
|
||||||
|
std::vector<std::vector<BattleHex::EDir>> getOutsideNeighbourDirectionsForLimitHexes(std::vector<BattleHex> rangedFullDamageHexes, std::vector<BattleHex> rangedFullDamageLimitHexes);
|
||||||
|
|
||||||
|
/// calculates what image to use as range limit, depending on the direction of neighbors
|
||||||
|
/// a mask is used internally to mark the directions of all neighbours
|
||||||
|
/// based on this mask the corresponding image is selected
|
||||||
|
std::vector<std::shared_ptr<IImage>> calculateRangedFullDamageHighlightImages(std::vector<std::vector<BattleHex::EDir>> fullRangeLimitHexesNeighbourDirections);
|
||||||
|
|
||||||
|
/// to reduce the number of source images used, some images will be used as flipped versions of preloaded ones
|
||||||
|
void flipRangedFullDamageLimitImagesIntoPositions();
|
||||||
|
|
||||||
void showBackground(Canvas & canvas);
|
void showBackground(Canvas & canvas);
|
||||||
void showBackgroundImage(Canvas & canvas);
|
void showBackgroundImage(Canvas & canvas);
|
||||||
void showBackgroundImageWithHexes(Canvas & canvas);
|
void showBackgroundImageWithHexes(Canvas & canvas);
|
||||||
|
@ -78,6 +78,7 @@ public:
|
|||||||
|
|
||||||
virtual void horizontalFlip() = 0;
|
virtual void horizontalFlip() = 0;
|
||||||
virtual void verticalFlip() = 0;
|
virtual void verticalFlip() = 0;
|
||||||
|
virtual void doubleFlip() = 0;
|
||||||
|
|
||||||
IImage();
|
IImage();
|
||||||
virtual ~IImage();
|
virtual ~IImage();
|
||||||
|
@ -281,6 +281,12 @@ void SDLImage::verticalFlip()
|
|||||||
surf = flipped;
|
surf = flipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SDLImage::doubleFlip()
|
||||||
|
{
|
||||||
|
horizontalFlip();
|
||||||
|
verticalFlip();
|
||||||
|
}
|
||||||
|
|
||||||
// Keep the original palette, in order to do color switching operation
|
// Keep the original palette, in order to do color switching operation
|
||||||
void SDLImage::savePalette()
|
void SDLImage::savePalette()
|
||||||
{
|
{
|
||||||
|
@ -64,6 +64,7 @@ public:
|
|||||||
|
|
||||||
void horizontalFlip() override;
|
void horizontalFlip() override;
|
||||||
void verticalFlip() override;
|
void verticalFlip() override;
|
||||||
|
void doubleFlip() override;
|
||||||
|
|
||||||
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
|
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
|
||||||
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
|
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
|
||||||
|
@ -39,6 +39,10 @@ BattleOptionsTab::BattleOptionsTab(BattleInterface * owner)
|
|||||||
{
|
{
|
||||||
movementHighlightOnHoverChangedCallback(value, owner);
|
movementHighlightOnHoverChangedCallback(value, owner);
|
||||||
});
|
});
|
||||||
|
addCallback("rangedFullDamageLimitHighlightOnHoverChanged", [this, owner](bool value)
|
||||||
|
{
|
||||||
|
rangedFullDamageLimitHighlightOnHoverChangedCallback(value, owner);
|
||||||
|
});
|
||||||
addCallback("mouseShadowChanged", [this](bool value)
|
addCallback("mouseShadowChanged", [this](bool value)
|
||||||
{
|
{
|
||||||
mouseShadowChangedCallback(value);
|
mouseShadowChangedCallback(value);
|
||||||
@ -76,6 +80,9 @@ BattleOptionsTab::BattleOptionsTab(BattleInterface * owner)
|
|||||||
std::shared_ptr<CToggleButton> movementHighlightOnHoverCheckbox = widget<CToggleButton>("movementHighlightOnHoverCheckbox");
|
std::shared_ptr<CToggleButton> movementHighlightOnHoverCheckbox = widget<CToggleButton>("movementHighlightOnHoverCheckbox");
|
||||||
movementHighlightOnHoverCheckbox->setSelected(settings["battle"]["movementHighlightOnHover"].Bool());
|
movementHighlightOnHoverCheckbox->setSelected(settings["battle"]["movementHighlightOnHover"].Bool());
|
||||||
|
|
||||||
|
std::shared_ptr<CToggleButton> rangedFullDamageLimitHighlightOnHoverCheckbox = widget<CToggleButton>("rangedFullDamageLimitHighlightOnHoverCheckbox");
|
||||||
|
rangedFullDamageLimitHighlightOnHoverCheckbox->setSelected(settings["battle"]["rangedFullDamageLimitHighlightOnHover"].Bool());
|
||||||
|
|
||||||
std::shared_ptr<CToggleButton> mouseShadowCheckbox = widget<CToggleButton>("mouseShadowCheckbox");
|
std::shared_ptr<CToggleButton> mouseShadowCheckbox = widget<CToggleButton>("mouseShadowCheckbox");
|
||||||
mouseShadowCheckbox->setSelected(settings["battle"]["mouseShadow"].Bool());
|
mouseShadowCheckbox->setSelected(settings["battle"]["mouseShadow"].Bool());
|
||||||
|
|
||||||
@ -152,6 +159,14 @@ void BattleOptionsTab::movementHighlightOnHoverChangedCallback(bool value, Battl
|
|||||||
parentBattleInterface->redrawBattlefield();
|
parentBattleInterface->redrawBattlefield();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BattleOptionsTab::rangedFullDamageLimitHighlightOnHoverChangedCallback(bool value, BattleInterface * parentBattleInterface)
|
||||||
|
{
|
||||||
|
Settings stackRange = settings.write["battle"]["rangedFullDamageLimitHighlightOnHover"];
|
||||||
|
stackRange->Bool() = value;
|
||||||
|
if(parentBattleInterface)
|
||||||
|
parentBattleInterface->redrawBattlefield();
|
||||||
|
}
|
||||||
|
|
||||||
void BattleOptionsTab::mouseShadowChangedCallback(bool value)
|
void BattleOptionsTab::mouseShadowChangedCallback(bool value)
|
||||||
{
|
{
|
||||||
Settings shadow = settings.write["battle"]["mouseShadow"];
|
Settings shadow = settings.write["battle"]["mouseShadow"];
|
||||||
|
@ -25,6 +25,7 @@ private:
|
|||||||
void viewGridChangedCallback(bool value, BattleInterface * parentBattleInterface);
|
void viewGridChangedCallback(bool value, BattleInterface * parentBattleInterface);
|
||||||
void movementShadowChangedCallback(bool value, BattleInterface * parentBattleInterface);
|
void movementShadowChangedCallback(bool value, BattleInterface * parentBattleInterface);
|
||||||
void movementHighlightOnHoverChangedCallback(bool value, BattleInterface * parentBattleInterface);
|
void movementHighlightOnHoverChangedCallback(bool value, BattleInterface * parentBattleInterface);
|
||||||
|
void rangedFullDamageLimitHighlightOnHoverChangedCallback(bool value, BattleInterface * parentBattleInterface);
|
||||||
void mouseShadowChangedCallback(bool value);
|
void mouseShadowChangedCallback(bool value);
|
||||||
void animationSpeedChangedCallback(int value);
|
void animationSpeedChangedCallback(int value);
|
||||||
void showQueueChangedCallback(bool value, BattleInterface * parentBattleInterface);
|
void showQueueChangedCallback(bool value, BattleInterface * parentBattleInterface);
|
||||||
|
@ -286,7 +286,7 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"default" : {},
|
"default" : {},
|
||||||
"required" : [ "speedFactor", "mouseShadow", "cellBorders", "stackRange", "movementHighlightOnHover", "showQueue", "swipeAttackDistance", "queueSize" ],
|
"required" : [ "speedFactor", "mouseShadow", "cellBorders", "stackRange", "movementHighlightOnHover", "rangedFullDamageLimitHighlightOnHover", "showQueue", "swipeAttackDistance", "queueSize" ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"speedFactor" : {
|
"speedFactor" : {
|
||||||
"type" : "number",
|
"type" : "number",
|
||||||
@ -308,6 +308,10 @@
|
|||||||
"type" : "boolean",
|
"type" : "boolean",
|
||||||
"default" : true
|
"default" : true
|
||||||
},
|
},
|
||||||
|
"rangedFullDamageLimitHighlightOnHover" : {
|
||||||
|
"type" : "boolean",
|
||||||
|
"default" : false
|
||||||
|
},
|
||||||
"showQueue" : {
|
"showQueue" : {
|
||||||
"type" : "boolean",
|
"type" : "boolean",
|
||||||
"default" : true
|
"default" : true
|
||||||
|
@ -7,13 +7,13 @@
|
|||||||
"name": "lineCreatureInfo",
|
"name": "lineCreatureInfo",
|
||||||
"type": "texture",
|
"type": "texture",
|
||||||
"image": "settingsWindow/lineHorizontal",
|
"image": "settingsWindow/lineHorizontal",
|
||||||
"rect": { "x" : 5, "y" : 229, "w": 365, "h": 3}
|
"rect": { "x" : 5, "y" : 289, "w": 365, "h": 3}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "lineAnimationSpeed",
|
"name": "lineAnimationSpeed",
|
||||||
"type": "texture",
|
"type": "texture",
|
||||||
"image": "settingsWindow/lineHorizontal",
|
"image": "settingsWindow/lineHorizontal",
|
||||||
"rect": { "x" : 5, "y" : 319, "w": 365, "h": 3}
|
"rect": { "x" : 5, "y" : 349, "w": 365, "h": 3}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type" : "labelTitle",
|
"type" : "labelTitle",
|
||||||
@ -23,7 +23,7 @@
|
|||||||
{
|
{
|
||||||
"type" : "labelTitle",
|
"type" : "labelTitle",
|
||||||
"text": "core.genrltxt.397", // Creature info
|
"text": "core.genrltxt.397", // Creature info
|
||||||
"position": {"x": 10, "y": 235}
|
"position": {"x": 10, "y": 265}
|
||||||
},
|
},
|
||||||
/////////////////////////////////////// Right section - Auto-combat settings (NOT IMPLEMENTED)
|
/////////////////////////////////////// Right section - Auto-combat settings (NOT IMPLEMENTED)
|
||||||
{
|
{
|
||||||
@ -69,7 +69,7 @@
|
|||||||
"name": "creatureInfoLabels",
|
"name": "creatureInfoLabels",
|
||||||
"type" : "verticalLayout",
|
"type" : "verticalLayout",
|
||||||
"customType" : "labelDescription",
|
"customType" : "labelDescription",
|
||||||
"position": {"x": 45, "y": 265},
|
"position": {"x": 45, "y": 295},
|
||||||
"items":
|
"items":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -84,7 +84,7 @@
|
|||||||
"name": "creatureInfoCheckboxes",
|
"name": "creatureInfoCheckboxes",
|
||||||
"type" : "verticalLayout",
|
"type" : "verticalLayout",
|
||||||
"customType" : "checkboxFake",
|
"customType" : "checkboxFake",
|
||||||
"position": {"x": 10, "y": 263},
|
"position": {"x": 10, "y": 293},
|
||||||
"items":
|
"items":
|
||||||
[
|
[
|
||||||
{},
|
{},
|
||||||
@ -107,6 +107,9 @@
|
|||||||
{
|
{
|
||||||
"text": "vcmi.battleOptions.movementHighlightOnHover.hover",
|
"text": "vcmi.battleOptions.movementHighlightOnHover.hover",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"text": "vcmi.battleOptions.rangedFullDamageLimitHighlightOnHover.hover",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"text": "core.genrltxt.406",
|
"text": "core.genrltxt.406",
|
||||||
},
|
},
|
||||||
@ -136,6 +139,11 @@
|
|||||||
"help": "vcmi.battleOptions.movementHighlightOnHover",
|
"help": "vcmi.battleOptions.movementHighlightOnHover",
|
||||||
"callback": "movementHighlightOnHoverChanged"
|
"callback": "movementHighlightOnHoverChanged"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "rangedFullDamageLimitHighlightOnHoverCheckbox",
|
||||||
|
"help": "vcmi.battleOptions.rangedFullDamageLimitHighlightOnHover",
|
||||||
|
"callback": "rangedFullDamageLimitHighlightOnHoverChanged"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mouseShadowCheckbox",
|
"name": "mouseShadowCheckbox",
|
||||||
"help": "core.help.429",
|
"help": "core.help.429",
|
||||||
|
@ -591,6 +591,24 @@ int32_t CUnitState::getInitiative(int turn) const
|
|||||||
return valOfBonuses(Selector::type()(BonusType::STACKS_SPEED).And(Selector::turns(turn)));
|
return valOfBonuses(Selector::type()(BonusType::STACKS_SPEED).And(Selector::turns(turn)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t CUnitState::getRangedFullDamageDistance() const
|
||||||
|
{
|
||||||
|
if(!isShooter())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
uint8_t rangedFullDamageDistance = GameConstants::BATTLE_PENALTY_DISTANCE;
|
||||||
|
|
||||||
|
// overwrite full ranged damage distance with the value set in Additional info field of LIMITED_SHOOTING_RANGE bonus
|
||||||
|
if(this->hasBonus(Selector::type()(BonusType::LIMITED_SHOOTING_RANGE)))
|
||||||
|
{
|
||||||
|
auto bonus = this->getBonus(Selector::type()(BonusType::LIMITED_SHOOTING_RANGE));
|
||||||
|
if(bonus != nullptr && bonus->additionalInfo != CAddInfo::NONE)
|
||||||
|
rangedFullDamageDistance = bonus->additionalInfo[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return rangedFullDamageDistance;
|
||||||
|
}
|
||||||
|
|
||||||
bool CUnitState::canMove(int turn) const
|
bool CUnitState::canMove(int turn) const
|
||||||
{
|
{
|
||||||
return alive() && !hasBonus(Selector::type()(BonusType::NOT_ACTIVE).And(Selector::turns(turn))); //eg. Ammo Cart or blinded creature
|
return alive() && !hasBonus(Selector::type()(BonusType::NOT_ACTIVE).And(Selector::turns(turn))); //eg. Ammo Cart or blinded creature
|
||||||
|
@ -221,6 +221,7 @@ public:
|
|||||||
BattleHex getPosition() const override;
|
BattleHex getPosition() const override;
|
||||||
void setPosition(BattleHex hex) override;
|
void setPosition(BattleHex hex) override;
|
||||||
int32_t getInitiative(int turn = 0) const override;
|
int32_t getInitiative(int turn = 0) const override;
|
||||||
|
uint8_t getRangedFullDamageDistance() const;
|
||||||
|
|
||||||
bool canMove(int turn = 0) const override;
|
bool canMove(int turn = 0) const override;
|
||||||
bool defended(int turn = 0) const override;
|
bool defended(int turn = 0) const override;
|
||||||
|