mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Finish split of cursors files
This commit is contained in:
commit
3f1b1095e2
458
client/gui/CursorHardware.cpp
Normal file
458
client/gui/CursorHardware.cpp
Normal file
@ -0,0 +1,458 @@
|
||||
/*
|
||||
* 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 "SDL_Extensions.h"
|
||||
#include "CGuiHandler.h"
|
||||
#include "CAnimation.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
||||
#include <SDL_render.h>
|
||||
#include <SDL_events.h>
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
std::unique_ptr<ICursor> CursorHandler::createCursor()
|
||||
{
|
||||
if (settings["video"]["cursor"].String() == "auto")
|
||||
{
|
||||
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
|
||||
return std::make_unique<CursorSoftware>();
|
||||
#else
|
||||
return std::make_unique<CursorHardware>();
|
||||
#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)
|
||||
{
|
||||
|
||||
type = Cursor::Type::DEFAULT;
|
||||
dndObject = nullptr;
|
||||
|
||||
cursors =
|
||||
{
|
||||
std::make_unique<CAnimation>("CRADVNTR"),
|
||||
std::make_unique<CAnimation>("CRCOMBAT"),
|
||||
std::make_unique<CAnimation>("CRDEFLT"),
|
||||
std::make_unique<CAnimation>("CRSPELL")
|
||||
};
|
||||
|
||||
for (auto & cursor : cursors)
|
||||
cursor->preload();
|
||||
|
||||
set(Cursor::Map::POINTER);
|
||||
}
|
||||
|
||||
Point CursorHandler::position() const
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
void CursorHandler::changeGraphic(Cursor::Type type, size_t index)
|
||||
{
|
||||
assert(dndObject == nullptr);
|
||||
|
||||
if (type == this->type && index == this->frame)
|
||||
return;
|
||||
|
||||
this->type = type;
|
||||
this->frame = index;
|
||||
|
||||
cursor->setImage(getCurrentImage(), getPivotOffset());
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Default index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Map index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Combat index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Spellcast index)
|
||||
{
|
||||
//Note: this is animated cursor, ignore specified frame and only change type
|
||||
changeGraphic(Cursor::Type::SPELLBOOK, frame);
|
||||
}
|
||||
|
||||
void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
|
||||
{
|
||||
dndObject = image;
|
||||
cursor->setImage(getCurrentImage(), getPivotOffset());
|
||||
}
|
||||
|
||||
void CursorHandler::dragAndDropCursor (std::string path, size_t index)
|
||||
{
|
||||
CAnimation anim(path);
|
||||
anim.load(index);
|
||||
dragAndDropCursor(anim.getImage(index));
|
||||
}
|
||||
|
||||
void CursorHandler::cursorMove(const int & x, const int & y)
|
||||
{
|
||||
pos.x = x;
|
||||
pos.y = y;
|
||||
|
||||
cursor->setCursorPosition(pos);
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffsetDefault(size_t index)
|
||||
{
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffsetSpellcast()
|
||||
{
|
||||
return { 18, 28};
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffset()
|
||||
{
|
||||
if (dndObject)
|
||||
return dndObject->dimensions() / 2;
|
||||
|
||||
switch (type) {
|
||||
case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame);
|
||||
case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame);
|
||||
case Cursor::Type::DEFAULT: return getPivotOffsetDefault(frame);
|
||||
case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast();
|
||||
};
|
||||
|
||||
assert(0);
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> CursorHandler::getCurrentImage()
|
||||
{
|
||||
if (dndObject)
|
||||
return dndObject;
|
||||
|
||||
return cursors[static_cast<size_t>(type)]->getImage(frame);
|
||||
}
|
||||
|
||||
void CursorHandler::centerCursor()
|
||||
{
|
||||
Point screenSize {screen->w, screen->h};
|
||||
pos = screenSize / 2 - getPivotOffset();
|
||||
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
|
||||
CSDL_Ext::warpMouse(pos.x, pos.y);
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
|
||||
|
||||
cursor->setCursorPosition(pos);
|
||||
}
|
||||
|
||||
void CursorHandler::updateSpellcastCursor()
|
||||
{
|
||||
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
|
||||
|
||||
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
size_t newFrame = frame;
|
||||
|
||||
while (frameTime >= frameDisplayDuration)
|
||||
{
|
||||
frameTime -= frameDisplayDuration;
|
||||
newFrame++;
|
||||
}
|
||||
|
||||
auto & animation = cursors.at(static_cast<size_t>(type));
|
||||
|
||||
while (newFrame >= animation->size())
|
||||
newFrame -= animation->size();
|
||||
|
||||
changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
|
||||
}
|
||||
|
||||
void CursorHandler::render()
|
||||
{
|
||||
if(!showing)
|
||||
return;
|
||||
|
||||
if (type == Cursor::Type::SPELLBOOK)
|
||||
updateSpellcastCursor();
|
||||
|
||||
cursor->render();
|
||||
}
|
||||
|
||||
void CursorHandler::hide()
|
||||
{
|
||||
if (!showing)
|
||||
return;
|
||||
|
||||
showing = false;
|
||||
cursor->setVisible(false);
|
||||
}
|
||||
|
||||
void CursorHandler::show()
|
||||
{
|
||||
if (showing)
|
||||
return;
|
||||
|
||||
showing = true;
|
||||
cursor->setVisible(true);
|
||||
}
|
||||
|
||||
void CursorSoftware::render()
|
||||
{
|
||||
//texture must be updated in the main (renderer) thread, but changes to cursor type may come from other threads
|
||||
if (needUpdate)
|
||||
updateTexture();
|
||||
|
||||
Point renderPos = pos - pivot;
|
||||
|
||||
SDL_Rect destRect;
|
||||
destRect.x = renderPos.x;
|
||||
destRect.y = renderPos.y;
|
||||
destRect.w = 40;
|
||||
destRect.h = 40;
|
||||
|
||||
SDL_RenderCopy(mainRenderer, cursorTexture, nullptr, &destRect);
|
||||
}
|
||||
|
||||
void CursorSoftware::createTexture(const Point & dimensions)
|
||||
{
|
||||
if(cursorTexture)
|
||||
SDL_DestroyTexture(cursorTexture);
|
||||
|
||||
if (cursorSurface)
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
|
||||
cursorSurface = CSDL_Ext::newSurface(dimensions.x, dimensions.y);
|
||||
cursorTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, dimensions.x, dimensions.y);
|
||||
|
||||
SDL_SetSurfaceBlendMode(cursorSurface, SDL_BLENDMODE_NONE);
|
||||
SDL_SetTextureBlendMode(cursorTexture, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
void CursorSoftware::updateTexture()
|
||||
{
|
||||
Point dimensions(-1, -1);
|
||||
|
||||
if (!cursorSurface || Point(cursorSurface->w, cursorSurface->h) != cursorImage->dimensions())
|
||||
createTexture(cursorImage->dimensions());
|
||||
|
||||
CSDL_Ext::fillSurface(cursorSurface, Colors::TRANSPARENCY);
|
||||
|
||||
cursorImage->draw(cursorSurface);
|
||||
SDL_UpdateTexture(cursorTexture, NULL, cursorSurface->pixels, cursorSurface->pitch);
|
||||
needUpdate = false;
|
||||
}
|
||||
|
||||
void CursorSoftware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
|
||||
{
|
||||
assert(image != nullptr);
|
||||
cursorImage = image;
|
||||
pivot = pivotOffset;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
void CursorSoftware::setCursorPosition( const Point & newPos )
|
||||
{
|
||||
pos = newPos;
|
||||
}
|
||||
|
||||
void CursorSoftware::setVisible(bool on)
|
||||
{
|
||||
visible = on;
|
||||
}
|
||||
|
||||
CursorSoftware::CursorSoftware():
|
||||
cursorTexture(nullptr),
|
||||
cursorSurface(nullptr),
|
||||
needUpdate(false),
|
||||
visible(false),
|
||||
pivot(0,0)
|
||||
{
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
|
||||
CursorSoftware::~CursorSoftware()
|
||||
{
|
||||
if(cursorTexture)
|
||||
SDL_DestroyTexture(cursorTexture);
|
||||
|
||||
if (cursorSurface)
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
}
|
||||
|
||||
CursorHardware::CursorHardware():
|
||||
cursor(nullptr)
|
||||
{
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
|
||||
CursorHardware::~CursorHardware()
|
||||
{
|
||||
if(cursor)
|
||||
SDL_FreeCursor(cursor);
|
||||
}
|
||||
|
||||
void CursorHardware::setVisible(bool on)
|
||||
{
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
if (on)
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
else
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void CursorHardware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
|
||||
{
|
||||
auto cursorSurface = CSDL_Ext::newSurface(image->dimensions().x, image->dimensions().y);
|
||||
|
||||
CSDL_Ext::fillSurface(cursorSurface, Colors::TRANSPARENCY);
|
||||
|
||||
image->draw(cursorSurface);
|
||||
|
||||
auto oldCursor = cursor;
|
||||
cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y);
|
||||
|
||||
if (!cursor)
|
||||
logGlobal->error("Failed to set cursor! SDL says %s", SDL_GetError());
|
||||
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
SDL_SetCursor(cursor);
|
||||
|
||||
if (oldCursor)
|
||||
SDL_FreeCursor(oldCursor);
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void CursorHardware::setCursorPosition( const Point & newPos )
|
||||
{
|
||||
//no-op
|
||||
}
|
||||
|
||||
void CursorHardware::render()
|
||||
{
|
||||
//no-op
|
||||
}
|
237
client/gui/CursorHardware.h
Normal file
237
client/gui/CursorHardware.h
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* CCursorHandler.h, 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class CAnimation;
|
||||
class IImage;
|
||||
struct SDL_Surface;
|
||||
struct SDL_Texture;
|
||||
struct SDL_Cursor;
|
||||
|
||||
#include "../../lib/Point.h"
|
||||
|
||||
namespace Cursor
|
||||
{
|
||||
enum class Type {
|
||||
ADVENTURE, // set of various cursors for adventure map
|
||||
COMBAT, // set of various cursors for combat
|
||||
DEFAULT, // default arrow and hourglass cursors
|
||||
SPELLBOOK // animated cursor for spellcasting
|
||||
};
|
||||
|
||||
enum class Default {
|
||||
POINTER = 0,
|
||||
//ARROW_COPY = 1, // probably unused
|
||||
HOURGLASS = 2,
|
||||
};
|
||||
|
||||
enum class Combat {
|
||||
INVALID = -1,
|
||||
|
||||
BLOCKED = 0,
|
||||
MOVE = 1,
|
||||
FLY = 2,
|
||||
SHOOT = 3,
|
||||
HERO = 4,
|
||||
QUERY = 5,
|
||||
POINTER = 6,
|
||||
HIT_NORTHEAST = 7,
|
||||
HIT_EAST = 8,
|
||||
HIT_SOUTHEAST = 9,
|
||||
HIT_SOUTHWEST = 10,
|
||||
HIT_WEST = 11,
|
||||
HIT_NORTHWEST = 12,
|
||||
HIT_NORTH = 13,
|
||||
HIT_SOUTH = 14,
|
||||
SHOOT_PENALTY = 15,
|
||||
SHOOT_CATAPULT = 16,
|
||||
HEAL = 17,
|
||||
SACRIFICE = 18,
|
||||
TELEPORT = 19,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
enum class Map {
|
||||
POINTER = 0,
|
||||
HOURGLASS = 1,
|
||||
HERO = 2,
|
||||
TOWN = 3,
|
||||
T1_MOVE = 4,
|
||||
T1_ATTACK = 5,
|
||||
T1_SAIL = 6,
|
||||
T1_DISEMBARK = 7,
|
||||
T1_EXCHANGE = 8,
|
||||
T1_VISIT = 9,
|
||||
T2_MOVE = 10,
|
||||
T2_ATTACK = 11,
|
||||
T2_SAIL = 12,
|
||||
T2_DISEMBARK = 13,
|
||||
T2_EXCHANGE = 14,
|
||||
T2_VISIT = 15,
|
||||
T3_MOVE = 16,
|
||||
T3_ATTACK = 17,
|
||||
T3_SAIL = 18,
|
||||
T3_DISEMBARK = 19,
|
||||
T3_EXCHANGE = 20,
|
||||
T3_VISIT = 21,
|
||||
T4_MOVE = 22,
|
||||
T4_ATTACK = 23,
|
||||
T4_SAIL = 24,
|
||||
T4_DISEMBARK = 25,
|
||||
T4_EXCHANGE = 26,
|
||||
T4_VISIT = 27,
|
||||
T1_SAIL_VISIT = 28,
|
||||
T2_SAIL_VISIT = 29,
|
||||
T3_SAIL_VISIT = 30,
|
||||
T4_SAIL_VISIT = 31,
|
||||
SCROLL_NORTH = 32,
|
||||
SCROLL_NORTHEAST = 33,
|
||||
SCROLL_EAST = 34,
|
||||
SCROLL_SOUTHEAST = 35,
|
||||
SCROLL_SOUTH = 36,
|
||||
SCROLL_SOUTHWEST = 37,
|
||||
SCROLL_WEST = 38,
|
||||
SCROLL_NORTHWEST = 39,
|
||||
//POINTER_COPY = 40, // probably unused
|
||||
TELEPORT = 41,
|
||||
SCUTTLE_BOAT = 42,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
enum class Spellcast {
|
||||
SPELL = 0,
|
||||
};
|
||||
}
|
||||
|
||||
class ICursor
|
||||
{
|
||||
public:
|
||||
virtual ~ICursor() = default;
|
||||
|
||||
virtual void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) = 0;
|
||||
virtual void setCursorPosition( const Point & newPos ) = 0;
|
||||
virtual void render() = 0;
|
||||
virtual void setVisible( bool on) = 0;
|
||||
};
|
||||
|
||||
class CursorHardware : public ICursor
|
||||
{
|
||||
std::shared_ptr<IImage> cursorImage;
|
||||
|
||||
SDL_Cursor * cursor;
|
||||
|
||||
public:
|
||||
CursorHardware();
|
||||
~CursorHardware();
|
||||
|
||||
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
|
||||
void setCursorPosition( const Point & newPos ) override;
|
||||
void render() override;
|
||||
void setVisible( bool on) override;
|
||||
};
|
||||
|
||||
class CursorSoftware : public ICursor
|
||||
{
|
||||
std::shared_ptr<IImage> cursorImage;
|
||||
|
||||
SDL_Texture * cursorTexture;
|
||||
SDL_Surface * cursorSurface;
|
||||
|
||||
Point pos;
|
||||
Point pivot;
|
||||
bool needUpdate;
|
||||
bool visible;
|
||||
|
||||
void createTexture(const Point & dimensions);
|
||||
void updateTexture();
|
||||
public:
|
||||
CursorSoftware();
|
||||
~CursorSoftware();
|
||||
|
||||
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
|
||||
void setCursorPosition( const Point & newPos ) override;
|
||||
void render() override;
|
||||
void setVisible( bool on) override;
|
||||
};
|
||||
|
||||
/// handles mouse cursor
|
||||
class CursorHandler final
|
||||
{
|
||||
std::shared_ptr<IImage> dndObject; //if set, overrides currentCursor
|
||||
|
||||
std::array<std::unique_ptr<CAnimation>, 4> cursors;
|
||||
|
||||
bool showing;
|
||||
|
||||
/// Current cursor
|
||||
Cursor::Type type;
|
||||
size_t frame;
|
||||
float frameTime;
|
||||
Point pos;
|
||||
|
||||
void changeGraphic(Cursor::Type type, size_t index);
|
||||
|
||||
Point getPivotOffsetDefault(size_t index);
|
||||
Point getPivotOffsetMap(size_t index);
|
||||
Point getPivotOffsetCombat(size_t index);
|
||||
Point getPivotOffsetSpellcast();
|
||||
Point getPivotOffset();
|
||||
|
||||
void updateSpellcastCursor();
|
||||
|
||||
std::shared_ptr<IImage> getCurrentImage();
|
||||
|
||||
std::unique_ptr<ICursor> cursor;
|
||||
|
||||
static std::unique_ptr<ICursor> createCursor();
|
||||
public:
|
||||
CursorHandler();
|
||||
~CursorHandler();
|
||||
|
||||
/// Replaces the cursor with a custom image.
|
||||
/// @param image Image to replace cursor with or nullptr to use the normal cursor.
|
||||
void dragAndDropCursor(std::shared_ptr<IImage> image);
|
||||
|
||||
void dragAndDropCursor(std::string path, size_t index);
|
||||
|
||||
/// Returns current position of the cursor
|
||||
Point position() const;
|
||||
|
||||
/// Changes cursor to specified index
|
||||
void set(Cursor::Default index);
|
||||
void set(Cursor::Map index);
|
||||
void set(Cursor::Combat index);
|
||||
void set(Cursor::Spellcast index);
|
||||
|
||||
/// Returns current index of cursor
|
||||
template<typename Index>
|
||||
Index get()
|
||||
{
|
||||
assert((std::is_same<Index, Cursor::Default>::value )|| type != Cursor::Type::DEFAULT );
|
||||
assert((std::is_same<Index, Cursor::Map>::value )|| type != Cursor::Type::ADVENTURE );
|
||||
assert((std::is_same<Index, Cursor::Combat>::value )|| type != Cursor::Type::COMBAT );
|
||||
assert((std::is_same<Index, Cursor::Spellcast>::value )|| type != Cursor::Type::SPELLBOOK );
|
||||
|
||||
return static_cast<Index>(frame);
|
||||
}
|
||||
|
||||
void render();
|
||||
|
||||
void hide();
|
||||
void show();
|
||||
|
||||
/// change cursor's positions to (x, y)
|
||||
void cursorMove(const int & x, const int & y);
|
||||
/// Move cursor to screen center
|
||||
void centerCursor();
|
||||
|
||||
};
|
458
client/gui/CursorSoftware.cpp
Normal file
458
client/gui/CursorSoftware.cpp
Normal file
@ -0,0 +1,458 @@
|
||||
/*
|
||||
* 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 "SDL_Extensions.h"
|
||||
#include "CGuiHandler.h"
|
||||
#include "CAnimation.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
||||
#include <SDL_render.h>
|
||||
#include <SDL_events.h>
|
||||
|
||||
#ifdef VCMI_APPLE
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
std::unique_ptr<ICursor> CursorHandler::createCursor()
|
||||
{
|
||||
if (settings["video"]["cursor"].String() == "auto")
|
||||
{
|
||||
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
|
||||
return std::make_unique<CursorSoftware>();
|
||||
#else
|
||||
return std::make_unique<CursorHardware>();
|
||||
#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)
|
||||
{
|
||||
|
||||
type = Cursor::Type::DEFAULT;
|
||||
dndObject = nullptr;
|
||||
|
||||
cursors =
|
||||
{
|
||||
std::make_unique<CAnimation>("CRADVNTR"),
|
||||
std::make_unique<CAnimation>("CRCOMBAT"),
|
||||
std::make_unique<CAnimation>("CRDEFLT"),
|
||||
std::make_unique<CAnimation>("CRSPELL")
|
||||
};
|
||||
|
||||
for (auto & cursor : cursors)
|
||||
cursor->preload();
|
||||
|
||||
set(Cursor::Map::POINTER);
|
||||
}
|
||||
|
||||
Point CursorHandler::position() const
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
void CursorHandler::changeGraphic(Cursor::Type type, size_t index)
|
||||
{
|
||||
assert(dndObject == nullptr);
|
||||
|
||||
if (type == this->type && index == this->frame)
|
||||
return;
|
||||
|
||||
this->type = type;
|
||||
this->frame = index;
|
||||
|
||||
cursor->setImage(getCurrentImage(), getPivotOffset());
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Default index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Map index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Combat index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Spellcast index)
|
||||
{
|
||||
//Note: this is animated cursor, ignore specified frame and only change type
|
||||
changeGraphic(Cursor::Type::SPELLBOOK, frame);
|
||||
}
|
||||
|
||||
void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
|
||||
{
|
||||
dndObject = image;
|
||||
cursor->setImage(getCurrentImage(), getPivotOffset());
|
||||
}
|
||||
|
||||
void CursorHandler::dragAndDropCursor (std::string path, size_t index)
|
||||
{
|
||||
CAnimation anim(path);
|
||||
anim.load(index);
|
||||
dragAndDropCursor(anim.getImage(index));
|
||||
}
|
||||
|
||||
void CursorHandler::cursorMove(const int & x, const int & y)
|
||||
{
|
||||
pos.x = x;
|
||||
pos.y = y;
|
||||
|
||||
cursor->setCursorPosition(pos);
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffsetDefault(size_t index)
|
||||
{
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffsetSpellcast()
|
||||
{
|
||||
return { 18, 28};
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffset()
|
||||
{
|
||||
if (dndObject)
|
||||
return dndObject->dimensions() / 2;
|
||||
|
||||
switch (type) {
|
||||
case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame);
|
||||
case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame);
|
||||
case Cursor::Type::DEFAULT: return getPivotOffsetDefault(frame);
|
||||
case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast();
|
||||
};
|
||||
|
||||
assert(0);
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> CursorHandler::getCurrentImage()
|
||||
{
|
||||
if (dndObject)
|
||||
return dndObject;
|
||||
|
||||
return cursors[static_cast<size_t>(type)]->getImage(frame);
|
||||
}
|
||||
|
||||
void CursorHandler::centerCursor()
|
||||
{
|
||||
Point screenSize {screen->w, screen->h};
|
||||
pos = screenSize / 2 - getPivotOffset();
|
||||
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
|
||||
CSDL_Ext::warpMouse(pos.x, pos.y);
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
|
||||
|
||||
cursor->setCursorPosition(pos);
|
||||
}
|
||||
|
||||
void CursorHandler::updateSpellcastCursor()
|
||||
{
|
||||
static const float frameDisplayDuration = 0.1f; // H3 uses 100 ms per frame
|
||||
|
||||
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
size_t newFrame = frame;
|
||||
|
||||
while (frameTime >= frameDisplayDuration)
|
||||
{
|
||||
frameTime -= frameDisplayDuration;
|
||||
newFrame++;
|
||||
}
|
||||
|
||||
auto & animation = cursors.at(static_cast<size_t>(type));
|
||||
|
||||
while (newFrame >= animation->size())
|
||||
newFrame -= animation->size();
|
||||
|
||||
changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
|
||||
}
|
||||
|
||||
void CursorHandler::render()
|
||||
{
|
||||
if(!showing)
|
||||
return;
|
||||
|
||||
if (type == Cursor::Type::SPELLBOOK)
|
||||
updateSpellcastCursor();
|
||||
|
||||
cursor->render();
|
||||
}
|
||||
|
||||
void CursorHandler::hide()
|
||||
{
|
||||
if (!showing)
|
||||
return;
|
||||
|
||||
showing = false;
|
||||
cursor->setVisible(false);
|
||||
}
|
||||
|
||||
void CursorHandler::show()
|
||||
{
|
||||
if (showing)
|
||||
return;
|
||||
|
||||
showing = true;
|
||||
cursor->setVisible(true);
|
||||
}
|
||||
|
||||
void CursorSoftware::render()
|
||||
{
|
||||
//texture must be updated in the main (renderer) thread, but changes to cursor type may come from other threads
|
||||
if (needUpdate)
|
||||
updateTexture();
|
||||
|
||||
Point renderPos = pos - pivot;
|
||||
|
||||
SDL_Rect destRect;
|
||||
destRect.x = renderPos.x;
|
||||
destRect.y = renderPos.y;
|
||||
destRect.w = 40;
|
||||
destRect.h = 40;
|
||||
|
||||
SDL_RenderCopy(mainRenderer, cursorTexture, nullptr, &destRect);
|
||||
}
|
||||
|
||||
void CursorSoftware::createTexture(const Point & dimensions)
|
||||
{
|
||||
if(cursorTexture)
|
||||
SDL_DestroyTexture(cursorTexture);
|
||||
|
||||
if (cursorSurface)
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
|
||||
cursorSurface = CSDL_Ext::newSurface(dimensions.x, dimensions.y);
|
||||
cursorTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, dimensions.x, dimensions.y);
|
||||
|
||||
SDL_SetSurfaceBlendMode(cursorSurface, SDL_BLENDMODE_NONE);
|
||||
SDL_SetTextureBlendMode(cursorTexture, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
void CursorSoftware::updateTexture()
|
||||
{
|
||||
Point dimensions(-1, -1);
|
||||
|
||||
if (!cursorSurface || Point(cursorSurface->w, cursorSurface->h) != cursorImage->dimensions())
|
||||
createTexture(cursorImage->dimensions());
|
||||
|
||||
CSDL_Ext::fillSurface(cursorSurface, Colors::TRANSPARENCY);
|
||||
|
||||
cursorImage->draw(cursorSurface);
|
||||
SDL_UpdateTexture(cursorTexture, NULL, cursorSurface->pixels, cursorSurface->pitch);
|
||||
needUpdate = false;
|
||||
}
|
||||
|
||||
void CursorSoftware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
|
||||
{
|
||||
assert(image != nullptr);
|
||||
cursorImage = image;
|
||||
pivot = pivotOffset;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
void CursorSoftware::setCursorPosition( const Point & newPos )
|
||||
{
|
||||
pos = newPos;
|
||||
}
|
||||
|
||||
void CursorSoftware::setVisible(bool on)
|
||||
{
|
||||
visible = on;
|
||||
}
|
||||
|
||||
CursorSoftware::CursorSoftware():
|
||||
cursorTexture(nullptr),
|
||||
cursorSurface(nullptr),
|
||||
needUpdate(false),
|
||||
visible(false),
|
||||
pivot(0,0)
|
||||
{
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
|
||||
CursorSoftware::~CursorSoftware()
|
||||
{
|
||||
if(cursorTexture)
|
||||
SDL_DestroyTexture(cursorTexture);
|
||||
|
||||
if (cursorSurface)
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
}
|
||||
|
||||
CursorHardware::CursorHardware():
|
||||
cursor(nullptr)
|
||||
{
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
|
||||
CursorHardware::~CursorHardware()
|
||||
{
|
||||
if(cursor)
|
||||
SDL_FreeCursor(cursor);
|
||||
}
|
||||
|
||||
void CursorHardware::setVisible(bool on)
|
||||
{
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
if (on)
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
else
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void CursorHardware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
|
||||
{
|
||||
auto cursorSurface = CSDL_Ext::newSurface(image->dimensions().x, image->dimensions().y);
|
||||
|
||||
CSDL_Ext::fillSurface(cursorSurface, Colors::TRANSPARENCY);
|
||||
|
||||
image->draw(cursorSurface);
|
||||
|
||||
auto oldCursor = cursor;
|
||||
cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y);
|
||||
|
||||
if (!cursor)
|
||||
logGlobal->error("Failed to set cursor! SDL says %s", SDL_GetError());
|
||||
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
#ifdef VCMI_APPLE
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
SDL_SetCursor(cursor);
|
||||
|
||||
if (oldCursor)
|
||||
SDL_FreeCursor(oldCursor);
|
||||
#ifdef VCMI_APPLE
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void CursorHardware::setCursorPosition( const Point & newPos )
|
||||
{
|
||||
//no-op
|
||||
}
|
||||
|
||||
void CursorHardware::render()
|
||||
{
|
||||
//no-op
|
||||
}
|
237
client/gui/CursorSoftware.h
Normal file
237
client/gui/CursorSoftware.h
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* CCursorHandler.h, 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class CAnimation;
|
||||
class IImage;
|
||||
struct SDL_Surface;
|
||||
struct SDL_Texture;
|
||||
struct SDL_Cursor;
|
||||
|
||||
#include "../../lib/Point.h"
|
||||
|
||||
namespace Cursor
|
||||
{
|
||||
enum class Type {
|
||||
ADVENTURE, // set of various cursors for adventure map
|
||||
COMBAT, // set of various cursors for combat
|
||||
DEFAULT, // default arrow and hourglass cursors
|
||||
SPELLBOOK // animated cursor for spellcasting
|
||||
};
|
||||
|
||||
enum class Default {
|
||||
POINTER = 0,
|
||||
//ARROW_COPY = 1, // probably unused
|
||||
HOURGLASS = 2,
|
||||
};
|
||||
|
||||
enum class Combat {
|
||||
INVALID = -1,
|
||||
|
||||
BLOCKED = 0,
|
||||
MOVE = 1,
|
||||
FLY = 2,
|
||||
SHOOT = 3,
|
||||
HERO = 4,
|
||||
QUERY = 5,
|
||||
POINTER = 6,
|
||||
HIT_NORTHEAST = 7,
|
||||
HIT_EAST = 8,
|
||||
HIT_SOUTHEAST = 9,
|
||||
HIT_SOUTHWEST = 10,
|
||||
HIT_WEST = 11,
|
||||
HIT_NORTHWEST = 12,
|
||||
HIT_NORTH = 13,
|
||||
HIT_SOUTH = 14,
|
||||
SHOOT_PENALTY = 15,
|
||||
SHOOT_CATAPULT = 16,
|
||||
HEAL = 17,
|
||||
SACRIFICE = 18,
|
||||
TELEPORT = 19,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
enum class Map {
|
||||
POINTER = 0,
|
||||
HOURGLASS = 1,
|
||||
HERO = 2,
|
||||
TOWN = 3,
|
||||
T1_MOVE = 4,
|
||||
T1_ATTACK = 5,
|
||||
T1_SAIL = 6,
|
||||
T1_DISEMBARK = 7,
|
||||
T1_EXCHANGE = 8,
|
||||
T1_VISIT = 9,
|
||||
T2_MOVE = 10,
|
||||
T2_ATTACK = 11,
|
||||
T2_SAIL = 12,
|
||||
T2_DISEMBARK = 13,
|
||||
T2_EXCHANGE = 14,
|
||||
T2_VISIT = 15,
|
||||
T3_MOVE = 16,
|
||||
T3_ATTACK = 17,
|
||||
T3_SAIL = 18,
|
||||
T3_DISEMBARK = 19,
|
||||
T3_EXCHANGE = 20,
|
||||
T3_VISIT = 21,
|
||||
T4_MOVE = 22,
|
||||
T4_ATTACK = 23,
|
||||
T4_SAIL = 24,
|
||||
T4_DISEMBARK = 25,
|
||||
T4_EXCHANGE = 26,
|
||||
T4_VISIT = 27,
|
||||
T1_SAIL_VISIT = 28,
|
||||
T2_SAIL_VISIT = 29,
|
||||
T3_SAIL_VISIT = 30,
|
||||
T4_SAIL_VISIT = 31,
|
||||
SCROLL_NORTH = 32,
|
||||
SCROLL_NORTHEAST = 33,
|
||||
SCROLL_EAST = 34,
|
||||
SCROLL_SOUTHEAST = 35,
|
||||
SCROLL_SOUTH = 36,
|
||||
SCROLL_SOUTHWEST = 37,
|
||||
SCROLL_WEST = 38,
|
||||
SCROLL_NORTHWEST = 39,
|
||||
//POINTER_COPY = 40, // probably unused
|
||||
TELEPORT = 41,
|
||||
SCUTTLE_BOAT = 42,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
enum class Spellcast {
|
||||
SPELL = 0,
|
||||
};
|
||||
}
|
||||
|
||||
class ICursor
|
||||
{
|
||||
public:
|
||||
virtual ~ICursor() = default;
|
||||
|
||||
virtual void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) = 0;
|
||||
virtual void setCursorPosition( const Point & newPos ) = 0;
|
||||
virtual void render() = 0;
|
||||
virtual void setVisible( bool on) = 0;
|
||||
};
|
||||
|
||||
class CursorHardware : public ICursor
|
||||
{
|
||||
std::shared_ptr<IImage> cursorImage;
|
||||
|
||||
SDL_Cursor * cursor;
|
||||
|
||||
public:
|
||||
CursorHardware();
|
||||
~CursorHardware();
|
||||
|
||||
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
|
||||
void setCursorPosition( const Point & newPos ) override;
|
||||
void render() override;
|
||||
void setVisible( bool on) override;
|
||||
};
|
||||
|
||||
class CursorSoftware : public ICursor
|
||||
{
|
||||
std::shared_ptr<IImage> cursorImage;
|
||||
|
||||
SDL_Texture * cursorTexture;
|
||||
SDL_Surface * cursorSurface;
|
||||
|
||||
Point pos;
|
||||
Point pivot;
|
||||
bool needUpdate;
|
||||
bool visible;
|
||||
|
||||
void createTexture(const Point & dimensions);
|
||||
void updateTexture();
|
||||
public:
|
||||
CursorSoftware();
|
||||
~CursorSoftware();
|
||||
|
||||
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
|
||||
void setCursorPosition( const Point & newPos ) override;
|
||||
void render() override;
|
||||
void setVisible( bool on) override;
|
||||
};
|
||||
|
||||
/// handles mouse cursor
|
||||
class CursorHandler final
|
||||
{
|
||||
std::shared_ptr<IImage> dndObject; //if set, overrides currentCursor
|
||||
|
||||
std::array<std::unique_ptr<CAnimation>, 4> cursors;
|
||||
|
||||
bool showing;
|
||||
|
||||
/// Current cursor
|
||||
Cursor::Type type;
|
||||
size_t frame;
|
||||
float frameTime;
|
||||
Point pos;
|
||||
|
||||
void changeGraphic(Cursor::Type type, size_t index);
|
||||
|
||||
Point getPivotOffsetDefault(size_t index);
|
||||
Point getPivotOffsetMap(size_t index);
|
||||
Point getPivotOffsetCombat(size_t index);
|
||||
Point getPivotOffsetSpellcast();
|
||||
Point getPivotOffset();
|
||||
|
||||
void updateSpellcastCursor();
|
||||
|
||||
std::shared_ptr<IImage> getCurrentImage();
|
||||
|
||||
std::unique_ptr<ICursor> cursor;
|
||||
|
||||
static std::unique_ptr<ICursor> createCursor();
|
||||
public:
|
||||
CursorHandler();
|
||||
~CursorHandler();
|
||||
|
||||
/// Replaces the cursor with a custom image.
|
||||
/// @param image Image to replace cursor with or nullptr to use the normal cursor.
|
||||
void dragAndDropCursor(std::shared_ptr<IImage> image);
|
||||
|
||||
void dragAndDropCursor(std::string path, size_t index);
|
||||
|
||||
/// Returns current position of the cursor
|
||||
Point position() const;
|
||||
|
||||
/// Changes cursor to specified index
|
||||
void set(Cursor::Default index);
|
||||
void set(Cursor::Map index);
|
||||
void set(Cursor::Combat index);
|
||||
void set(Cursor::Spellcast index);
|
||||
|
||||
/// Returns current index of cursor
|
||||
template<typename Index>
|
||||
Index get()
|
||||
{
|
||||
assert((std::is_same<Index, Cursor::Default>::value )|| type != Cursor::Type::DEFAULT );
|
||||
assert((std::is_same<Index, Cursor::Map>::value )|| type != Cursor::Type::ADVENTURE );
|
||||
assert((std::is_same<Index, Cursor::Combat>::value )|| type != Cursor::Type::COMBAT );
|
||||
assert((std::is_same<Index, Cursor::Spellcast>::value )|| type != Cursor::Type::SPELLBOOK );
|
||||
|
||||
return static_cast<Index>(frame);
|
||||
}
|
||||
|
||||
void render();
|
||||
|
||||
void hide();
|
||||
void show();
|
||||
|
||||
/// change cursor's positions to (x, y)
|
||||
void cursorMove(const int & x, const int & y);
|
||||
/// Move cursor to screen center
|
||||
void centerCursor();
|
||||
|
||||
};
|
237
client/gui/ICursor.h
Normal file
237
client/gui/ICursor.h
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* CCursorHandler.h, 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class CAnimation;
|
||||
class IImage;
|
||||
struct SDL_Surface;
|
||||
struct SDL_Texture;
|
||||
struct SDL_Cursor;
|
||||
|
||||
#include "../../lib/Point.h"
|
||||
|
||||
namespace Cursor
|
||||
{
|
||||
enum class Type {
|
||||
ADVENTURE, // set of various cursors for adventure map
|
||||
COMBAT, // set of various cursors for combat
|
||||
DEFAULT, // default arrow and hourglass cursors
|
||||
SPELLBOOK // animated cursor for spellcasting
|
||||
};
|
||||
|
||||
enum class Default {
|
||||
POINTER = 0,
|
||||
//ARROW_COPY = 1, // probably unused
|
||||
HOURGLASS = 2,
|
||||
};
|
||||
|
||||
enum class Combat {
|
||||
INVALID = -1,
|
||||
|
||||
BLOCKED = 0,
|
||||
MOVE = 1,
|
||||
FLY = 2,
|
||||
SHOOT = 3,
|
||||
HERO = 4,
|
||||
QUERY = 5,
|
||||
POINTER = 6,
|
||||
HIT_NORTHEAST = 7,
|
||||
HIT_EAST = 8,
|
||||
HIT_SOUTHEAST = 9,
|
||||
HIT_SOUTHWEST = 10,
|
||||
HIT_WEST = 11,
|
||||
HIT_NORTHWEST = 12,
|
||||
HIT_NORTH = 13,
|
||||
HIT_SOUTH = 14,
|
||||
SHOOT_PENALTY = 15,
|
||||
SHOOT_CATAPULT = 16,
|
||||
HEAL = 17,
|
||||
SACRIFICE = 18,
|
||||
TELEPORT = 19,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
enum class Map {
|
||||
POINTER = 0,
|
||||
HOURGLASS = 1,
|
||||
HERO = 2,
|
||||
TOWN = 3,
|
||||
T1_MOVE = 4,
|
||||
T1_ATTACK = 5,
|
||||
T1_SAIL = 6,
|
||||
T1_DISEMBARK = 7,
|
||||
T1_EXCHANGE = 8,
|
||||
T1_VISIT = 9,
|
||||
T2_MOVE = 10,
|
||||
T2_ATTACK = 11,
|
||||
T2_SAIL = 12,
|
||||
T2_DISEMBARK = 13,
|
||||
T2_EXCHANGE = 14,
|
||||
T2_VISIT = 15,
|
||||
T3_MOVE = 16,
|
||||
T3_ATTACK = 17,
|
||||
T3_SAIL = 18,
|
||||
T3_DISEMBARK = 19,
|
||||
T3_EXCHANGE = 20,
|
||||
T3_VISIT = 21,
|
||||
T4_MOVE = 22,
|
||||
T4_ATTACK = 23,
|
||||
T4_SAIL = 24,
|
||||
T4_DISEMBARK = 25,
|
||||
T4_EXCHANGE = 26,
|
||||
T4_VISIT = 27,
|
||||
T1_SAIL_VISIT = 28,
|
||||
T2_SAIL_VISIT = 29,
|
||||
T3_SAIL_VISIT = 30,
|
||||
T4_SAIL_VISIT = 31,
|
||||
SCROLL_NORTH = 32,
|
||||
SCROLL_NORTHEAST = 33,
|
||||
SCROLL_EAST = 34,
|
||||
SCROLL_SOUTHEAST = 35,
|
||||
SCROLL_SOUTH = 36,
|
||||
SCROLL_SOUTHWEST = 37,
|
||||
SCROLL_WEST = 38,
|
||||
SCROLL_NORTHWEST = 39,
|
||||
//POINTER_COPY = 40, // probably unused
|
||||
TELEPORT = 41,
|
||||
SCUTTLE_BOAT = 42,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
enum class Spellcast {
|
||||
SPELL = 0,
|
||||
};
|
||||
}
|
||||
|
||||
class ICursor
|
||||
{
|
||||
public:
|
||||
virtual ~ICursor() = default;
|
||||
|
||||
virtual void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) = 0;
|
||||
virtual void setCursorPosition( const Point & newPos ) = 0;
|
||||
virtual void render() = 0;
|
||||
virtual void setVisible( bool on) = 0;
|
||||
};
|
||||
|
||||
class CursorHardware : public ICursor
|
||||
{
|
||||
std::shared_ptr<IImage> cursorImage;
|
||||
|
||||
SDL_Cursor * cursor;
|
||||
|
||||
public:
|
||||
CursorHardware();
|
||||
~CursorHardware();
|
||||
|
||||
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
|
||||
void setCursorPosition( const Point & newPos ) override;
|
||||
void render() override;
|
||||
void setVisible( bool on) override;
|
||||
};
|
||||
|
||||
class CursorSoftware : public ICursor
|
||||
{
|
||||
std::shared_ptr<IImage> cursorImage;
|
||||
|
||||
SDL_Texture * cursorTexture;
|
||||
SDL_Surface * cursorSurface;
|
||||
|
||||
Point pos;
|
||||
Point pivot;
|
||||
bool needUpdate;
|
||||
bool visible;
|
||||
|
||||
void createTexture(const Point & dimensions);
|
||||
void updateTexture();
|
||||
public:
|
||||
CursorSoftware();
|
||||
~CursorSoftware();
|
||||
|
||||
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
|
||||
void setCursorPosition( const Point & newPos ) override;
|
||||
void render() override;
|
||||
void setVisible( bool on) override;
|
||||
};
|
||||
|
||||
/// handles mouse cursor
|
||||
class CursorHandler final
|
||||
{
|
||||
std::shared_ptr<IImage> dndObject; //if set, overrides currentCursor
|
||||
|
||||
std::array<std::unique_ptr<CAnimation>, 4> cursors;
|
||||
|
||||
bool showing;
|
||||
|
||||
/// Current cursor
|
||||
Cursor::Type type;
|
||||
size_t frame;
|
||||
float frameTime;
|
||||
Point pos;
|
||||
|
||||
void changeGraphic(Cursor::Type type, size_t index);
|
||||
|
||||
Point getPivotOffsetDefault(size_t index);
|
||||
Point getPivotOffsetMap(size_t index);
|
||||
Point getPivotOffsetCombat(size_t index);
|
||||
Point getPivotOffsetSpellcast();
|
||||
Point getPivotOffset();
|
||||
|
||||
void updateSpellcastCursor();
|
||||
|
||||
std::shared_ptr<IImage> getCurrentImage();
|
||||
|
||||
std::unique_ptr<ICursor> cursor;
|
||||
|
||||
static std::unique_ptr<ICursor> createCursor();
|
||||
public:
|
||||
CursorHandler();
|
||||
~CursorHandler();
|
||||
|
||||
/// Replaces the cursor with a custom image.
|
||||
/// @param image Image to replace cursor with or nullptr to use the normal cursor.
|
||||
void dragAndDropCursor(std::shared_ptr<IImage> image);
|
||||
|
||||
void dragAndDropCursor(std::string path, size_t index);
|
||||
|
||||
/// Returns current position of the cursor
|
||||
Point position() const;
|
||||
|
||||
/// Changes cursor to specified index
|
||||
void set(Cursor::Default index);
|
||||
void set(Cursor::Map index);
|
||||
void set(Cursor::Combat index);
|
||||
void set(Cursor::Spellcast index);
|
||||
|
||||
/// Returns current index of cursor
|
||||
template<typename Index>
|
||||
Index get()
|
||||
{
|
||||
assert((std::is_same<Index, Cursor::Default>::value )|| type != Cursor::Type::DEFAULT );
|
||||
assert((std::is_same<Index, Cursor::Map>::value )|| type != Cursor::Type::ADVENTURE );
|
||||
assert((std::is_same<Index, Cursor::Combat>::value )|| type != Cursor::Type::COMBAT );
|
||||
assert((std::is_same<Index, Cursor::Spellcast>::value )|| type != Cursor::Type::SPELLBOOK );
|
||||
|
||||
return static_cast<Index>(frame);
|
||||
}
|
||||
|
||||
void render();
|
||||
|
||||
void hide();
|
||||
void show();
|
||||
|
||||
/// change cursor's positions to (x, y)
|
||||
void cursorMove(const int & x, const int & y);
|
||||
/// Move cursor to screen center
|
||||
void centerCursor();
|
||||
|
||||
};
|
Loading…
Reference in New Issue
Block a user