mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-06 09:09:40 +02:00
Support for custom cursors for adventure map spells
This commit is contained in:
@@ -1234,11 +1234,10 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
adventureInt->onHeroChanged(hero);
|
adventureInt->onHeroChanged(hero);
|
||||||
if ((bonus.type == BonusType::FLYING_MOVEMENT || bonus.type == BonusType::WATER_WALKING) && !gain)
|
|
||||||
{
|
//recalculate paths because hero has lost or gained bonus influencing pathfinding
|
||||||
//recalculate paths because hero has lost bonus influencing pathfinding
|
if ((bonus.type == BonusType::FLYING_MOVEMENT || bonus.type == BonusType::WATER_WALKING || bonus.type == BonusType::ROUGH_TERRAIN_DISCOUNT || bonus.type == BonusType::NO_TERRAIN_PENALTY))
|
||||||
localState->erasePath(hero);
|
localState->verifyPath(hero);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlayerInterface::moveHero( const CGHeroInstance *h, const CGPath& path )
|
void CPlayerInterface::moveHero( const CGHeroInstance *h, const CGPath& path )
|
||||||
@@ -1602,9 +1601,6 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, SpellID sp
|
|||||||
if(ENGINE->windows().topWindow<CSpellWindow>())
|
if(ENGINE->windows().topWindow<CSpellWindow>())
|
||||||
ENGINE->windows().popWindows(1);
|
ENGINE->windows().popWindows(1);
|
||||||
|
|
||||||
if(spellID == SpellID::FLY || spellID == SpellID::WATER_WALK)
|
|
||||||
localState->erasePath(caster);
|
|
||||||
|
|
||||||
auto castSoundPath = spellID.toSpell()->getCastSound();
|
auto castSoundPath = spellID.toSpell()->getCastSound();
|
||||||
if(!castSoundPath.empty())
|
if(!castSoundPath.empty())
|
||||||
ENGINE->sound().playSound(castSoundPath);
|
ENGINE->sound().playSound(castSoundPath);
|
||||||
|
|||||||
@@ -612,31 +612,14 @@ void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
|
|||||||
|
|
||||||
if(spellBeingCasted)
|
if(spellBeingCasted)
|
||||||
{
|
{
|
||||||
switch(spellBeingCasted->id.toEnum())
|
|
||||||
{
|
|
||||||
case SpellID::SCUTTLE_BOAT:
|
|
||||||
if(isValidAdventureSpellTarget(targetPosition))
|
|
||||||
ENGINE->cursor().set(Cursor::Map::SCUTTLE_BOAT);
|
|
||||||
else
|
|
||||||
ENGINE->cursor().set(Cursor::Map::POINTER);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case SpellID::DIMENSION_DOOR:
|
spells::detail::ProblemImpl problem;
|
||||||
if(isValidAdventureSpellTarget(targetPosition))
|
|
||||||
{
|
if(isValidAdventureSpellTarget(targetPosition))
|
||||||
if(GAME->interface()->cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS) && GAME->interface()->cb->isTileGuardedUnchecked(targetPosition))
|
ENGINE->cursor().set(spellBeingCasted->getAdventureMechanics().getCursorForTarget(GAME->interface()->cb.get(), GAME->interface()->localState->getCurrentHero(), targetPosition));
|
||||||
ENGINE->cursor().set(Cursor::Map::T1_ATTACK);
|
else
|
||||||
else
|
|
||||||
ENGINE->cursor().set(Cursor::Map::TELEPORT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ENGINE->cursor().set(Cursor::Map::POINTER);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
ENGINE->cursor().set(Cursor::Map::POINTER);
|
ENGINE->cursor().set(Cursor::Map::POINTER);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isTargetPositionVisible)
|
if(!isTargetPositionVisible)
|
||||||
|
|||||||
@@ -117,9 +117,6 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
|
|||||||
cellUnitMovementHighlight = ENGINE->renderHandler().loadImage(ImagePath::builtin("UnitMovementHighlight.PNG"), EImageBlitMode::COLORKEY);
|
cellUnitMovementHighlight = ENGINE->renderHandler().loadImage(ImagePath::builtin("UnitMovementHighlight.PNG"), EImageBlitMode::COLORKEY);
|
||||||
cellUnitMaxMovementHighlight = ENGINE->renderHandler().loadImage(ImagePath::builtin("UnitMaxMovementHighlight.PNG"), EImageBlitMode::COLORKEY);
|
cellUnitMaxMovementHighlight = ENGINE->renderHandler().loadImage(ImagePath::builtin("UnitMaxMovementHighlight.PNG"), EImageBlitMode::COLORKEY);
|
||||||
|
|
||||||
attackCursors = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT"), EImageBlitMode::COLORKEY);
|
|
||||||
spellCursors = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"), EImageBlitMode::COLORKEY);
|
|
||||||
|
|
||||||
rangedFullDamageLimitImages = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsGreen.json"), EImageBlitMode::COLORKEY);
|
rangedFullDamageLimitImages = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsGreen.json"), EImageBlitMode::COLORKEY);
|
||||||
shootingRangeLimitImages = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsRed.json"), EImageBlitMode::COLORKEY);
|
shootingRangeLimitImages = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsRed.json"), EImageBlitMode::COLORKEY);
|
||||||
|
|
||||||
@@ -861,24 +858,7 @@ void BattleFieldController::show(Canvas & to)
|
|||||||
renderBattlefield(to);
|
renderBattlefield(to);
|
||||||
|
|
||||||
if (isActive() && isGesturing() && getHoveredHex() != BattleHex::INVALID)
|
if (isActive() && isGesturing() && getHoveredHex() != BattleHex::INVALID)
|
||||||
{
|
to.draw(ENGINE->cursor().getCurrentImage(), hexPositionAbsolute(getHoveredHex()).center() - ENGINE->cursor().getPivotOffset());
|
||||||
auto combatCursorIndex = ENGINE->cursor().get<Cursor::Combat>();
|
|
||||||
if (combatCursorIndex)
|
|
||||||
{
|
|
||||||
auto combatImageIndex = static_cast<size_t>(*combatCursorIndex);
|
|
||||||
to.draw(attackCursors->getImage(combatImageIndex), hexPositionAbsolute(getHoveredHex()).center() - ENGINE->cursor().getPivotOffsetCombat(combatImageIndex));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto spellCursorIndex = ENGINE->cursor().get<Cursor::Spellcast>();
|
|
||||||
if (spellCursorIndex)
|
|
||||||
{
|
|
||||||
auto spellImageIndex = static_cast<size_t>(*spellCursorIndex);
|
|
||||||
to.draw(spellCursors->getImage(spellImageIndex), hexPositionAbsolute(getHoveredHex()).center() - ENGINE->cursor().getPivotOffsetSpellcast());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BattleFieldController::receiveEvent(const Point & position, int eventType) const
|
bool BattleFieldController::receiveEvent(const Point & position, int eventType) const
|
||||||
|
|||||||
@@ -37,9 +37,6 @@ class BattleFieldController : public CIntObject
|
|||||||
std::shared_ptr<CAnimation> rangedFullDamageLimitImages;
|
std::shared_ptr<CAnimation> rangedFullDamageLimitImages;
|
||||||
std::shared_ptr<CAnimation> shootingRangeLimitImages;
|
std::shared_ptr<CAnimation> shootingRangeLimitImages;
|
||||||
|
|
||||||
std::shared_ptr<CAnimation> attackCursors;
|
|
||||||
std::shared_ptr<CAnimation> spellCursors;
|
|
||||||
|
|
||||||
/// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack
|
/// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack
|
||||||
std::unique_ptr<Canvas> backgroundWithHexes;
|
std::unique_ptr<Canvas> backgroundWithHexes;
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "../render/IRenderHandler.h"
|
#include "../render/IRenderHandler.h"
|
||||||
|
|
||||||
#include "../../lib/CConfigHandler.h"
|
#include "../../lib/CConfigHandler.h"
|
||||||
|
#include "../../lib/json/JsonUtils.h"
|
||||||
|
|
||||||
std::unique_ptr<ICursor> CursorHandler::createCursor()
|
std::unique_ptr<ICursor> CursorHandler::createCursor()
|
||||||
{
|
{
|
||||||
@@ -42,8 +43,6 @@ CursorHandler::CursorHandler()
|
|||||||
, showing(false)
|
, showing(false)
|
||||||
, pos(0,0)
|
, pos(0,0)
|
||||||
, dndObject(nullptr)
|
, dndObject(nullptr)
|
||||||
, type(Cursor::Type::ADVENTURE)
|
|
||||||
, frame(0)
|
|
||||||
{
|
{
|
||||||
showType = dynamic_cast<CursorSoftware *>(cursor.get()) ? Cursor::ShowType::SOFTWARE : Cursor::ShowType::HARDWARE;
|
showType = dynamic_cast<CursorSoftware *>(cursor.get()) ? Cursor::ShowType::SOFTWARE : Cursor::ShowType::HARDWARE;
|
||||||
}
|
}
|
||||||
@@ -52,44 +51,154 @@ CursorHandler::~CursorHandler() = default;
|
|||||||
|
|
||||||
void CursorHandler::init()
|
void CursorHandler::init()
|
||||||
{
|
{
|
||||||
cursors =
|
JsonNode cursorConfig = JsonUtils::assembleFromFiles("config/cursors.json");
|
||||||
|
std::vector<AnimationPath> animations;
|
||||||
|
|
||||||
|
for (const auto & cursorEntry : cursorConfig.Struct())
|
||||||
{
|
{
|
||||||
ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRADVNTR"), EImageBlitMode::COLORKEY),
|
CursorParameters parameters;
|
||||||
ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT"), EImageBlitMode::COLORKEY),
|
parameters.cursorID = cursorEntry.first;
|
||||||
ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRDEFLT"), EImageBlitMode::COLORKEY),
|
parameters.image = ImagePath::fromJson(cursorEntry.second["image"]);
|
||||||
ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"), EImageBlitMode::COLORKEY)
|
parameters.animation = AnimationPath::fromJson(cursorEntry.second["animation"]);
|
||||||
};
|
parameters.animationFrameIndex = cursorEntry.second["frame"].Integer();
|
||||||
|
parameters.isAnimated = cursorEntry.second["animated"].Bool();
|
||||||
|
parameters.pivot.x = cursorEntry.second["pivotX"].Integer();
|
||||||
|
parameters.pivot.y = cursorEntry.second["pivotY"].Integer();
|
||||||
|
|
||||||
|
cursors.push_back(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Preload?
|
||||||
|
|
||||||
set(Cursor::Map::POINTER);
|
set(Cursor::Map::POINTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CursorHandler::changeGraphic(Cursor::Type type, size_t index)
|
void CursorHandler::set(const std::string & index)
|
||||||
{
|
{
|
||||||
assert(dndObject == nullptr);
|
assert(dndObject == nullptr);
|
||||||
|
|
||||||
if (type == this->type && index == this->frame)
|
if (index == currentCursorID)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->type = type;
|
currentCursorID = index;
|
||||||
this->frame = index;
|
currentCursorIndex = 0;
|
||||||
|
frameTime = 0;
|
||||||
|
for (size_t i = 0; i < cursors.size(); ++i)
|
||||||
|
{
|
||||||
|
if (cursors[i].cursorID == index)
|
||||||
|
{
|
||||||
|
currentCursorIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto & currentCursor = cursors.at(currentCursorIndex);
|
||||||
|
|
||||||
|
if (currentCursor.image.empty())
|
||||||
|
{
|
||||||
|
if (!loadedAnimations.count(currentCursor.animation))
|
||||||
|
loadedAnimations[currentCursor.animation] = ENGINE->renderHandler().loadAnimation(currentCursor.animation, EImageBlitMode::COLORKEY);
|
||||||
|
|
||||||
|
if (currentCursor.isAnimated)
|
||||||
|
cursorImage = loadedAnimations[currentCursor.animation]->getImage(0);
|
||||||
|
else
|
||||||
|
cursorImage = loadedAnimations[currentCursor.animation]->getImage(currentCursor.animationFrameIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!loadedImages.count(currentCursor.image))
|
||||||
|
loadedImages[currentCursor.image] = ENGINE->renderHandler().loadImage(currentCursor.image, EImageBlitMode::COLORKEY);
|
||||||
|
cursorImage = loadedImages[currentCursor.image];
|
||||||
|
}
|
||||||
|
|
||||||
cursor->setImage(getCurrentImage(), getPivotOffset());
|
cursor->setImage(getCurrentImage(), getPivotOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CursorHandler::set(Cursor::Map index)
|
void CursorHandler::set(Cursor::Map index)
|
||||||
{
|
{
|
||||||
changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
|
constexpr std::array mapCursorNames =
|
||||||
|
{
|
||||||
|
"mapPointer",
|
||||||
|
"mapHourglass",
|
||||||
|
"mapHero",
|
||||||
|
"mapTown",
|
||||||
|
"mapTurn1Move",
|
||||||
|
"mapTurn1Attack",
|
||||||
|
"mapTurn1Sail",
|
||||||
|
"mapTurn1Disembark",
|
||||||
|
"mapTurn1Exchange",
|
||||||
|
"mapTurn1Visit",
|
||||||
|
"mapTurn2Move",
|
||||||
|
"mapTurn2Attack",
|
||||||
|
"mapTurn2Sail",
|
||||||
|
"mapTurn2Disembark",
|
||||||
|
"mapTurn2Exchange",
|
||||||
|
"mapTurn2Visit",
|
||||||
|
"mapTurn3Move",
|
||||||
|
"mapTurn3Attack",
|
||||||
|
"mapTurn3Sail",
|
||||||
|
"mapTurn3Disembark",
|
||||||
|
"mapTurn3Exchange",
|
||||||
|
"mapTurn3Visit",
|
||||||
|
"mapTurn4Move",
|
||||||
|
"mapTurn4Attack",
|
||||||
|
"mapTurn4Sail",
|
||||||
|
"mapTurn4Disembark",
|
||||||
|
"mapTurn4Exchange",
|
||||||
|
"mapTurn4Visit",
|
||||||
|
"mapTurn1SailVisit",
|
||||||
|
"mapTurn2SailVisit",
|
||||||
|
"mapTurn3SailVisit",
|
||||||
|
"mapTurn4SailVisit",
|
||||||
|
"mapScrollNorth",
|
||||||
|
"mapScrollNorthEast",
|
||||||
|
"mapScrollEast",
|
||||||
|
"mapScrollSouthEast",
|
||||||
|
"mapScrollSouth",
|
||||||
|
"mapScrollSouthWest",
|
||||||
|
"mapScrollWest",
|
||||||
|
"mapScrollNorthWest",
|
||||||
|
"UNUSED",
|
||||||
|
"mapDimensionDoor",
|
||||||
|
"mapScuttleBoat"
|
||||||
|
};
|
||||||
|
|
||||||
|
set(mapCursorNames.at(static_cast<int>(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CursorHandler::set(Cursor::Combat index)
|
void CursorHandler::set(Cursor::Combat index)
|
||||||
{
|
{
|
||||||
changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
|
constexpr std::array combatCursorNames =
|
||||||
|
{
|
||||||
|
"combatBlocked",
|
||||||
|
"combatMove",
|
||||||
|
"combatFly",
|
||||||
|
"combatShoot",
|
||||||
|
"combatHero",
|
||||||
|
"combatQuery",
|
||||||
|
"combatPointer",
|
||||||
|
"combatHitNorthEast",
|
||||||
|
"combatHitEast",
|
||||||
|
"combatHitSouthEast",
|
||||||
|
"combatHitSouthWest",
|
||||||
|
"combatHitWest",
|
||||||
|
"combatHitNorthWest",
|
||||||
|
"combatHitNorth",
|
||||||
|
"combatHitSouth",
|
||||||
|
"combatShootPenalty",
|
||||||
|
"combatShootCatapult",
|
||||||
|
"combatHeal",
|
||||||
|
"combatSacrifice",
|
||||||
|
"combatTeleport"
|
||||||
|
};
|
||||||
|
|
||||||
|
set(combatCursorNames.at(static_cast<int>(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CursorHandler::set(Cursor::Spellcast index)
|
void CursorHandler::set(Cursor::Spellcast index)
|
||||||
{
|
{
|
||||||
//Note: this is animated cursor, ignore specified frame and only change type
|
//Note: this is animated cursor, ignore requested frame and only change type
|
||||||
changeGraphic(Cursor::Type::SPELLBOOK, frame);
|
set("castSpell");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
|
void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
|
||||||
@@ -112,114 +221,12 @@ void CursorHandler::cursorMove(const int & x, const int & y)
|
|||||||
cursor->setCursorPosition(pos);
|
cursor->setCursorPosition(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Point CursorHandler::getPivotOffsetMap(size_t index)
|
|
||||||
{
|
|
||||||
static const std::array<Point, 43> offsets = {{
|
|
||||||
{ 0, 0}, // POINTER = 0,
|
|
||||||
{ 0, 0}, // HOURGLASS = 1,
|
|
||||||
{ 12, 10}, // HERO = 2,
|
|
||||||
{ 12, 12}, // TOWN = 3,
|
|
||||||
|
|
||||||
{ 15, 13}, // T1_MOVE = 4,
|
|
||||||
{ 13, 13}, // T1_ATTACK = 5,
|
|
||||||
{ 16, 32}, // T1_SAIL = 6,
|
|
||||||
{ 13, 20}, // T1_DISEMBARK = 7,
|
|
||||||
{ 8, 9}, // T1_EXCHANGE = 8,
|
|
||||||
{ 14, 16}, // T1_VISIT = 9,
|
|
||||||
|
|
||||||
{ 15, 13}, // T2_MOVE = 10,
|
|
||||||
{ 13, 13}, // T2_ATTACK = 11,
|
|
||||||
{ 16, 32}, // T2_SAIL = 12,
|
|
||||||
{ 13, 20}, // T2_DISEMBARK = 13,
|
|
||||||
{ 8, 9}, // T2_EXCHANGE = 14,
|
|
||||||
{ 14, 16}, // T2_VISIT = 15,
|
|
||||||
|
|
||||||
{ 15, 13}, // T3_MOVE = 16,
|
|
||||||
{ 13, 13}, // T3_ATTACK = 17,
|
|
||||||
{ 16, 32}, // T3_SAIL = 18,
|
|
||||||
{ 13, 20}, // T3_DISEMBARK = 19,
|
|
||||||
{ 8, 9}, // T3_EXCHANGE = 20,
|
|
||||||
{ 14, 16}, // T3_VISIT = 21,
|
|
||||||
|
|
||||||
{ 15, 13}, // T4_MOVE = 22,
|
|
||||||
{ 13, 13}, // T4_ATTACK = 23,
|
|
||||||
{ 16, 32}, // T4_SAIL = 24,
|
|
||||||
{ 13, 20}, // T4_DISEMBARK = 25,
|
|
||||||
{ 8, 9}, // T4_EXCHANGE = 26,
|
|
||||||
{ 14, 16}, // T4_VISIT = 27,
|
|
||||||
|
|
||||||
{ 16, 32}, // T1_SAIL_VISIT = 28,
|
|
||||||
{ 16, 32}, // T2_SAIL_VISIT = 29,
|
|
||||||
{ 16, 32}, // T3_SAIL_VISIT = 30,
|
|
||||||
{ 16, 32}, // T4_SAIL_VISIT = 31,
|
|
||||||
|
|
||||||
{ 6, 1}, // SCROLL_NORTH = 32,
|
|
||||||
{ 16, 2}, // SCROLL_NORTHEAST = 33,
|
|
||||||
{ 21, 6}, // SCROLL_EAST = 34,
|
|
||||||
{ 16, 16}, // SCROLL_SOUTHEAST = 35,
|
|
||||||
{ 6, 21}, // SCROLL_SOUTH = 36,
|
|
||||||
{ 1, 16}, // SCROLL_SOUTHWEST = 37,
|
|
||||||
{ 1, 5}, // SCROLL_WEST = 38,
|
|
||||||
{ 2, 1}, // SCROLL_NORTHWEST = 39,
|
|
||||||
|
|
||||||
{ 0, 0}, // POINTER_COPY = 40,
|
|
||||||
{ 14, 16}, // TELEPORT = 41,
|
|
||||||
{ 20, 20}, // SCUTTLE_BOAT = 42
|
|
||||||
}};
|
|
||||||
|
|
||||||
assert(offsets.size() == size_t(Cursor::Map::COUNT)); //Invalid number of pivot offsets for cursor
|
|
||||||
assert(index < offsets.size());
|
|
||||||
return offsets[index] * ENGINE->screenHandler().getScalingFactor();
|
|
||||||
}
|
|
||||||
|
|
||||||
Point CursorHandler::getPivotOffsetCombat(size_t index)
|
|
||||||
{
|
|
||||||
static const std::array<Point, 20> offsets = {{
|
|
||||||
{ 12, 12 }, // BLOCKED = 0,
|
|
||||||
{ 10, 14 }, // MOVE = 1,
|
|
||||||
{ 14, 14 }, // FLY = 2,
|
|
||||||
{ 12, 12 }, // SHOOT = 3,
|
|
||||||
{ 12, 12 }, // HERO = 4,
|
|
||||||
{ 8, 12 }, // QUERY = 5,
|
|
||||||
{ 0, 0 }, // POINTER = 6,
|
|
||||||
{ 21, 0 }, // HIT_NORTHEAST = 7,
|
|
||||||
{ 31, 5 }, // HIT_EAST = 8,
|
|
||||||
{ 21, 21 }, // HIT_SOUTHEAST = 9,
|
|
||||||
{ 0, 21 }, // HIT_SOUTHWEST = 10,
|
|
||||||
{ 0, 5 }, // HIT_WEST = 11,
|
|
||||||
{ 0, 0 }, // HIT_NORTHWEST = 12,
|
|
||||||
{ 6, 0 }, // HIT_NORTH = 13,
|
|
||||||
{ 6, 31 }, // HIT_SOUTH = 14,
|
|
||||||
{ 14, 0 }, // SHOOT_PENALTY = 15,
|
|
||||||
{ 12, 12 }, // SHOOT_CATAPULT = 16,
|
|
||||||
{ 12, 12 }, // HEAL = 17,
|
|
||||||
{ 12, 12 }, // SACRIFICE = 18,
|
|
||||||
{ 14, 20 }, // TELEPORT = 19
|
|
||||||
}};
|
|
||||||
|
|
||||||
assert(offsets.size() == size_t(Cursor::Combat::COUNT)); //Invalid number of pivot offsets for cursor
|
|
||||||
assert(index < offsets.size());
|
|
||||||
return offsets[index] * ENGINE->screenHandler().getScalingFactor();
|
|
||||||
}
|
|
||||||
|
|
||||||
Point CursorHandler::getPivotOffsetSpellcast()
|
|
||||||
{
|
|
||||||
return Point(18, 28) * ENGINE->screenHandler().getScalingFactor();
|
|
||||||
}
|
|
||||||
|
|
||||||
Point CursorHandler::getPivotOffset()
|
Point CursorHandler::getPivotOffset()
|
||||||
{
|
{
|
||||||
if (dndObject)
|
if (dndObject)
|
||||||
return dndObject->dimensions() / 2;
|
return dndObject->dimensions() / 2;
|
||||||
|
|
||||||
switch (type) {
|
return cursors.at(currentCursorIndex).pivot;
|
||||||
case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame);
|
|
||||||
case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame);
|
|
||||||
case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast();
|
|
||||||
};
|
|
||||||
|
|
||||||
assert(0);
|
|
||||||
return {0, 0};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<IImage> CursorHandler::getCurrentImage()
|
std::shared_ptr<IImage> CursorHandler::getCurrentImage()
|
||||||
@@ -227,15 +234,17 @@ std::shared_ptr<IImage> CursorHandler::getCurrentImage()
|
|||||||
if (dndObject)
|
if (dndObject)
|
||||||
return dndObject;
|
return dndObject;
|
||||||
|
|
||||||
return cursors[static_cast<size_t>(type)]->getImage(frame);
|
return cursorImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CursorHandler::updateSpellcastCursor()
|
void CursorHandler::updateAnimatedCursor()
|
||||||
{
|
{
|
||||||
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
|
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
|
||||||
|
|
||||||
frameTime += ENGINE->framerate().getElapsedMilliseconds() / 1000.f;
|
frameTime += ENGINE->framerate().getElapsedMilliseconds() / 1000.f;
|
||||||
size_t newFrame = frame;
|
int32_t newFrame = currentFrame;
|
||||||
|
const auto & animationName = cursors.at(currentCursorIndex).animation;
|
||||||
|
const auto & animation = loadedAnimations.at(animationName);
|
||||||
|
|
||||||
while (frameTime >= frameDisplayDuration)
|
while (frameTime >= frameDisplayDuration)
|
||||||
{
|
{
|
||||||
@@ -243,12 +252,12 @@ void CursorHandler::updateSpellcastCursor()
|
|||||||
newFrame++;
|
newFrame++;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto & animation = cursors.at(static_cast<size_t>(type));
|
|
||||||
|
|
||||||
while (newFrame >= animation->size())
|
while (newFrame >= animation->size())
|
||||||
newFrame -= animation->size();
|
newFrame -= animation->size();
|
||||||
|
|
||||||
changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
|
currentFrame = newFrame;
|
||||||
|
cursorImage = animation->getImage(currentFrame);
|
||||||
|
cursor->setImage(getCurrentImage(), getPivotOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CursorHandler::render()
|
void CursorHandler::render()
|
||||||
@@ -261,8 +270,8 @@ void CursorHandler::update()
|
|||||||
if(!showing)
|
if(!showing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (type == Cursor::Type::SPELLBOOK)
|
if (cursors.at(currentCursorIndex).isAnimated)
|
||||||
updateSpellcastCursor();
|
updateAnimatedCursor();
|
||||||
|
|
||||||
cursor->update();
|
cursor->update();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,18 +18,18 @@ class CAnimation;
|
|||||||
|
|
||||||
namespace Cursor
|
namespace Cursor
|
||||||
{
|
{
|
||||||
enum class Type {
|
enum class Type : int8_t {
|
||||||
ADVENTURE, // set of various cursors for adventure map
|
ADVENTURE, // set of various cursors for adventure map
|
||||||
COMBAT, // set of various cursors for combat
|
COMBAT, // set of various cursors for combat
|
||||||
SPELLBOOK // animated cursor for spellcasting
|
SPELLBOOK // animated cursor for spellcasting
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ShowType {
|
enum class ShowType : int8_t {
|
||||||
SOFTWARE,
|
SOFTWARE,
|
||||||
HARDWARE
|
HARDWARE
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Combat {
|
enum class Combat : int8_t {
|
||||||
BLOCKED = 0,
|
BLOCKED = 0,
|
||||||
MOVE = 1,
|
MOVE = 1,
|
||||||
FLY = 2,
|
FLY = 2,
|
||||||
@@ -54,7 +54,7 @@ namespace Cursor
|
|||||||
COUNT
|
COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Map {
|
enum class Map : int8_t {
|
||||||
POINTER = 0,
|
POINTER = 0,
|
||||||
HOURGLASS = 1,
|
HOURGLASS = 1,
|
||||||
HERO = 2,
|
HERO = 2,
|
||||||
@@ -102,7 +102,7 @@ namespace Cursor
|
|||||||
COUNT
|
COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Spellcast {
|
enum class Spellcast : int8_t {
|
||||||
SPELL = 0,
|
SPELL = 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -110,26 +110,33 @@ namespace Cursor
|
|||||||
/// handles mouse cursor
|
/// handles mouse cursor
|
||||||
class CursorHandler final
|
class CursorHandler final
|
||||||
{
|
{
|
||||||
|
struct CursorParameters
|
||||||
|
{
|
||||||
|
std::string cursorID;
|
||||||
|
ImagePath image;
|
||||||
|
AnimationPath animation;
|
||||||
|
Point pivot;
|
||||||
|
int animationFrameIndex;
|
||||||
|
bool isAnimated;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<CursorParameters> cursors;
|
||||||
|
std::map<AnimationPath, std::shared_ptr<CAnimation>> loadedAnimations;
|
||||||
|
std::map<ImagePath, std::shared_ptr<IImage>> loadedImages;
|
||||||
|
|
||||||
std::shared_ptr<IImage> dndObject; //if set, overrides currentCursor
|
std::shared_ptr<IImage> dndObject; //if set, overrides currentCursor
|
||||||
|
std::shared_ptr<IImage> cursorImage; //if set, overrides currentCursor
|
||||||
std::array<std::shared_ptr<CAnimation>, 4> cursors;
|
|
||||||
|
|
||||||
bool showing;
|
|
||||||
|
|
||||||
/// Current cursor
|
/// Current cursor
|
||||||
Cursor::Type type;
|
std::string currentCursorID;
|
||||||
Cursor::ShowType showType;
|
|
||||||
size_t frame;
|
|
||||||
float frameTime;
|
|
||||||
Point pos;
|
Point pos;
|
||||||
|
float frameTime;
|
||||||
|
int32_t currentCursorIndex;
|
||||||
|
int32_t currentFrame;
|
||||||
|
Cursor::ShowType showType;
|
||||||
|
bool showing;
|
||||||
|
|
||||||
void changeGraphic(Cursor::Type type, size_t index);
|
void updateAnimatedCursor();
|
||||||
|
|
||||||
Point getPivotOffset();
|
|
||||||
|
|
||||||
void updateSpellcastCursor();
|
|
||||||
|
|
||||||
std::shared_ptr<IImage> getCurrentImage();
|
|
||||||
|
|
||||||
std::unique_ptr<ICursor> cursor;
|
std::unique_ptr<ICursor> cursor;
|
||||||
|
|
||||||
@@ -150,25 +157,10 @@ public:
|
|||||||
void set(Cursor::Map index);
|
void set(Cursor::Map index);
|
||||||
void set(Cursor::Combat index);
|
void set(Cursor::Combat index);
|
||||||
void set(Cursor::Spellcast index);
|
void set(Cursor::Spellcast index);
|
||||||
|
void set(const std::string & index);
|
||||||
|
|
||||||
/// Returns current index of cursor
|
std::shared_ptr<IImage> getCurrentImage();
|
||||||
template<typename Index>
|
Point getPivotOffset();
|
||||||
std::optional<Index> get()
|
|
||||||
{
|
|
||||||
bool typeValid = true;
|
|
||||||
|
|
||||||
typeValid &= (std::is_same<Index, Cursor::Map>::value )|| type != Cursor::Type::ADVENTURE;
|
|
||||||
typeValid &= (std::is_same<Index, Cursor::Combat>::value )|| type != Cursor::Type::COMBAT;
|
|
||||||
typeValid &= (std::is_same<Index, Cursor::Spellcast>::value )|| type != Cursor::Type::SPELLBOOK;
|
|
||||||
|
|
||||||
if (typeValid)
|
|
||||||
return static_cast<Index>(frame);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Point getPivotOffsetSpellcast();
|
|
||||||
Point getPivotOffsetMap(size_t index);
|
|
||||||
Point getPivotOffsetCombat(size_t index);
|
|
||||||
|
|
||||||
void render();
|
void render();
|
||||||
void update();
|
void update();
|
||||||
|
|||||||
66
config/cursors.json
Normal file
66
config/cursors.json
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"mapPointer" : { "pivotX" : 0, "pivotY" : 0, "animation" : "CRADVNTR", "frame" : 0 },
|
||||||
|
"mapHourglass" : { "pivotX" : 0, "pivotY" : 0, "animation" : "CRADVNTR", "frame" : 1 },
|
||||||
|
"mapHero" : { "pivotX" : 12, "pivotY" : 10, "animation" : "CRADVNTR", "frame" : 2 },
|
||||||
|
"mapTown" : { "pivotX" : 12, "pivotY" : 12, "animation" : "CRADVNTR", "frame" : 3 },
|
||||||
|
"mapTurn1Move" : { "pivotX" : 15, "pivotY" : 13, "animation" : "CRADVNTR", "frame" : 4 },
|
||||||
|
"mapTurn1Attack" : { "pivotX" : 13, "pivotY" : 13, "animation" : "CRADVNTR", "frame" : 5 },
|
||||||
|
"mapTurn1Sail" : { "pivotX" : 16, "pivotY" : 32, "animation" : "CRADVNTR", "frame" : 6 },
|
||||||
|
"mapTurn1Disembark" : { "pivotX" : 13, "pivotY" : 20, "animation" : "CRADVNTR", "frame" : 7 },
|
||||||
|
"mapTurn1Exchange" : { "pivotX" : 8, "pivotY" : 9, "animation" : "CRADVNTR", "frame" : 8 },
|
||||||
|
"mapTurn1Visit" : { "pivotX" : 14, "pivotY" : 16, "animation" : "CRADVNTR", "frame" : 9 },
|
||||||
|
"mapTurn2Move" : { "pivotX" : 15, "pivotY" : 13, "animation" : "CRADVNTR", "frame" : 10 },
|
||||||
|
"mapTurn2Attack" : { "pivotX" : 13, "pivotY" : 13, "animation" : "CRADVNTR", "frame" : 11 },
|
||||||
|
"mapTurn2Sail" : { "pivotX" : 16, "pivotY" : 32, "animation" : "CRADVNTR", "frame" : 12 },
|
||||||
|
"mapTurn2Disembark" : { "pivotX" : 13, "pivotY" : 20, "animation" : "CRADVNTR", "frame" : 13 },
|
||||||
|
"mapTurn2Exchange" : { "pivotX" : 8, "pivotY" : 9, "animation" : "CRADVNTR", "frame" : 14 },
|
||||||
|
"mapTurn2Visit" : { "pivotX" : 14, "pivotY" : 16, "animation" : "CRADVNTR", "frame" : 15 },
|
||||||
|
"mapTurn3Move" : { "pivotX" : 15, "pivotY" : 13, "animation" : "CRADVNTR", "frame" : 16 },
|
||||||
|
"mapTurn3Attack" : { "pivotX" : 13, "pivotY" : 13, "animation" : "CRADVNTR", "frame" : 17 },
|
||||||
|
"mapTurn3Sail" : { "pivotX" : 16, "pivotY" : 32, "animation" : "CRADVNTR", "frame" : 18 },
|
||||||
|
"mapTurn3Disembark" : { "pivotX" : 13, "pivotY" : 20, "animation" : "CRADVNTR", "frame" : 19 },
|
||||||
|
"mapTurn3Exchange" : { "pivotX" : 8, "pivotY" : 9, "animation" : "CRADVNTR", "frame" : 20 },
|
||||||
|
"mapTurn3Visit" : { "pivotX" : 14, "pivotY" : 16, "animation" : "CRADVNTR", "frame" : 21 },
|
||||||
|
"mapTurn4Move" : { "pivotX" : 15, "pivotY" : 13, "animation" : "CRADVNTR", "frame" : 22 },
|
||||||
|
"mapTurn4Attack" : { "pivotX" : 13, "pivotY" : 13, "animation" : "CRADVNTR", "frame" : 23 },
|
||||||
|
"mapTurn4Sail" : { "pivotX" : 16, "pivotY" : 32, "animation" : "CRADVNTR", "frame" : 24 },
|
||||||
|
"mapTurn4Disembark" : { "pivotX" : 13, "pivotY" : 20, "animation" : "CRADVNTR", "frame" : 25 },
|
||||||
|
"mapTurn4Exchange" : { "pivotX" : 8, "pivotY" : 9, "animation" : "CRADVNTR", "frame" : 26 },
|
||||||
|
"mapTurn4Visit" : { "pivotX" : 14, "pivotY" : 16, "animation" : "CRADVNTR", "frame" : 27 },
|
||||||
|
"mapTurn1SailVisit" : { "pivotX" : 16, "pivotY" : 32, "animation" : "CRADVNTR", "frame" : 28 },
|
||||||
|
"mapTurn2SailVisit" : { "pivotX" : 16, "pivotY" : 32, "animation" : "CRADVNTR", "frame" : 29 },
|
||||||
|
"mapTurn3SailVisit" : { "pivotX" : 16, "pivotY" : 32, "animation" : "CRADVNTR", "frame" : 30 },
|
||||||
|
"mapTurn4SailVisit" : { "pivotX" : 16, "pivotY" : 32, "animation" : "CRADVNTR", "frame" : 31 },
|
||||||
|
"mapScrollNorth" : { "pivotX" : 6, "pivotY" : 1, "animation" : "CRADVNTR", "frame" : 32 },
|
||||||
|
"mapScrollNorthEast" : { "pivotX" : 16, "pivotY" : 2, "animation" : "CRADVNTR", "frame" : 33 },
|
||||||
|
"mapScrollEast" : { "pivotX" : 21, "pivotY" : 6, "animation" : "CRADVNTR", "frame" : 34 },
|
||||||
|
"mapScrollSouthEast" : { "pivotX" : 16, "pivotY" : 16, "animation" : "CRADVNTR", "frame" : 35 },
|
||||||
|
"mapScrollSouth" : { "pivotX" : 6, "pivotY" : 21, "animation" : "CRADVNTR", "frame" : 36 },
|
||||||
|
"mapScrollSouthWest" : { "pivotX" : 1, "pivotY" : 16, "animation" : "CRADVNTR", "frame" : 37 },
|
||||||
|
"mapScrollWest" : { "pivotX" : 1, "pivotY" : 5, "animation" : "CRADVNTR", "frame" : 38 },
|
||||||
|
"mapScrollNorthWest" : { "pivotX" : 2, "pivotY" : 1, "animation" : "CRADVNTR", "frame" : 39 },
|
||||||
|
"mapDimensionDoor" : { "pivotX" : 14, "pivotY" : 16, "animation" : "CRADVNTR", "frame" : 41 },
|
||||||
|
"mapDimensionDoorGuarded" : { "pivotX" : 14, "pivotY" : 16, "image" : "CursrA51" },
|
||||||
|
"mapScuttleBoat" : { "pivotX" : 20, "pivotY" : 20, "animation" : "CRADVNTR", "frame" : 42 },
|
||||||
|
"combatBlocked" : { "pivotX" : 12, "pivotY" : 12, "animation" : "CRCOMBAT", "frame" : 0},
|
||||||
|
"combatMove" : { "pivotX" : 10, "pivotY" : 14, "animation" : "CRCOMBAT", "frame" : 1},
|
||||||
|
"combatFly" : { "pivotX" : 14, "pivotY" : 14, "animation" : "CRCOMBAT", "frame" : 2},
|
||||||
|
"combatShoot" : { "pivotX" : 12, "pivotY" : 12, "animation" : "CRCOMBAT", "frame" : 3},
|
||||||
|
"combatHero" : { "pivotX" : 12, "pivotY" : 12, "animation" : "CRCOMBAT", "frame" : 4},
|
||||||
|
"combatQuery" : { "pivotX" : 8, "pivotY" : 12, "animation" : "CRCOMBAT", "frame" : 5},
|
||||||
|
"combatPointer" : { "pivotX" : 0, "pivotY" : 0, "animation" : "CRCOMBAT", "frame" : 6},
|
||||||
|
"combatHitNorthEast" : { "pivotX" : 21, "pivotY" : 0, "animation" : "CRCOMBAT", "frame" : 7},
|
||||||
|
"combatHitEast" : { "pivotX" : 31, "pivotY" : 5, "animation" : "CRCOMBAT", "frame" : 8},
|
||||||
|
"combatHitSouthEast" : { "pivotX" : 21, "pivotY" : 21, "animation" : "CRCOMBAT", "frame" : 9},
|
||||||
|
"combatHitSouthWest" : { "pivotX" : 0, "pivotY" : 21, "animation" : "CRCOMBAT", "frame" : 10},
|
||||||
|
"combatHitWest" : { "pivotX" : 0, "pivotY" : 5, "animation" : "CRCOMBAT", "frame" : 11},
|
||||||
|
"combatHitNorthWest" : { "pivotX" : 0, "pivotY" : 0, "animation" : "CRCOMBAT", "frame" : 12},
|
||||||
|
"combatHitNorth" : { "pivotX" : 6, "pivotY" : 0, "animation" : "CRCOMBAT", "frame" : 13},
|
||||||
|
"combatHitSouth" : { "pivotX" : 6, "pivotY" : 31, "animation" : "CRCOMBAT", "frame" : 14},
|
||||||
|
"combatShootPenalty" : { "pivotX" : 14, "pivotY" : 0, "animation" : "CRCOMBAT", "frame" : 15},
|
||||||
|
"combatShootCatapult" : { "pivotX" : 12, "pivotY" : 12, "animation" : "CRCOMBAT", "frame" : 16},
|
||||||
|
"combatHeal" : { "pivotX" : 12, "pivotY" : 12, "animation" : "CRCOMBAT", "frame" : 17},
|
||||||
|
"combatSacrifice" : { "pivotX" : 12, "pivotY" : 12, "animation" : "CRCOMBAT", "frame" : 18},
|
||||||
|
"combatTeleport" : { "pivotX" : 14, "pivotY" : 20, "animation" : "CRCOMBAT", "frame" : 19},
|
||||||
|
"castSpell" : { "pivotX" : 18, "pivotY" : 28, "animation" : "CRSPELL", "animated" : true }
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
"adventureEffect" : {
|
"adventureEffect" : {
|
||||||
"type" : "removeObject",
|
"type" : "removeObject",
|
||||||
"castsPerDay" : 0,
|
"castsPerDay" : 0,
|
||||||
|
"cursor" : "mapScuttleBoat", // defined in config/cursors.json
|
||||||
"rangeX" : 9,
|
"rangeX" : 9,
|
||||||
"rangeY" : 8,
|
"rangeY" : 8,
|
||||||
"ignoreFow" : false,
|
"ignoreFow" : false,
|
||||||
@@ -375,6 +376,8 @@
|
|||||||
"movementPointsRequired" : 0,
|
"movementPointsRequired" : 0,
|
||||||
"movementPointsTaken" : 300,
|
"movementPointsTaken" : 300,
|
||||||
"waterLandFailureTakesPoints" : true,
|
"waterLandFailureTakesPoints" : true,
|
||||||
|
"cursor" : "mapDimensionDoor", // defined in config/cursors.json
|
||||||
|
"cursorGuarded" : "mapTurn1Attack", // defined in config/cursors.json
|
||||||
"castsPerDay" : 2,
|
"castsPerDay" : 2,
|
||||||
"rangeX" : 9,
|
"rangeX" : 9,
|
||||||
"rangeY" : 8,
|
"rangeY" : 8,
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ const TerrainTile * EditorCallback::getTileUnchecked(int3) const
|
|||||||
THROW_EDITOR_UNSUPPORTED;
|
THROW_EDITOR_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EditorCallback::isTileGuardedUnchecked(int3 tile) const
|
||||||
|
{
|
||||||
|
THROW_EDITOR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
const CGObjectInstance * EditorCallback::getTopObj(int3) const
|
const CGObjectInstance * EditorCallback::getTopObj(int3) const
|
||||||
{
|
{
|
||||||
THROW_EDITOR_UNSUPPORTED;
|
THROW_EDITOR_UNSUPPORTED;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
|
|
||||||
const TerrainTile * getTile(int3 tile, bool verbose) const override;
|
const TerrainTile * getTile(int3 tile, bool verbose) const override;
|
||||||
const TerrainTile * getTileUnchecked(int3 tile) const override;
|
const TerrainTile * getTileUnchecked(int3 tile) const override;
|
||||||
|
bool isTileGuardedUnchecked(int3 tile) const override;
|
||||||
const CGObjectInstance * getTopObj(int3 pos) const override;
|
const CGObjectInstance * getTopObj(int3 pos) const override;
|
||||||
EDiggingStatus getTileDigStatus(int3 tile, bool verbose) const override;
|
EDiggingStatus getTileDigStatus(int3 tile, bool verbose) const override;
|
||||||
void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override;
|
void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override;
|
||||||
|
|||||||
@@ -147,6 +147,8 @@ public:
|
|||||||
virtual bool checkForVisitableDir(const int3 & src, const int3 & dst) const = 0;
|
virtual bool checkForVisitableDir(const int3 & src, const int3 & dst) const = 0;
|
||||||
/// Returns all wandering monsters that guard specified tile
|
/// Returns all wandering monsters that guard specified tile
|
||||||
virtual std::vector<const CGObjectInstance *> getGuardingCreatures (int3 pos) const = 0;
|
virtual std::vector<const CGObjectInstance *> getGuardingCreatures (int3 pos) const = 0;
|
||||||
|
/// Returns if tile is guarded by wandering monsters without checking whether player has access to the tile. AVOID USAGE.
|
||||||
|
virtual bool isTileGuardedUnchecked(int3 tile) const = 0;
|
||||||
|
|
||||||
/// Returns all tiles within specified range with specific tile visibility mode
|
/// Returns all tiles within specified range with specific tile visibility mode
|
||||||
virtual void getTilesInRange(std::unordered_set<int3> & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player = std::optional<PlayerColor>(), int3::EDistanceFormula formula = int3::DIST_2D) const = 0;
|
virtual void getTilesInRange(std::unordered_set<int3> & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player = std::optional<PlayerColor>(), int3::EDistanceFormula formula = int3::DIST_2D) const = 0;
|
||||||
|
|||||||
@@ -355,6 +355,7 @@ public:
|
|||||||
virtual bool canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const = 0;
|
virtual bool canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const = 0;
|
||||||
virtual bool canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const = 0;
|
virtual bool canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const = 0;
|
||||||
virtual bool isTargetInRange(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const = 0;
|
virtual bool isTargetInRange(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const = 0;
|
||||||
|
virtual std::string getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const = 0;
|
||||||
|
|
||||||
virtual bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const = 0;
|
virtual bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public:
|
|||||||
virtual bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const {return true;};
|
virtual bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const {return true;};
|
||||||
virtual bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const {return true;};
|
virtual bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const {return true;};
|
||||||
virtual bool isTargetInRange(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const {return true;};
|
virtual bool isTargetInRange(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const {return true;};
|
||||||
|
virtual std::string getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const {return {};};
|
||||||
};
|
};
|
||||||
|
|
||||||
class AdventureSpellEffect final : public IAdventureSpellEffect
|
class AdventureSpellEffect final : public IAdventureSpellEffect
|
||||||
@@ -52,7 +53,8 @@ class AdventureSpellRangedEffect : public IAdventureSpellEffect
|
|||||||
public:
|
public:
|
||||||
AdventureSpellRangedEffect(const JsonNode & config);
|
AdventureSpellRangedEffect(const JsonNode & config);
|
||||||
|
|
||||||
bool isTargetInRange(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override;
|
bool isTargetInRange(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
||||||
|
std::string getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override = 0; //must be implemented in derived
|
||||||
};
|
};
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|||||||
@@ -130,6 +130,11 @@ bool AdventureSpellMechanics::isTargetInRange(spells::Problem & problem, const I
|
|||||||
return getLevel(caster).effect->isTargetInRange(problem, cb, caster, pos);
|
return getLevel(caster).effect->isTargetInRange(problem, cb, caster, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string AdventureSpellMechanics::getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const
|
||||||
|
{
|
||||||
|
return getLevel(caster).effect->getCursorForTarget(cb, caster, pos);
|
||||||
|
}
|
||||||
|
|
||||||
bool AdventureSpellMechanics::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
|
bool AdventureSpellMechanics::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
|
||||||
{
|
{
|
||||||
spells::detail::ProblemImpl problem;
|
spells::detail::ProblemImpl problem;
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public:
|
|||||||
bool canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const final;
|
bool canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const final;
|
||||||
bool canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
bool canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
||||||
bool isTargetInRange(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
bool isTargetInRange(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
||||||
|
std::string getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
||||||
bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const final;
|
bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const final;
|
||||||
|
|
||||||
void giveBonuses(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
|
void giveBonuses(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
DimensionDoorEffect::DimensionDoorEffect(const CSpell * s, const JsonNode & config)
|
DimensionDoorEffect::DimensionDoorEffect(const CSpell * s, const JsonNode & config)
|
||||||
: AdventureSpellRangedEffect(config)
|
: AdventureSpellRangedEffect(config)
|
||||||
|
, cursor(config["cursor"].String())
|
||||||
|
, cursorGuarded(config["cursorGuarded"].String())
|
||||||
, movementPointsRequired(config["movementPointsRequired"].Integer())
|
, movementPointsRequired(config["movementPointsRequired"].Integer())
|
||||||
, movementPointsTaken(config["movementPointsTaken"].Integer())
|
, movementPointsTaken(config["movementPointsTaken"].Integer())
|
||||||
, waterLandFailureTakesPoints(config["waterLandFailureTakesPoints"].Bool())
|
, waterLandFailureTakesPoints(config["waterLandFailureTakesPoints"].Bool())
|
||||||
@@ -30,6 +32,20 @@ DimensionDoorEffect::DimensionDoorEffect(const CSpell * s, const JsonNode & conf
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string DimensionDoorEffect::getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const
|
||||||
|
{
|
||||||
|
if(!cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS))
|
||||||
|
return cursor;
|
||||||
|
|
||||||
|
if (!exposeFow && !cb->isVisibleFor(pos, caster->getCasterOwner()))
|
||||||
|
return cursor;
|
||||||
|
|
||||||
|
if (!cb->isTileGuardedUnchecked(pos))
|
||||||
|
return cursor;
|
||||||
|
|
||||||
|
return cursorGuarded;
|
||||||
|
}
|
||||||
|
|
||||||
bool DimensionDoorEffect::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const
|
bool DimensionDoorEffect::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const
|
||||||
{
|
{
|
||||||
if(!caster->getHeroCaster())
|
if(!caster->getHeroCaster())
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
class DimensionDoorEffect final : public AdventureSpellRangedEffect
|
class DimensionDoorEffect final : public AdventureSpellRangedEffect
|
||||||
{
|
{
|
||||||
|
std::string cursor;
|
||||||
|
std::string cursorGuarded;
|
||||||
int movementPointsRequired;
|
int movementPointsRequired;
|
||||||
int movementPointsTaken;
|
int movementPointsTaken;
|
||||||
bool waterLandFailureTakesPoints;
|
bool waterLandFailureTakesPoints;
|
||||||
@@ -25,11 +27,12 @@ public:
|
|||||||
DimensionDoorEffect(const CSpell * s, const JsonNode & config);
|
DimensionDoorEffect(const CSpell * s, const JsonNode & config);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override;
|
bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const final;
|
||||||
bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override;
|
bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
||||||
|
|
||||||
ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override;
|
ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const final;
|
||||||
void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override;
|
void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const final;
|
||||||
|
std::string getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
||||||
};
|
};
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ RemoveObjectEffect::RemoveObjectEffect(const CSpell * s, const JsonNode & config
|
|||||||
: AdventureSpellRangedEffect(config)
|
: AdventureSpellRangedEffect(config)
|
||||||
, owner(s)
|
, owner(s)
|
||||||
, failMessage(MetaString::createFromTextID("core.genrltxt.337")) //%s tried to scuttle the boat, but failed
|
, failMessage(MetaString::createFromTextID("core.genrltxt.337")) //%s tried to scuttle the boat, but failed
|
||||||
|
, cursor(config["cursor"].String())
|
||||||
{
|
{
|
||||||
for(const auto & objectNode : config["objects"].Struct())
|
for(const auto & objectNode : config["objects"].Struct())
|
||||||
{
|
{
|
||||||
@@ -38,6 +39,11 @@ RemoveObjectEffect::RemoveObjectEffect(const CSpell * s, const JsonNode & config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string RemoveObjectEffect::getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const
|
||||||
|
{
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
bool RemoveObjectEffect::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const
|
bool RemoveObjectEffect::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const
|
||||||
{
|
{
|
||||||
if (!isTargetInRange(problem, cb, caster, pos))
|
if (!isTargetInRange(problem, cb, caster, pos))
|
||||||
|
|||||||
@@ -19,13 +19,14 @@ class RemoveObjectEffect final : public AdventureSpellRangedEffect
|
|||||||
const CSpell * owner;
|
const CSpell * owner;
|
||||||
std::vector<MapObjectID> removedObjects;
|
std::vector<MapObjectID> removedObjects;
|
||||||
MetaString failMessage;
|
MetaString failMessage;
|
||||||
ImagePath cursorPath;
|
std::string cursor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RemoveObjectEffect(const CSpell * s, const JsonNode & config);
|
RemoveObjectEffect(const CSpell * s, const JsonNode & config);
|
||||||
|
|
||||||
bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override;
|
bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
||||||
ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override;
|
ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const final;
|
||||||
|
std::string getCursorForTarget(const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final;
|
||||||
};
|
};
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ public:
|
|||||||
SummonBoatEffect(const CSpell * s, const JsonNode & config);
|
SummonBoatEffect(const CSpell * s, const JsonNode & config);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override;
|
bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const final;
|
||||||
|
|
||||||
ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override;
|
ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const final;
|
||||||
};
|
};
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ public:
|
|||||||
MOCK_CONST_METHOD1(guardingCreaturePosition, int3(int3 pos));
|
MOCK_CONST_METHOD1(guardingCreaturePosition, int3(int3 pos));
|
||||||
MOCK_CONST_METHOD2(checkForVisitableDir, bool(const int3 & src, const int3 & dst));
|
MOCK_CONST_METHOD2(checkForVisitableDir, bool(const int3 & src, const int3 & dst));
|
||||||
MOCK_CONST_METHOD1(getGuardingCreatures, std::vector<const CGObjectInstance *>(int3 pos));
|
MOCK_CONST_METHOD1(getGuardingCreatures, std::vector<const CGObjectInstance *>(int3 pos));
|
||||||
|
MOCK_CONST_METHOD1(isTileGuardedUnchecked, bool(int3 pos));
|
||||||
|
|
||||||
MOCK_METHOD2(pickAllowedArtsSet, void(std::vector<ArtifactID> & out, vstd::RNG & rand));
|
MOCK_METHOD2(pickAllowedArtsSet, void(std::vector<ArtifactID> & out, vstd::RNG & rand));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user