1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00
Files
vcmi/client/gui/CursorHandler.cpp

324 lines
7.3 KiB
C++
Raw Normal View History

/*
* CCursorHandler.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CursorHandler.h"
#include "GameEngine.h"
#include "FramerateManager.h"
#include "../renderSDL/CursorSoftware.h"
#include "../renderSDL/CursorHardware.h"
#include "../render/CAnimation.h"
#include "../render/IImage.h"
#include "../render/IScreenHandler.h"
#include "../render/IRenderHandler.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/json/JsonUtils.h"
std::unique_ptr<ICursor> CursorHandler::createCursor()
{
2025-02-16 22:29:07 +01:00
#if defined(VCMI_MOBILE) || defined(VCMI_PORTMASTER)
if (settings["general"]["userRelativePointer"].Bool())
return std::make_unique<CursorSoftware>();
#endif
if (settings["video"]["cursor"].String() == "hardware")
return std::make_unique<CursorHardware>();
assert(settings["video"]["cursor"].String() == "software");
return std::make_unique<CursorSoftware>();
}
CursorHandler::CursorHandler()
: cursor(createCursor())
, frameTime(0.f)
, showing(false)
, pos(0,0)
, dndObject(nullptr)
{
showType = dynamic_cast<CursorSoftware *>(cursor.get()) ? Cursor::ShowType::SOFTWARE : Cursor::ShowType::HARDWARE;
}
CursorHandler::~CursorHandler() = default;
void CursorHandler::init()
{
JsonNode cursorConfig = JsonUtils::assembleFromFiles("config/cursors.json");
std::vector<AnimationPath> animations;
for (const auto & cursorEntry : cursorConfig.Struct())
{
CursorParameters parameters;
parameters.cursorID = cursorEntry.first;
parameters.image = ImagePath::fromJson(cursorEntry.second["image"]);
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);
}
set(Cursor::Map::POINTER);
}
void CursorHandler::set(const std::string & index)
{
assert(dndObject == nullptr);
if (index == currentCursorID)
return;
currentCursorID = 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());
}
void CursorHandler::set(Cursor::Map 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)
{
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)
{
//Note: this is animated cursor, ignore requested frame and only change type
set("castSpell");
}
void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
{
dndObject = image;
cursor->setImage(getCurrentImage(), getPivotOffset());
}
void CursorHandler::dragAndDropCursor (const AnimationPath & path, size_t index)
{
auto anim = ENGINE->renderHandler().loadAnimation(path, EImageBlitMode::COLORKEY);
dragAndDropCursor(anim->getImage(index));
}
void CursorHandler::cursorMove(const int & x, const int & y)
{
pos.x = x;
pos.y = y;
cursor->setCursorPosition(pos);
}
Point CursorHandler::getPivotOffset()
{
if (dndObject)
return dndObject->dimensions() / 2;
return cursors.at(currentCursorIndex).pivot;
}
std::shared_ptr<IImage> CursorHandler::getCurrentImage()
{
if (dndObject)
return dndObject;
return cursorImage;
}
void CursorHandler::updateAnimatedCursor()
{
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
frameTime += ENGINE->framerate().getElapsedMilliseconds() / 1000.f;
int32_t newFrame = currentFrame;
const auto & animationName = cursors.at(currentCursorIndex).animation;
const auto & animation = loadedAnimations.at(animationName);
while (frameTime >= frameDisplayDuration)
{
frameTime -= frameDisplayDuration;
newFrame++;
}
while (newFrame >= animation->size())
newFrame -= animation->size();
currentFrame = newFrame;
cursorImage = animation->getImage(currentFrame);
cursor->setImage(getCurrentImage(), getPivotOffset());
}
void CursorHandler::render()
{
cursor->render();
}
void CursorHandler::update()
{
if(!showing)
return;
if (cursors.at(currentCursorIndex).isAnimated)
updateAnimatedCursor();
cursor->update();
}
void CursorHandler::hide()
{
if (!showing)
return;
showing = false;
cursor->setVisible(false);
}
void CursorHandler::show()
{
if (showing)
return;
showing = true;
cursor->setVisible(true);
}
2024-04-30 16:22:35 +03:00
Cursor::ShowType CursorHandler::getShowType() const
{
2024-04-30 11:38:13 +03:00
return showType;
}
2024-04-30 16:22:35 +03:00
void CursorHandler::changeCursor(Cursor::ShowType newShowType)
{
2024-04-30 16:22:35 +03:00
if(newShowType == showType)
2024-04-30 11:38:13 +03:00
return;
2024-04-30 16:22:35 +03:00
switch(newShowType)
2024-04-30 11:38:13 +03:00
{
case Cursor::ShowType::SOFTWARE:
cursor.reset(new CursorSoftware());
showType = Cursor::ShowType::SOFTWARE;
cursor->setImage(getCurrentImage(), getPivotOffset());
break;
case Cursor::ShowType::HARDWARE:
cursor.reset(new CursorHardware());
showType = Cursor::ShowType::HARDWARE;
cursor->setImage(getCurrentImage(), getPivotOffset());
break;
}
}
2024-10-01 19:40:12 +00:00
void CursorHandler::onScreenResize()
{
cursor->setImage(getCurrentImage(), getPivotOffset());
}