| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 | /* * 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>#endifstd::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}
 |