#include "cursor/Cursor.hpp" #include "ui/FrameScript.hpp" #include "gx/Texture.hpp" #include "gx/Device.hpp" #include #include #include 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); } } void CursorResetCursor() { // TODO }