mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-04-16 01:55:21 +03:00
feat(cursor): add cursor module that stores and updates various cursor images
This commit is contained in:
parent
7a2376741d
commit
29bc21a242
17
src/cursor/CMakeLists.txt
Normal file
17
src/cursor/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
file(GLOB PRIVATE_SOURCES "*.cpp")
|
||||||
|
|
||||||
|
add_library(cursor STATIC
|
||||||
|
${PRIVATE_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(cursor
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_SOURCE_DIR}/src
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(cursor
|
||||||
|
PRIVATE
|
||||||
|
storm
|
||||||
|
gx
|
||||||
|
ui
|
||||||
|
)
|
218
src/cursor/Cursor.cpp
Normal file
218
src/cursor/Cursor.cpp
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
#include "cursor/Cursor.hpp"
|
||||||
|
#include "ui/FrameScript.hpp"
|
||||||
|
#include "gx/Texture.hpp"
|
||||||
|
#include "gx/Device.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <storm/String.hpp>
|
||||||
|
|
||||||
|
static CURSORMODE s_cursorMode;
|
||||||
|
static uint32_t s_cursorResetMode;
|
||||||
|
static CURSORITEMTYPE s_cursorItemType;
|
||||||
|
static uint32_t s_cursorItemMips;
|
||||||
|
static uint32_t s_cursorItemMipsHeight;
|
||||||
|
static uint32_t s_cursorItemMipsWidth;
|
||||||
|
static int8_t s_cursorFile;
|
||||||
|
|
||||||
|
static uint32_t s_cursorItem[CURSOR_IMAGE_SIZE];
|
||||||
|
|
||||||
|
static uint32_t s_cursorImages[NUM_CURSOR_MODES][CURSOR_IMAGE_SIZE];
|
||||||
|
|
||||||
|
static const char* s_cursorNames[NUM_CURSOR_MODES] = {
|
||||||
|
nullptr, /* NO_CURSOR */
|
||||||
|
"Point", /* POINT_CURSOR */
|
||||||
|
"Cast", /* CAST_CURSOR */
|
||||||
|
"Buy", /* BUY_CURSOR */
|
||||||
|
"Attack", /* ATTACK_CURSOR */
|
||||||
|
"Interact", /* INTERACT_CURSOR */
|
||||||
|
"Speak", /* SPEAK_CURSOR */
|
||||||
|
"Inspect", /* INSPECT_CURSOR */
|
||||||
|
"Pickup", /* PICKUP_CURSOR */
|
||||||
|
"Taxi", /* TAXI_CURSOR */
|
||||||
|
"Trainer", /* TRAINER_CURSOR */
|
||||||
|
"Mine", /* MINE_CURSOR */
|
||||||
|
"Skin", /* SKIN_CURSOR */
|
||||||
|
"GatherHerbs", /* GATHER_CURSOR */
|
||||||
|
"PickLock", /* LOCK_CURSOR */
|
||||||
|
"Mail", /* MAIL_CURSOR */
|
||||||
|
"LootAll", /* LOOT_ALL_CURSOR */
|
||||||
|
"Repair", /* REPAIR_CURSOR */
|
||||||
|
"RepairNPC", /* REPAIRNPC_CURSOR */
|
||||||
|
"Item", /* ITEM_CURSOR */
|
||||||
|
"SkinHorde", /* SKIN_HORDE_CURSOR */
|
||||||
|
"SkinAlliance", /* SKIN_ALLIANCE_CURSOR */
|
||||||
|
"Innkeeper", /* INNKEEPER_CURSOR */
|
||||||
|
"Quest", /* QUEST_CURSOR */
|
||||||
|
"QuestRepeatable", /* QUEST_REPEATABLE_CURSOR */
|
||||||
|
"QuestTurnIn", /* QUEST_TURNIN_CURSOR */
|
||||||
|
"vehichleCursor", /* VEHICLE_CURSOR */
|
||||||
|
"UnablePoint", /* POINT_ERROR_CURSOR */
|
||||||
|
"UnableCast", /* CAST_ERROR_CURSOR */
|
||||||
|
"UnableBuy", /* BUY_ERROR_CURSOR */
|
||||||
|
"UnableAttack", /* ATTACK_ERROR_CURSOR */
|
||||||
|
"UnableInteract", /* INTERACT_ERROR_CURSOR */
|
||||||
|
"UnableSpeak", /* SPEAK_ERROR_CURSOR */
|
||||||
|
"UnableInspect", /* INSPECT_ERROR_CURSOR */
|
||||||
|
"UnablePickup", /* PICKUP_ERROR_CURSOR */
|
||||||
|
"UnableTaxi", /* TAXI_ERROR_CURSOR */
|
||||||
|
"UnableTrainer", /* TRAINER_ERROR_CURSOR */
|
||||||
|
"UnableMine", /* MINE_ERROR_CURSOR */
|
||||||
|
"UnableSkin", /* SKIN_ERROR_CURSOR */
|
||||||
|
"UnableGatherHerbs", /* GATHER_ERROR_CURSOR */
|
||||||
|
"UnablePickLock", /* LOCK_ERROR_CURSOR */
|
||||||
|
"UnableMail", /* MAIL_ERROR_CURSOR */
|
||||||
|
"UnableLootAll", /* LOOT_ALL_ERROR_CURSOR */
|
||||||
|
"UnableRepair", /* REPAIR_ERROR_CURSOR */
|
||||||
|
"UnableRepairNPC", /* REPAIRNPC_ERROR_CURSOR */
|
||||||
|
"UnableItem", /* ITEM_ERROR_CURSOR */
|
||||||
|
"UnableSkinHorde", /* SKIN_HORDE_ERROR_CURSOR */
|
||||||
|
"UnableSkinAlliance", /* SKIN_ALLIANCE_ERROR_CURSOR */
|
||||||
|
"UnableInnkeeper", /* INNKEEPER_ERROR_CURSOR */
|
||||||
|
"UnableQuest", /* QUEST_ERROR_CURSOR */
|
||||||
|
"UnableQuestRepeatable", /* QUEST_REPEATABLE_ERROR_CURSOR */
|
||||||
|
"UnableQuestTurnIn", /* QUEST_TURNIN_ERROR_CURSOR */
|
||||||
|
"UnablevehichleCursor" /* VEHICLE_ERROR_CURSOR */
|
||||||
|
};
|
||||||
|
|
||||||
|
int32_t CopyCursorImage(uint32_t* image, MipBits* mipImages, uint32_t width, uint32_t height) {
|
||||||
|
if (mipImages) {
|
||||||
|
if (width == 32 && height == 32) {
|
||||||
|
memcpy(image, mipImages->mip[0], CURSOR_IMAGE_BYTES);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width == 64 && height == 64) {
|
||||||
|
// Downsample /2
|
||||||
|
|
||||||
|
auto end = image + 1024;
|
||||||
|
|
||||||
|
auto topRow = mipImages->mip[0];
|
||||||
|
|
||||||
|
auto dest = image;
|
||||||
|
|
||||||
|
while (dest < end) {
|
||||||
|
auto bottomRow = topRow + 64;
|
||||||
|
|
||||||
|
auto topPixels = topRow;
|
||||||
|
auto bottomPixels = bottomRow;
|
||||||
|
|
||||||
|
// Merge 4 pixels into one
|
||||||
|
while (bottomPixels < bottomRow) {
|
||||||
|
*dest++ = ((topPixels[0].b + topPixels[1].b + bottomPixels[0].b + bottomPixels[1].b) / 4) |
|
||||||
|
(((topPixels[0].g + topPixels[1].g + bottomPixels[0].g + bottomPixels[1].g) / 4) << 8) |
|
||||||
|
(((topPixels[0].r + topPixels[1].r + bottomPixels[0].r + bottomPixels[1].r) / 4) << 16) |
|
||||||
|
(((topPixels[0].a + topPixels[1].a + bottomPixels[0].a + bottomPixels[1].a) / 4) << 24);
|
||||||
|
|
||||||
|
topPixels += 2;
|
||||||
|
bottomPixels += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance two rows
|
||||||
|
topRow += 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// failure
|
||||||
|
|
||||||
|
memset(image, 0, CURSOR_IMAGE_BYTES);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateCursor() {
|
||||||
|
auto cursor = g_theGxDevicePtr->CursorLock();
|
||||||
|
|
||||||
|
if (s_cursorItemType == CURSOR_EMPTY) {
|
||||||
|
// update the cursor to the current mode's image
|
||||||
|
memcpy(cursor, s_cursorImages[s_cursorMode], CURSOR_IMAGE_BYTES);
|
||||||
|
} else {
|
||||||
|
if (s_cursorItemMips == 0) {
|
||||||
|
// merge the icon with the cursor bitmap
|
||||||
|
// so the pointer in the top left remains
|
||||||
|
int32_t i = 0;
|
||||||
|
int32_t j = CURSOR_IMAGE_SIZE/4;
|
||||||
|
|
||||||
|
while (j) {
|
||||||
|
auto pixel1 = s_cursorImages[CURSOR_ITEM][i];
|
||||||
|
if (!(pixel1 & 0xFF000000)) {
|
||||||
|
pixel1 = s_cursorItem[i];
|
||||||
|
}
|
||||||
|
cursor[i] = pixel1;
|
||||||
|
|
||||||
|
auto pixel2 = s_cursorImages[CURSOR_ITEM][i + 1];
|
||||||
|
if (!(pixel2 & 0xFF000000)) {
|
||||||
|
pixel2 = s_cursorItem[i + 1];
|
||||||
|
}
|
||||||
|
cursor[i + 1] = pixel2;
|
||||||
|
|
||||||
|
auto pixel3 = s_cursorImages[CURSOR_ITEM][i + 2];
|
||||||
|
if (!(pixel3 & 0xFF000000)) {
|
||||||
|
pixel3 = s_cursorItem[i + 2];
|
||||||
|
}
|
||||||
|
cursor[i + 2] = pixel3;
|
||||||
|
|
||||||
|
auto pixel4 = s_cursorImages[CURSOR_ITEM][i + 3];
|
||||||
|
if (!(pixel4 & 0xFF000000)) {
|
||||||
|
pixel4 = s_cursorItem[i + 3];
|
||||||
|
}
|
||||||
|
cursor[i + 3] = pixel4;
|
||||||
|
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(cursor, s_cursorImages[CURSOR_ITEM], CURSOR_IMAGE_BYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_theGxDevicePtr->CursorUnlock(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CursorInitialize() {
|
||||||
|
s_cursorMode = POINT_CURSOR;
|
||||||
|
s_cursorResetMode = 1;
|
||||||
|
s_cursorItemType = CURSOR_EMPTY;
|
||||||
|
s_cursorItemMips = 0;
|
||||||
|
s_cursorItemMipsHeight = 0;
|
||||||
|
s_cursorItemMipsWidth = 0;
|
||||||
|
|
||||||
|
char cursorFilename[STORM_MAX_PATH];
|
||||||
|
|
||||||
|
for (uint32_t mode = 0; mode < NUM_CURSOR_MODES; mode++) {
|
||||||
|
auto cursorName = s_cursorNames[mode];
|
||||||
|
auto cursorImage = s_cursorImages[mode];
|
||||||
|
|
||||||
|
if (cursorName) {
|
||||||
|
SStrPrintf(cursorFilename, STORM_MAX_PATH, "Interface\\Cursor\\%s.blp", cursorName);
|
||||||
|
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
PIXEL_FORMAT dataFormat = PIXEL_ARGB8888;
|
||||||
|
|
||||||
|
auto mipImages = TextureLoadImage(cursorFilename, &width, &height, &dataFormat, nullptr, nullptr, nullptr, 0);
|
||||||
|
|
||||||
|
if (mipImages) {
|
||||||
|
CopyCursorImage(cursorImage, mipImages, width, height);
|
||||||
|
TextureFreeMippedImg(mipImages, dataFormat, width, height);
|
||||||
|
} else {
|
||||||
|
memset(cursorImage, 0, CURSOR_IMAGE_BYTES);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memset(cursorImage, 0, CURSOR_IMAGE_BYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_cursorFile = 0;
|
||||||
|
UpdateCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CursorSetMode(CURSORMODE mode) {
|
||||||
|
if (mode != NUM_CURSOR_MODES && s_cursorMode != mode) {
|
||||||
|
s_cursorMode = mode;
|
||||||
|
UpdateCursor();
|
||||||
|
FrameScript_SignalEvent(275, nullptr);
|
||||||
|
}
|
||||||
|
}
|
13
src/cursor/Cursor.hpp
Normal file
13
src/cursor/Cursor.hpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef CURSOR_CURSOR_HPP
|
||||||
|
#define CURSOR_CURSOR_HPP
|
||||||
|
|
||||||
|
#define CURSOR_IMAGE_SIZE (32 * 32)
|
||||||
|
#define CURSOR_IMAGE_BYTES (CURSOR_IMAGE_SIZE * 4)
|
||||||
|
|
||||||
|
#include "cursor/Types.hpp"
|
||||||
|
|
||||||
|
void CursorInitialize();
|
||||||
|
|
||||||
|
void CursorSetMode(CURSORMODE mode);
|
||||||
|
|
||||||
|
#endif
|
69
src/cursor/Types.hpp
Normal file
69
src/cursor/Types.hpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#ifndef CURSOR_TYPES_HPP
|
||||||
|
#define CURSOR_TYPES_HPP
|
||||||
|
|
||||||
|
enum CURSORITEMTYPE {
|
||||||
|
CURSOR_EMPTY = 0,
|
||||||
|
CURSOR_ITEM = 1,
|
||||||
|
CURSOR_MONEY = 2,
|
||||||
|
CURSOR_SPELL = 3,
|
||||||
|
NUM_CURSOR_ITEM_TYPES = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CURSORMODE {
|
||||||
|
NO_CURSOR = 0,
|
||||||
|
POINT_CURSOR = 1,
|
||||||
|
CAST_CURSOR = 2,
|
||||||
|
BUY_CURSOR = 3,
|
||||||
|
ATTACK_CURSOR = 4,
|
||||||
|
INTERACT_CURSOR = 5,
|
||||||
|
SPEAK_CURSOR = 6,
|
||||||
|
INSPECT_CURSOR = 7,
|
||||||
|
PICKUP_CURSOR = 8,
|
||||||
|
TAXI_CURSOR = 9,
|
||||||
|
TRAINER_CURSOR = 10,
|
||||||
|
MINE_CURSOR = 11,
|
||||||
|
SKIN_CURSOR = 12,
|
||||||
|
GATHER_CURSOR = 13,
|
||||||
|
LOCK_CURSOR = 14,
|
||||||
|
MAIL_CURSOR = 15,
|
||||||
|
LOOT_ALL_CURSOR = 16,
|
||||||
|
REPAIR_CURSOR = 17,
|
||||||
|
REPAIRNPC_CURSOR = 18,
|
||||||
|
ITEM_CURSOR = 19,
|
||||||
|
SKIN_HORDE_CURSOR = 20,
|
||||||
|
SKIN_ALLIANCE_CURSOR = 21,
|
||||||
|
INNKEEPER_CURSOR = 22,
|
||||||
|
QUEST_CURSOR = 23,
|
||||||
|
QUEST_REPEATABLE_CURSOR = 24,
|
||||||
|
QUEST_TURNIN_CURSOR = 25,
|
||||||
|
VEHICLE_CURSOR = 26,
|
||||||
|
POINT_ERROR_CURSOR = 27,
|
||||||
|
CAST_ERROR_CURSOR = 28,
|
||||||
|
BUY_ERROR_CURSOR = 29,
|
||||||
|
ATTACK_ERROR_CURSOR = 30,
|
||||||
|
INTERACT_ERROR_CURSOR = 31,
|
||||||
|
SPEAK_ERROR_CURSOR = 32,
|
||||||
|
INSPECT_ERROR_CURSOR = 33,
|
||||||
|
PICKUP_ERROR_CURSOR = 34,
|
||||||
|
TAXI_ERROR_CURSOR = 35,
|
||||||
|
TRAINER_ERROR_CURSOR = 36,
|
||||||
|
MINE_ERROR_CURSOR = 37,
|
||||||
|
SKIN_ERROR_CURSOR = 38,
|
||||||
|
GATHER_ERROR_CURSOR = 39,
|
||||||
|
LOCK_ERROR_CURSOR = 40,
|
||||||
|
MAIL_ERROR_CURSOR = 41,
|
||||||
|
LOOT_ALL_ERROR_CURSOR = 42,
|
||||||
|
REPAIR_ERROR_CURSOR = 43,
|
||||||
|
REPAIRNPC_ERROR_CURSOR = 44,
|
||||||
|
ITEM_ERROR_CURSOR = 45,
|
||||||
|
SKIN_HORDE_ERROR_CURSOR = 46,
|
||||||
|
SKIN_ALLIANCE_ERROR_CURSOR = 47,
|
||||||
|
INNKEEPER_ERROR_CURSOR = 48,
|
||||||
|
QUEST_ERROR_CURSOR = 49,
|
||||||
|
QUEST_REPEATABLE_ERROR_CURSOR = 50,
|
||||||
|
QUEST_TURNIN_ERROR_CURSOR = 51,
|
||||||
|
VEHICLE_ERROR_CURSOR = 52,
|
||||||
|
NUM_CURSOR_MODES = 53
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user