Skip to content

Commit

Permalink
Extract objcurs widths into a file
Browse files Browse the repository at this point in the history
Read from the widths file when using the original CEL.
When using CLX, use width in CLX.
  • Loading branch information
glebm committed Feb 17, 2024
1 parent 55a5337 commit df51e78
Show file tree
Hide file tree
Showing 13 changed files with 373 additions and 106 deletions.
6 changes: 6 additions & 0 deletions CMake/Assets.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ set(devilutionx_assets
ui_art/mainmenuw.clx
ui_art/supportw.clx)

if(NOT UNPACKED_MPQS)
list(APPEND devilutionx_assets
data/inv/objcurs-widths.txt
data/inv/objcurs2-widths.txt)
endif()

if(NOT USE_SDL1 AND NOT VITA)
list(APPEND devilutionx_assets
ui_art/button.png
Expand Down
149 changes: 57 additions & 92 deletions Source/cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

#include <cmath>
#include <cstdint>
#include <limits>
#include <string_view>
#include <vector>

#include <fmt/format.h>

Expand All @@ -17,7 +20,6 @@
#include "engine.h"
#include "engine/backbuffer_state.hpp"
#include "engine/demomode.h"
#include "engine/load_cel.hpp"
#include "engine/point.hpp"
#include "engine/points_in_rectangle_range.hpp"
#include "engine/render/clx_render.hpp"
Expand All @@ -37,84 +39,20 @@
#include "utils/surface_to_clx.hpp"
#include "utils/utf8.hpp"

#ifdef UNPACKED_MPQS
#include "engine/load_clx.hpp"
#else
#include "engine/load_cel.hpp"
#include "engine/load_file.hpp"
#include "utils/parse_int.hpp"
#endif

namespace devilution {
namespace {
/** Cursor images CEL */
OptionalOwnedClxSpriteList pCursCels;
OptionalOwnedClxSpriteList pCursCels2;

/** Maps from objcurs.cel frame number to frame width. */
const uint16_t InvItemWidth1[] = {
// clang-format off
// Cursors
33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 23,
// Items
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
};
const uint16_t InvItemWidth2[] = {
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
2 * 28, 2 * 28, 1 * 28, 1 * 28, 1 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28
// clang-format on
};
constexpr uint16_t InvItems1Size = sizeof(InvItemWidth1) / sizeof(InvItemWidth1[0]);
constexpr uint16_t InvItems2Size = sizeof(InvItemWidth2) / sizeof(InvItemWidth2[0]);

/** Maps from objcurs.cel frame number to frame height. */
const uint16_t InvItemHeight1[InvItems1Size] = {
// clang-format off
// Cursors
29, 32, 32, 32, 32, 32, 32, 32, 32, 32, 35,
// Items
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28, 2 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
};
const uint16_t InvItemHeight2[InvItems2Size] = {
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28, 1 * 28,
2 * 28, 2 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28, 3 * 28,
3 * 28
// clang-format on
};

OptionalOwnedClxSpriteList *HalfSizeItemSprites;
OptionalOwnedClxSpriteList *HalfSizeItemSpritesRed;

Expand Down Expand Up @@ -430,6 +368,28 @@ bool TrySelectPixelBased(Point tile)
return false;
}

std::vector<uint16_t> ReadWidths(const char *path)
{
size_t len;
const std::unique_ptr<char[]> data = LoadFileInMem<char>(path, &len);
std::string_view str { data.get(), len };
std::vector<uint16_t> result;
while (!str.empty()) {
const char *end;
const ParseIntResult<uint16_t> parseResult = ParseInt<uint16_t>(str, std::numeric_limits<uint16_t>::min(),
std::numeric_limits<uint16_t>::max(), &end);
if (!parseResult.has_value()) {
app_fatal(StrCat("Failed to parse ", path, ": [", str, "]"));
}
result.push_back(parseResult.value());
str.remove_prefix(end - str.data());
while (!str.empty() && (str[0] == '\r' || str[0] == '\n')) {
str.remove_prefix(1);
}
}
return result;
}

} // namespace

/** Current highlighted monster */
Expand All @@ -455,9 +415,17 @@ int pcurs;
void InitCursor()
{
assert(!pCursCels);
pCursCels = LoadCel("data\\inv\\objcurs", InvItemWidth1);
if (gbIsHellfire)
pCursCels2 = LoadCel("data\\inv\\objcurs2", InvItemWidth2);
#ifdef UNPACKED_MPQS
pCursCels = LoadClxListOrSheet("data\\inv\\objcurs.clx");
if (gbIsHellfire) {
pCursCels2 = LoadClxListOrSheet("data\\inv\\objcurs2.clx");
}
#else
pCursCels = LoadCel("data\\inv\\objcurs", ReadWidths("data\\inv\\objcurs-widths.txt").data());
if (gbIsHellfire) {
pCursCels2 = LoadCel("data\\inv\\objcurs2", ReadWidths("data\\inv\\objcurs2-widths.txt").data());
}
#endif
ClearCursor();
}

Expand All @@ -470,22 +438,20 @@ void FreeCursor()

ClxSprite GetInvItemSprite(int cursId)
{
if (cursId <= InvItems1Size)
assert(cursId > 0);
const size_t numSprites = pCursCels->numSprites();
if (static_cast<size_t>(cursId) <= numSprites) {
return (*pCursCels)[cursId - 1];
return (*pCursCels2)[cursId - InvItems1Size - 1];
}

size_t GetNumInvItems()
{
return InvItems1Size + InvItems2Size;
}
assert(gbIsHellfire);
assert(cursId - numSprites <= pCursCels2->numSprites());
return (*pCursCels2)[cursId - numSprites - 1];
}

Size GetInvItemSize(int cursId)
{
const int i = cursId - 1;
if (i >= InvItems1Size)
return { InvItemWidth2[i - InvItems1Size], InvItemHeight2[i - InvItems1Size] };
return { InvItemWidth1[i], InvItemHeight1[i] };
const ClxSprite sprite = GetInvItemSprite(cursId);
return { sprite.width(), sprite.height() };
}

ClxSprite GetHalfSizeItemSprite(int cursId)
Expand All @@ -502,9 +468,8 @@ void CreateHalfSizeItemSprites()
{
if (HalfSizeItemSprites != nullptr)
return;
const int numInvItems = gbIsHellfire
? InvItems1Size + InvItems2Size - (static_cast<size_t>(CURSOR_FIRSTITEM) - 1)
: InvItems1Size + (static_cast<size_t>(CURSOR_FIRSTITEM) - 1);
const int numInvItems = pCursCels->numSprites() - (static_cast<size_t>(CURSOR_FIRSTITEM) - 1)
+ (gbIsHellfire ? pCursCels2->numSprites() : 0);
HalfSizeItemSprites = new OptionalOwnedClxSpriteList[numInvItems];
HalfSizeItemSpritesRed = new OptionalOwnedClxSpriteList[numInvItems];
const uint8_t *redTrn = GetInfravisionTRN();
Expand Down Expand Up @@ -538,11 +503,11 @@ void CreateHalfSizeItemSprites()
};

size_t outputIndex = 0;
for (size_t i = static_cast<int>(CURSOR_FIRSTITEM) - 1; i < InvItems1Size; ++i, ++outputIndex) {
for (size_t i = static_cast<int>(CURSOR_FIRSTITEM) - 1, n = pCursCels->numSprites(); i < n; ++i, ++outputIndex) {
createHalfSize((*pCursCels)[i], outputIndex);
}
if (gbIsHellfire) {
for (size_t i = 0; i < InvItems2Size; ++i, ++outputIndex) {
for (size_t i = 0, n = pCursCels2->numSprites(); i < n; ++i, ++outputIndex) {
createHalfSize((*pCursCels2)[i], outputIndex);
}
}
Expand Down
5 changes: 5 additions & 0 deletions Source/engine/clx_sprite.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,11 @@ class OwnedClxSpriteList {
return ClxSpriteList { *this }[spriteIndex];
}

[[nodiscard]] uint32_t numSprites() const
{
return ClxSpriteList { *this }.numSprites();
}

private:
// For OptionalOwnedClxSpriteList.
OwnedClxSpriteList() = default;
Expand Down
3 changes: 1 addition & 2 deletions Source/inv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2154,8 +2154,7 @@ int CalculateGold(Player &player)

Size GetInventorySize(const Item &item)
{
int itemSizeIndex = item._iCurs + CURSOR_FIRSTITEM;
auto size = GetInvItemSize(itemSizeIndex);
auto size = GetInvItemSize(item._iCurs);

return { size.width / InventorySlotSizeInPixels.width, size.height / InventorySlotSizeInPixels.height };
}
Expand Down
1 change: 0 additions & 1 deletion Source/itemdat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ tl::expected<item_cursor_graphic, std::string> ParseItemCursorGraphic(std::strin
if (value == "SHORT_WAR_BOW") return ICURS_SHORT_WAR_BOW;
if (value == "COMPOSITE_STAFF") return ICURS_COMPOSITE_STAFF;
if (value == "SHORT_BATTLE_BOW") return ICURS_SHORT_BATTLE_BOW;
if (value == "GOLD") return ICURS_GOLD;
if (value == "AURIC_AMULET") return ICURS_AURIC_AMULET;
if (value == "RUNE_BOMB") return ICURS_RUNE_BOMB;
if (value == "THEODORE") return ICURS_THEODORE;
Expand Down
3 changes: 2 additions & 1 deletion Source/itemdat.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ enum _item_indexes : int16_t { // TODO defines all indexes in AllItemsList
IDI_BOOK3,
IDI_BOOK4,
IDI_BARBARIAN = 139,
IDI_SHORT_BATTLE_BOW = 148,
IDI_RUNEOFSTONE = 165,
IDI_SORCERER_DIABLO,
IDI_ARENAPOT,
Expand Down Expand Up @@ -217,7 +218,7 @@ enum item_cursor_graphic : uint8_t {
ICURS_SHORT_WAR_BOW = 165,
ICURS_COMPOSITE_STAFF = 166,
ICURS_SHORT_BATTLE_BOW = 167,
ICURS_GOLD = 168,
// Hellfire items:
ICURS_AURIC_AMULET = 180,
ICURS_RUNE_BOMB = 187,
ICURS_THEODORE = 188,
Expand Down
Loading

0 comments on commit df51e78

Please sign in to comment.