Add FixedPoint::divideWithoutWidening for cases where the caller can
already show the wide intermediate isn't needed, and use it in
AnimationInfo::setNewAnimation to avoid a 64-bit division that the
original int32_t arithmetic never required.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Replaces ParseFixed6/parseFixed6/readFixed6/readDecimal's raw-int
plumbing with the new FixedPoint type, then propagates strongly-typed
fixed-point fields through everywhere that consumed them: player
_pHitPoints/_pMana (and their base/max variants), ClassAttributes'
percentage/multiplier fields, Item::_iPLHP/_iPLMana, and
Monster::hitPoints/maxHitPoints. Eliminates manual <<6/>>6 bit-shifting
at nearly every call site in favor of fromInt/fromRaw/whole() and the
new fixed-point arithmetic operators, while preserving raw() only at
genuine serialization/network wire-format boundaries.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Introduces a strongly-typed wrapper for the 10.6 fixed-point values used
throughout the engine (e.g. hit points, mana), with Fixed10_6 (int16_t
storage) and Fixed26_6 (int32_t storage) aliases.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
In CLX, only transparent runs can continue on the next line.
`Cl2ToClx` joined the opaque pixels that end a line with the opaque
pixels that begin the next line into a single run, and the renderer
then drew the second part past the end of the first line instead of
at the beginning of the next one.
CL2 itself has no such runs. Among the player sprites of DIABDAT.MPQ,
the converter produced them in 991 of 78,016 frames (2,236 pixels),
mostly in spell casting animations that reach both edges of the frame.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
While a mouse button or controller action is held, the last action is
re-sent every game tick. This affects walking, melee attacks on targets
out of reach and operating objects the player isn't next to. If no path
to the target exists, each repeat runs two or three worst-case path
searches for the same result, which noticeably reduces FPS.
Add WalkabilityGeneration, bumped whenever tile solidity, objects or
the tiles occupied by monsters and players change. RepeatPlayerAction
remembers the last request that needed walking and skips an identical
one while the start tile, destination, generation and player life
states are unchanged. A request that finds a path moves the player, so
this only skips requests that failed or are still pending.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Added option to show summoned monster (Golem) party portrait
Fixed crash when loading .trn files for monsters with missing animations
(Golem is missing standing animation)
When calculating width of mana bars, if another player has a maximum amount of mana of zero (like a Barbarian) would cause a division by zero, crashing the rendering client. Fixed by not rendering the blue portion of the mana bar if the max mana is zero.
Not applying players palette transform, stored in a .trn file. Fixed by applying the trn to the stored portrait sprite, if available.
Created several values in the player classes sprites.tsv to store the offsets of the sprites rendered in the portrait. The new values are:
- **partyOffsetTownX** : X Offset for the portrait when the player is in town
- **partyOffsetTownY** : Y Offset for the portrait when the player is in town
- **partyOffsetDungeonX** : X Offset for the portrait when the player is in dungeon
- **partyOffsetDungeonY** : Y Offset for the portrait when the player is in dungeon
- **partyOffsetDeadX** : X Offset for the portrait when the player is dead
- **partyOffsetDeadY** : Y Offset for the portrait when the player is dead
This new values are documented in the assets/txtdata/classes/README.md file, with also the rest of the possible values for the sprites.tsv files
ExtractFoliageTransparentSquare incremented `src` in the condition that
tests the current pixel, so when a transparent pixel ended a solid run,
`src` was already past it and the run was copied from one pixel too far
right. Foliage on floor tiles was drawn shifted one pixel left, ending in
a black (index 0) pixel. Runs that end at the end of the row were right.
Advance `src` with `x` instead, so it points at the pixel being tested
and both copies of a run use `src - solidRun`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015zyYqNJFojmYr8vULsJ9nh
CheckArchivesUpToDate() ran at the end of DiabloInit(), but
LuaInitialize() loads lua\inspect.lua (optional = false) much earlier
in DiabloMain(). With an out-of-date devilutionx.mpq, the game aborted
with a confusing "Asset not found: lua\inspect.lua" error instead of
the clear "update devilutionx.mpq" message.
Move the check to right after ApplicationInit() in DiabloMain(): the
window already exists at that point (so the styled error dialog
renders as before), and the check now happens before any asset is
loaded from the core archives.
Verified on Windows x64 with the published v5 devilutionx.mpq and the
assets directory removed from the build folder: the game now shows
"Failed to load UI resources. Make sure devilutionx.mpq is in the game
folder and that it is up to date." instead of the Lua error.
SDL3 reverts to SDL1 behavior and no longer auto-resolves the.app bundle's Contents/Resources/ directory, but the #elif only checked USE_SDL1.
This left USE_SDL3 falling through to the generic branch and looking for a nonexistent assets/ subdirectory, causing every asset load to fail.
Since all of the object files we're building go into the binary that
uses `-pthread` (due to SDL, where available), all of them should be
compiled with `-pthread` to ensure a consistent threading model.
This also allows all object files to use the same PCH on clang,
where threading model is part of the PCH data.
Like Amiga and 3DS, DOS does not have a system threads library. Use
CMake/platforms/threads-stub to provide the dummy Threads::Threads target
instead of special-casing DOS in CMakeLists.txt and
Source/CMakeLists.txt.
DevilutionX::SDL carried libdevilutionx_sdl2_to_1_2_backports, so
linking SDL pulled in our own compatibility shims, and with them
libdevilutionx_strings and libdevilutionx_utils_console. Anything that
only wanted the SDL headers got a chunk of devilutionX behind them.
Drop it from DevilutionX::SDL and let the targets that actually use the
shims say so. That is every target whose objects reference a symbol
defined by sdl2_to_1_2_backports.cpp, found by matching the undefined
symbols in each object library against the ones it defines. Object
libraries do not link, so a missing dependency here is invisible at
build time: the archive reached the executable through whichever target
happened to name it.
The dependency is PUBLIC where one of the target's own headers reaches
utils/sdl2_to_1_2_backports.h and PRIVATE where only its .cpp files do.
libdevilutionx_paths already declared it and is moved to PRIVATE on the
same rule: only utils/paths.cpp includes the header, utils/paths.h does
not.
Co-Authored-By: Claude Opus 5 <[email protected]>
Split several monolithic files to reduce transitive dependencies and
enable the dun_render_benchmark to link without bringing in all of
DevilutionX:
- Extract dungeon tile globals and loading functions from `gendung` into
`dun_tile_data.{hpp,cpp}`
- Extract light table data and MakeLightTable from lighting into
`engine/light_tables.{hpp,cpp}`
- Move cursor_id and SelectionRegion to cursor_defs.hpp
- Extract quest log rendering to `panels/quest_log.{hpp,cpp}`
- Extract drlg quest setpiece logic to `levels/drlg_quests.{hpp,cpp}`
- Extract quest data loading to `tables/questdat.{hpp,cpp}`
- Move `GameData` and `gbIsMultiplayer` into `game_mode.{hpp,cpp}`
- Add `parse_sfx_id.{hpp,cpp}`
- Update CMake libraries accordingly
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- `fmt::format` with literal format strings -> `std::format`.
- `fmt::format(fmt::runtime(...))` -> `FormatRuntime` in the new
`utils/format.hpp`, a std::vformat wrapper that is also safe under the
P2905 lvalue requirement of `std::make_format_args`.
- `fmt::dynamic_format_arg_store` (Lua logging) -> a small hand-rolled
per-field formatter over `std::variant`, returning `std::expected`.
- `fmt::format_int` -> `std::to_chars`.
- `fmt::join` -> manual joins at the two call sites.
- SDL_audiolib: new patch that rewrites aulib_log.h to use std::format
and drops the fmt dependency from its CMakeLists.
- The `-static-libstdc++` guard now keys off
`DEVILUTIONX_STATIC_SDL_AUDIOLIB` instead of `DEVILUTIONX_STATIC_LIBFMT`.
- libfmt removed from CMake, vcpkg, Brewfile, CI workflows,
Dockerfiles, packaging scripts, UWP project, and docs.
- macOS bumped to 13.3+, iOS to 16.3+ (required for `<format>`)
- Amiga m68k: provide `__cmpdf2`, which is missing from the hard-float
libgcc multilib but referenced via libstdc++'s long double paths.
Co-Authored-By: Claude Fable 5 <[email protected]>
Now that the project targets C++23, use the standard library's
std::expected instead of the vendored tl::expected polyfill:
- tl::expected -> std::expected, tl::unexpected/tl::make_unexpected -> std::unexpected
- .map()/.map_error() -> .transform()/.transform_error()
- #include <expected.hpp> -> #include <expected>, grouped with std headers
- Remove 3rdParty/tl/expected.hpp (the tl target remains for function_ref.hpp)
Co-Authored-By: Claude Fable 5 <[email protected]>
The loaders returned success with a null/empty buffer when a file could
not be opened in HeadlessMode, breaking every caller that probes
has_value() to pick between asset sources. With Diablo-only data the
Hellfire->Diablo town fallbacks (LoadLvlGFX / LoadMinData /
LoadLevelSOLData) never triggered: entering town dereferenced a null
town.min with uninitialized tileCount in SetDungeonMicros, a null
pMegaTiles in T_FillSector, and silently kept the previous level's
SOLData.
Files that do exist were already loaded identically in headless and
graphical builds; the special case only affected the missing-file path.
Headless code paths are a subset of the graphical ones, so reporting
the error exactly like the graphical build restores the intended
fallback behaviour with no call-site changes.
Unifies "Show Item Graphics" and "Visual Store" boolean options
into a single enum.
Also moves floating info box option next to the store UI option
as they are somewhat related.
play_movie() only early-outs for demo playback, but SVidPlayBegin
requires the SDL video subsystem that HeadlessMode intentionally never
initializes, so any real-time (non-demo) headless session crashes on
the first movie: the Lazarus in-game movie (gendata\fprst3.smk) and
the ending cinematics when Diablo dies. Mirror the existing
demo::IsRunning() early-out; the surrounding fade/redraw paths are
already headless-safe, and this one guard covers every callsite
(intro logos, Lazarus, victory/ending, credits loop).
No behavior change in graphical builds.
lwip/arch.h unconditionally includes <stdio.h>, which on bionic
transitively drags in the real <fcntl.h> before lwip/sockets.h's own
`#ifndef O_NONBLOCK` compat definition is reached, in every
translation unit that includes lwip headers - including lwip's own
sockets.c. So on Android O_NONBLOCK never resolves to lwip's nominal
value of 1, but it does so consistently on both sides of the
lwip_fcntl() call, and netconn_set_nonblocking() only checks
truthiness, so behavior is unaffected.
Co-Authored-By: Claude Sonnet 5 <[email protected]>