Commit Graph

592 Commits

Author SHA1 Message Date
sindoring 76efd63b5b graphify 2026-10-28 16:06:45 +04:00
sindoring 41127aa60c graphify
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 16:02:34 +04:00
sindoring 77d8b76bea MoonWell's main thread gets Linux's 8 MiB of stack on Windows
Since upstream b4d0987a startup recurses deeper than Windows' 1 MiB
default and MoonWell.exe overflowed its stack before the glue screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 15:58:13 +04:00
sindoring 415f054d51 Sync upstream through b4d0987a
Applies upstream c5fc904f..b4d0987a (34 commits): live loot, auction,
mail and trade getters; packet-ordered signals; world click records;
transport props. docs/MAP.md and DEVIATIONS.md regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 15:48:41 +04:00
sindoring ce98cc1f98 Attached items whose idle keys their bones are posed by it
Project Reforged's helms (patch-G) key their root bone in Stand: a
constant translation of (-0.09, 0, -0.18) that seats the helm on the HD
head. benilla drew every resting item at its bind pose, as no stock item
keys its bones, so these helms floated above and in front of the head.

An item whose loader-idle clip poses a bone now gets a pose and a player
like a flexing bow, its palette rows F x model[b] x ibp[b]: the reference
animates every attached M2 (0x714260). Stock items, keyless, are
unchanged.

Verified with creature-sun-front captures of MoonWell.exe at 1600x900
on the install's Data (Reforged A + G), the fixture's display set to
Stormwind guards (5446 female, 1984 male, Helm_Plate_AllianceUprez_B_01):
the helm sat above the head before and sits on it after; display 1862
(stock Helm_Plate_B_01Stormwind) is unchanged. benilla-app's equipment
tests 26/26. The fixture's display override was a working-tree change
only. Built on Windows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 14:49:30 +04:00
sindoring 33f4147522 MoonWell models: a change closes the game instead of restarting it
A restarted process cannot log in: the launcher's login ticket is spent.
The saved choice now just closes the client, and the confirmation (patch-F)
tells the player to start it again through the launcher.

Verified: builds and lints clean (release).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 14:12:00 +04:00
sindoring aadd6ecbf2 graphify
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 14:05:28 +04:00
sindoring 366089e291 moonwell-deploy: a failed multipart part is sent again
Publishing Project Reforged's patch-A.mpq (1.7 GB) failed four times at
a random part (27, 85, 5, 24), and each failure aborted the upload and
restarted the file from part 1. A part is now sent up to six times with
a doubling pause (4 s to 64 s) before the upload fails, and the failure
names the store's error instead of dropping it.

Verified: builds and lints clean; the release publish is running with it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 13:11:19 +04:00
sindoring 10c7c9b7ee graphify
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 12:36:45 +04:00
sindoring c45093e0c6 MoonWell: interface textures up to 2048² draw, not the solid green
Upstream's #2424 holds 1.12.1's device gate on UI textures: a side
that is not a power of two, or is 1024 or more, leaves the solid 8x8
green (0x4484ae-0x4484d0). MoonWell UI (patch-F) draws Forever UI and
its login screens from atlas sheets of 1024² (74) and 2048² (2), all
power-of-two, so after the upstream sync its frames, previews and
glue art turned green.

Core seam (benilla-formats, a few lines): raise_ui_texture_side(side),
set once by the entry point, lifts makes_device_texture's upper bound
(at least 1024); the power-of-two and 8-minimum rules stay. The MoonWell
launcher raises it to 4096, as VanillaHelpers raises the client's limit.

Verified with MoonWell.exe captures at 1600x900 on the release package's
Data: the style chooser's previews and the create screen's vignettes,
banners and bronze frames draw again (green before); the Forever style
in game (a --test-style forever patch-F) draws its frames, bars and
minimap. benilla-formats 549/549 and benilla-assets' green-gate test
pass. Built on Windows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 12:35:32 +04:00
sindoring 5842d4df93 graphify
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 12:10:01 +04:00
sindoring 03e12a8391 Sync upstream through c5fc904f
66 upstream commits since d3b5df3f, applied with the explicit base.
Conflicts: docs/MAP.md and docs/DEVIATIONS.md taken from upstream and
regenerated (scripts/genmap.sh); Cargo.lock taken from upstream and
refreshed for MoonWell's crates; crates/benilla-ui/tests/vm_parity.golden
taken from upstream, its `sweep cos` answer keyed cos@linux-x86_64, as
this fork lists cos among the per-platform cases.

Verified: MoonWell (release) builds; benilla-formats 549/549,
vm_parity, moonwell-gfx 46/46 (the shader anchors hold).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 12:08:48 +04:00
sindoring ebaab7fa41 Tests read the stock chain: added patch archives are left out
The tests that read the install assert the stock 1.12.1 data, so a
player's texture pack (Project Reforged's patch-A, -G, -T...) or an
interface mod (patch-F) in the same Data folder failed them. With
$BENILLA_STOCK_CHAIN set, Chain::open mounts a Data directory's base
set, patch.MPQ, patch-2.MPQ and speech2.MPQ, and leaves out every other
patch-?.MPQ; the language pack's folder is not filtered.
scripts/unit-tests.sh, the gates' test runner, exports it.

Verified: a chain test (stock mode keeps patch and patch-2, drops
patch-A.mpq and patch-F.MPQ; without it all mount); benilla-formats'
547 tests pass on the install with Reforged's A and G under
BENILLA_STOCK_CHAIN=1, where three character tests failed before. The
other crates' install tests were not run here. Built on Windows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 11:51:55 +04:00
sindoring 616cbf3bd0 Character skins at 2x / 4x for high-resolution texture packs
1.12 composites a character's body skin (base skin, face, hair,
underwear, worn armour) into a 256² atlas, and benilla copied each
overlay texel for texel into its 256²-space tile, so a pack's larger
textures landed misplaced or were cut. Project Reforged's patch-T (and
patch-U) ship skins and TextureComponents at 2x (4x) and say so with
VanillaHelpers' convention, VanillaHelpers\ResizeCharacterSkin.txt
holding 2 or 4.

CharSections reads that file at load. At scale 1 the composite is
unchanged. At 2 or 4 the atlas is 512² / 1024²: the base skin and every
overlay are resampled to their scaled tile when their size differs (a
pack texture at its scale fits as is, a stock one is enlarged), blended
at level 0, and the mip pyramid is built by 2x2 averages. A Deviation:
comment names the reference's 256².

Verified: a unit test (a stock 128x64 overlay fills its doubled g1 tile
and nothing else, 10 levels down to 1x1); the character tests pass on
the stock data (33/33; on the install with Reforged's patch-A, three of
them fail with or without this change, as they assert stock data);
benilla-extract charatlas for a human female naked and in plate (display
1184): 256² with patch-A alone, 512² with patch-A + patch-T, the layout
the same and the T one visibly sharper; glue-charcreate captures at
2560x1440 with A and with A + T, the T face sharper. The captures needed
upstream's e4bff386 in the working tree only. Built on Windows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 11:50:37 +04:00
sindoring 4a01c25e2f MoonWell: character models SD or HD, switched by a restart
The install's HD archives (Project Reforged's character set: A
characters and NPCs, G gear, L female body, T and U high-resolution
character and gear textures) are mounted or left out of the chain. The
chain opens once, so a change restarts the client.

Core seam (benilla-formats, ~30 lines): leave_out_archives(names) set
by the entry point before anything opens the chain; Chain::open_client
and open_localized skip those archives of the install's Data folder by
name, in any case. The language pack's folder is not filtered.

The launcher (crates/moonwell/src/models.rs) reads the choice from
benilla-config/moonwell-models.toml (characters = "sd" | "hd", HD when
absent; $MOONWELL_MODELS overrides it for a run) and adds
MoonWell_GetCharacterModels / MoonWell_SetCharacterModels to every glue
and in-game VM; a different choice is saved and the client starts again
with its arguments. The interface for it lives in MoonWell UI (patch-F).

Verified: a chain test (a left-out archive does not mount, the rest keep
their order); glue-charcreate captures of MoonWell.exe at 2560x1440 on a
Data with Reforged's A, G and T, SD and HD showing the stock and the HD
human female; an in-game capture answers MoonWell_GetCharacterModels().
The restart itself was not run (captures save no state). Built on
Windows: release dev build and player build.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 11:50:30 +04:00
sindoring dc6b20580c graphify 2026-10-25 16:02:07 +04:00
sindoring 454893ecd7 Sync upstream through d3b5df3f
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 15:38:44 +04:00
sindoring 1ffc2e9072 graphify: snapshot of 2026-10-23
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 18:13:43 +04:00
sindoring 6976515791 Frame-cost recordings with GPU passes, CPU phases and tags; cheaper MoonWell AO
The FPS journal writes gpu-passes.log (every top-level GPU pass) and cpu-phases.csv (wall,
process and main-thread CPU per main schedule) beside fps-journal.csv, with a session marker
per recording. On Windows the phase clocks count thread cycles (QueryThreadCycleTime /
QueryProcessCycleTime at a TSC rate calibrated against Instant), since tick-sized process
times cannot split a frame. A crate on top labels recordings through FpsJournalTags; each tag
lands in all three files when recording starts and whenever it changes, and
scripts/journal-report.py summarises a recording per session and tag state side by side.

moonwell-gfx tags recordings with its settings (mwgfx) and gets cheaper at renderScale 2:
- the ambient occlusion reads a linear depth pyramid (ao_depth.wgsl, after McGuire's SAO),
  far samples off coarse levels: 2.2-2.4 ms -> ~1.1 ms at 12 MP, image unchanged (max 9/255);
- the water's scene copy is skipped while no view sees a liquid;
- shadow casters take their own visibility class, so the world camera stops listing them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 18:13:00 +04:00
sindoring 4a6a16a9fc graphify
Add the graphify knowledge graph of crates/ and the CLAUDE.md rules for using it. Only the graph, report, viz and manifest are tracked; caches and scratch files are ignored.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:23:33 +04:00
sindoring 768c647b46 Sync upstream through 591358af
Import 48 upstream commits since 9a5f2a56 and retain the ruRU localization: Cyrillic names
keep the twelve-letter cap in upstream's reworked name check, the roster keeps its Unicode
name match beside upstream's new tests, and the spell cost cells take upstream's keyed
GlobalStrings templates, which supersede ours. The workspace stays at 0.3.0 (upstream went
back to 0.2.0 until its 0.3.0 ships); cos stays a per-platform VM parity case. Workspace
tests pass on Windows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:16:49 +04:00
sindoring 19a165fac3 Add MoonWell graphics (moonwell-gfx)
A graphics crate on top of benilla, added through `run_with` in the MoonWell launcher, with
small seams in the core: atmospheric fog, sun shadows (static, terrain and entity casters),
a post pass (SSAO, sun shafts, grading), wind in trees and grass, stylised water after
benilla-watershader (planar mirrors for two heights, SSR, shore foam, character wakes, river
current), light from fires and lamps with per-pixel point lights, halos and torch shadows,
and an in-game options window (/mwgfx, the game menu) with a defaults button. Everything is
off by default and then draws the stock image.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:16:48 +04:00
sindoring f9c63faed7 добавь vscode в гитигнор 2026-10-19 15:37:59 +04:00
sindoring 7752edb982 Sync upstream release 0.3.0
Import 25 commits through 9a5f2a56, retain Russian localization and agreement distribution. Windows locked player build, formatting and offline stock glue compatibility checks pass. Full suites not rerun. Production release requested by maintainer.
2026-10-19 12:59:26 +03:00
sindoring 2bdb1798b7 Sync upstream and restore license agreement distribution
Import 32 upstream commits through a5cb73a8 and include original EULA/TOS in client packages. Windows player build, targeted packaging tests, clippy and offline stock agreement scrolling checks pass. Original data remain outside Git. Full workspace suites not rerun.
2026-10-18 13:51:56 +03:00
sindoring cc4da3cb61 Fix Russian character deletion confirmation
Accept Cyrillic confirmation case-insensitively in the ruRU glue layer. Offline stock GlueXML reproduction and focused verification passed; full suites not rerun per user instruction. Production release authorized.
2026-10-17 23:43:34 +03:00
sindoring d1961e79fc Sync upstream through b2473e60 and retain ruRU (#8)
Integrate 182 upstream commits through b2473e60 while retaining MoonWell packaging, branding and the community ruRU pack.

Upstream replaces the C Lua/mlua host with benilla-lua/benilla-mlua and the native glue screens with stock GlueXML. The update also includes the new locale/DBC/name rules, cache and interaction handling, rendering, quest log and chat behavior, including the latest quest completion and timed-quest failure fixes. Fork adaptations use the new APIs: stock Wow.ini locales remain intact, while the ruRU overlay reads column zero and retains its ruRU authentication tag and Lua identity. Russian names keep their twelve-letter policy and Unicode roster matching; fonts, spell costs, race/class labels and translated options retain real-pack regression coverage.

Windows regression fixes keep the locale-default test valid under instrumented window overrides and the shader assertion independent of checkout line endings. The original C Lua 5.1 host, rebuilt from c0630f3d in an isolated directory, reproduces the complete Windows cos-sweep answer byte for byte; cos now uses platform-specific goldens.

Validation on Windows: scripts/gates.sh passed all automatic gates, including real-data and no-install workspace tests, reference tables, documentation, message catalog, render-pass lint, player clippy/tests and engine boots with and without data. MoonWell player clippy also passed with -D warnings. The committed tree exactly matches the full green stamp; five vendored Kira files retain the tested checkout's line endings, with normalized contents unchanged. All three actual ruRU pack regressions passed in both builds. Fifteen test archives were restored read-only from the published client and SHA-256 checked.

Each real-data/player rung skipped 31 tests because the optional addon corpus is absent (24 general, 7 Quiver). Live smoke was not run: no declared probe account; no player-account login or character creation was performed. Linux/macOS changes were not locally built.
2026-10-17 21:10:31 +03:00
sindoring 241e37d760 Sync upstream UI fixes through c0630f3d (#7)
Integrate six upstream commits through c0630f3d (2026-10-11), preserving MoonWell branding, launcher integration and ruRU localization.

The update fixes OnUpdate ordering by strata/level/link order, bare Lua errors passed to the error handler, case-insensitive addon file lookup and UNIT_FACTION on faction-template changes. It also adds an optional Bevy dynamic-linking feature and lets test gates report all failing crates.

Merge conflicts retain localized spell costs, race/class display names, Cyrillic character-name input and the twelve-letter creation limit. UI error handling and update traversal follow upstream. docs/MAP.md was regenerated and has no delta.

Validation: full scripts/gates.sh passed on the exact source tree (1884 s) on Windows with the real 1.12.1 MPQ/DBC data and ruRU pack, including workspace tests with and without install data, player tests without dev, and engine boot checks. The 31 optional addon corpus tests are unavailable on this checkout. No live account login was attempted because no probe identity is configured. The optional dynamic-linking feature is retained but not enabled by the default gates.
2026-10-11 17:52:06 +03:00
sindoring d201c8e469 Fix Russian spell costs, player labels and character names (#6)
Spell resource costs, UnitRace/UnitClass and the character-name field were still using English-only paths after enabling ruRU. Costs now fill the selected GlobalStrings templates, the player record and live unit feeds use ChrRaces/ChrClasses display names while retaining file tokens, and typed/pasted Unicode letters reach the UTF-8 create packet. CharacterCreateNameEdit now initializes its reference limit of 12 letters; a 12-letter Cyrillic name retains all 24 UTF-8 bytes. Roster selection also matches the server's recased Cyrillic name.

Reference: 1.12 PaperDollFrame_SetLevel reads UnitRace/UnitClass; ruRU GlobalStrings supplies MANA_COST/RAGE_COST/ENERGY_COST and their PER_TIME forms; the selected ChrRaces/ChrClasses DBCs supply display names. vmangos normalizePlayerName decodes UTF-8 and recases letters: https://github.com/vmangos/core/blob/development/src/game/ObjectMgr.cpp#L60.

Original symptoms reproduced by instruments with real client data: 30 Mana, Human/Warrior in CharacterLevelText, and Cyrillic keyboard text rejected. The same instruments check Russian and adjacent English text, Human/Warrior and Tauren/Druid, Unicode typing/paste/backspace and the 12-letter cap. The create packet and normalized roster spelling have separate checks.

Validation: the original three symptoms reproduced, then the same three instruments passed with real ruRU and adjacent enUS data. Full land gates passed on the final source tree (816 s): fmt, workspace clippy, workspace tests with data and without an install, doc links, render-pass lint, player tests without dev, and engine boot with/without data. Only the 31 absent addon-corpus tests skipped on each real-data test rung. The strengthened create-packet check verifies the actual reset field, its 12-letter cap and unmodified UTF-8. Built on Windows. No live character creation or player-account login (no declared probe account).
2026-10-11 17:04:58 +03:00
sindoring 2ef92a2032 Sync GitHub upstream build profiles through 710eb8f5 (#5)
Bring the fork from its already integrated upstream c2fc3cf3 snapshot to 710eb8f531. The new upstream commit changes only development/play build profiles: dev ThinLTO is disabled and dependencies use one codegen unit. Production release and ship profiles are unchanged; MoonWell integration and Russian localization are retained.

Source: upstream commit 710eb8f5, PR samwhosung/benilla#857. The patch is applied unchanged. MAP was regenerated and has no delta.

Validation on Windows: formatting and locked workspace metadata pass. All automated land gates passed on the final tree (4100 seconds): clippy, workspace tests with and without installed data, documentation and render-pass checks, player-build tests and both engine boot checks. The only skipped tests are 31 optional vanilla-addon/Quiver corpus tests, because that corpus is unavailable. Parsed TOML confirms that all profiles match upstream and that release/ship profiles and workspace dependencies are unchanged. Russian roster labels, character creation names, deletion text and locale-aware NPC-cache tests passed against the real archives. No declared probe account is available for a live game login. Upstream performance figures were measured on macOS and are not claimed for Windows.
2026-10-11 13:31:24 +03:00
sindoring ac0a4fbc47 Fix Russian character captions and NPC locale cache (#4)
Russian character selection displays literal numbered placeholders, and character creation uses English race/class names even when its descriptions are translated. Resolve display names from the selected ChrRaces/ChrClasses DBCs and use the existing numbered-argument formatter for roster rows and deletion prompts.

Creature caches were always written and accepted as enUS. Write the selected client locale into the header and reject caches from another language, allowing the server to answer fresh NPC-name queries.

Reference: the supplied ruRU pack has CHARACTER_SELECT_INFO ordered as class then level, and its DBC name slots contain the Russian labels. The deployed server has 9,755 Russian NPC names; existing local caches contain 101 English-only NPC records. The reported quest giver is Guard Thomas (261), starting quest 37; its Russian name exists in the deployed database.

Validation: ten targeted Windows checks passed against the supplied archives with data required and no skips. All automated land gates passed: formatting, clippy, tests with and without installed data, player-build tests, documentation and enforcers. The 31 optional addon-corpus tests skip because the vanilla addon/Quiver corpus is unavailable. Tests cover real ruRU roster rows including ghosts, Human/Warrior and Tauren/Druid creation headers, changing class-button labels, deletion text without deleting, English fallback, and cache rejection across enUS/ruRU. Windows only. No unattended game login was performed because this checkout has no declared probe account.
2026-10-10 19:22:46 +03:00
sindoring 53b0907393 Add ruRU client localization (#2)
The client selects an installed Data/ruRU pack for assets, DBCs, Lua GetLocale, Cyrillic fonts and realmd locale. WOW_LOCALE supports auto, enUS and ruRU. Localize MoonWell options, menu, bindings and script log; preserve English reference loading and fallback. The deployment tool stages and validates the supplied ruRU archives without changing the install.

Rebased on main 97a92194, which integrates approved GitHub upstream c2fc3cf3. No conflicts were required for localization.

Windows validation on the exact final tree: scripts/gates.sh ALL GATES GREEN (2671 seconds), workspace tests with and without reference data, player build tests and engine boot checks. Russian FrameXML, captions, settings/search, Cyrillic font glyphs and locale challenge tests are included. 31 tests skipped only because the addon corpus is absent. No declared account for a live login; other platforms were not built. Game assets remain outside Git.
2026-10-10 16:34:15 +03:00
sindoring 97a92194cc Integrate GitHub upstream UI fidelity updates (#3)
Update the fork to the approved upstream/main snapshot c2fc3cf3, including UI scale, load/reload lifecycle and visibility fixes. Preserve MoonWell integration and live-player seating during ReloadUI. MAP was regenerated.

Windows validation: all scripts/gates.sh gates green (2963 seconds), with and without vanilla data, player build tests and engine boot checks. 31 tests skipped only for the absent addon corpus. No declared account for live login; other platforms were not built.
2026-10-10 15:48:17 +03:00
sindoring da72c705f1 Integrate MoonWell launcher and deployment tools
Integrate the MoonWell launcher and deployment tooling on the local upstream baseline d0d1b5de. Correct the shipped icon path, regenerate the code map, and make source/Lua gates portable to Windows. Classify launcher startup icon access as input plumbing.

Verified on Windows: scripts/gates.sh ALL GATES GREEN; MoonWell release build without dev; 12 deployment tests; deployment and packaging script syntax. Stock MPQ chain excludes the custom patch-3. Thirty-one addon tests skip per data-enabled rung because the corpus is absent. Native macOS/Linux and live login smoke were not run; no declared test account is available.
2026-10-08 14:01:54 +03:00
Mathih13 b396bbf69a Click to Move walks to the ground, along the sky and into melee (#783)
With Click to Move on, benilla walked only to things you interact with. In 1.12.1 a right-click on the ground walks to the point (`0x5e0378` → `0x6102b0`, 0.5 yd short, speed-scaled, no leash, held in a deck's frame). A right-click on the sky with an empty cursor walks along the ray (`0x492dca` → `0x610100`), pitching while swimming. An attack on an enemy out of melee walks to where it stood (`0x60c22b` → `0x60fcc0`, mode 10): the stop is about 1.9 yd because the arm names the player's own guid, the radii are unscaled, and the 80 yd leash says ERR_AUTOFOLLOW_TOO_FAR. Inside melee the player turns to face it.

Deviations, each written where it lives: the melee point keeps the enemy's height, does not snap to the reference's fixed arrival heading, and rides the deck it was armed on.

Tests: the router, the steering, the gates, the leash and the deck frames.

Fixes #758

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Mathih13 <15229325+Mathih13@users.noreply.github.com>
Co-Authored-By: Sam <samuv.eth@gmail.com>
2026-10-02 21:03:36 +07:00
Mathih13 9821765451 A loot window opening on a unit selects it, as in 1.12.1 (#779)
In 1.12.1 the `SMSG_LOOT_RESPONSE` handler selects a looted unit before it opens the window (`0x5ebc35` → `0x48f3a0`, SetSelection at `0x48f3be`). benilla opened the window without selecting it. So a Tab pressed during a Click-to-Move walk to a corpse left the Tab target selected when the loot opened.

The handler now admits the response and arms the latch, then selects a unit through the one SetSelection path, then opens the window. A refusal, a chest or an item selects nothing. SetSelection's re-swing now uses StartAttack's target gate (alive, then CanAttack, `0x5ecc16`), so switching to a body mid-swing never swings at it.

Tests: the handler's select, dedup, refusals, teardown order and swing stop, a hostile body looted mid-swing, and the stock TargetFrame naming the looted body.

Fixes #763

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 17:33:06 +07:00
HEOJUNFO 6c25640a37 Light bands with out-of-order keys interpolate as a cycle, as in 1.12.1 (#768)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 14:14:41 +07:00
Andrew Barnes c627483a85 Return numeric flags from spell-targeting predicates (#764)
Use binding_abi::flag for SpellIsTargeting and SpellCanTargetUnit so addons
can compare their results with 1, as in 1.12.1. Keep nil for false.

Verified on Windows with Rust 1.98.1: three regression failures before
the fix, all 35 focused spell tests passing after it, formatting and
render-pass lint clean. The full UI run passed 1677 tests, with one
ignored and one traceback-name failure reproduced on untouched main.

Fixes #711
2026-10-02 13:45:16 +07:00
Sam Sung 4fd0c643a6 One-shot emotes: LiftOff/Land in combat, no restarts, local play (#773)
Three ways the one-shot emote player differed from 1.12.1.

- **LiftOff and Land were refused in combat.** `0x5fcd20` exempts AnimIDs 192 and 200 from its combat test only (`0x5fcd5f`-`0x5fcd92`), so a dragon's take-off and landing now play mid-fight.
- **An emote could restart a clip already armed.** The player skips an id already armed on the key bone or bone 0 (`0x5fcd56`, `0x5fdb50`). It compares the requested id, which op4 stores at `+0xf8` (`0x71252f`), not the fallback that plays.
- **Your own emote waited for the server.** `DoEmote` plays it locally while a direction key is held (`0x5fd680`, called at `0x5ef660`), so a moving `/wave` shows at once and its echo is skipped.

Tests cover the exemption, the armed test (including on a rider), each local-play gate, and the skipped echo.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:58:50 +07:00
Sam Sung 56d8011ab7 NPCs pause their work loop while you talk to them, as in 1.12.1 (#772)
Two ways the emote-state base pick differed from 1.12.1.

- A blacksmith kept hammering while you used his vendor window. The resolver `0x5fd770` passes over a state whose `Emotes.dbc` flags have `0x2000` (work, mining, ready, dance) while the unit is the interact NPC (`0x5fd7e2`-`0x5fd7fd`). `SetInteractNPC` re-picks on open (`0x493198`) and on close (`0x493219`, before the name clears at `0x49334b`), never for a player. So the NPC stands, and the loop resumes at its next re-pick.
- A set state outranks the idle fallbacks: `0x5fd770` runs before `0x5fd830`, and a row with AnimID 0 claims Stand. After `/stand` (state 26), a swimmer at rest shows Stand until it moves.

Tests cover the open, the close hold, players, a pose under a one-shot, and the precedence order.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:32:20 +07:00
Sam Sung 2dc7d8a9e6 /wave <name> targets the named player; two DoEmote gates follow 1.12.1 (#769)
Three ways `DoEmote` and the chat talk gesture differed from 1.12.1:

- `/wave Bob` ignored the name and targeted the selection. 1.12.1 resolves a name that does not start with `%` as a player, at any range (`0x49fdb1`-`0x49fdf0`, `0x493aa0`), and sends the emote untargeted on a miss. `DoEmote(token, name)` from Lua does the same.
- A polymorphed, stunned or sleeping speaker still played the talk gesture. `UNIT_FIELD_FLAGS 0x20000000` suppresses it when the sender's name is cached (`0x49d7f7`).
- `DoEmote` sent while our unit was possessed (`0x5ef57e`). A chat-only emote now reads `Emotes.dbc` row 0's flags, so it is refused while asleep or dead (`0x5ef5b1`, `0x47db8e`).

Tests cover the name, miss, `%t` and self cases, the gesture gate, and both `DoEmote` gates against the install's row 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 09:23:08 +07:00
Sam Sung fbea2d9f3a Emote routing follows 1.12.1's upper-body and mount tables (#767)
One-shot emotes were routed by a hand-made class table that wrongly let `/pray` (75), the `/beg` family (79), and 126, 127, 131, 132 and 135 play upper-body. A mounted rider played every id on its upper body.

1.12.1 reads two byte tables:
- CLASS_A (`0x5fed90`): the ids that may play on the key bone.
- The mount set (`0x5fec80`): what goes to the mount model while mounted (`0x5fe7b5`..`0x5fe803`). Anything else plays nowhere.

What changed:
- Both tables now match the bytes, and a mount-set id is forwarded to the mount child.
- Unmounted `/eat` (61) always plays upper-body (`0x5fe834`).
- A model with no split bone arms nothing for an upper-body play (`0x5fdcc4`).

Tests pin both tables for every id 0..300, plus the mounted, eat and no-bone driver cases.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 09:02:52 +07:00
Sam Sung 4f36c843f1 Emote voices play only for you and your party, as in 1.12.1 (#766)
Every text emote's voice played for every performer, so every `/cheer` in a city was heard. 1.12.1's composer tail (`0x623c80`) plays it only for you or a member of the four party slots (`0x623cc2`-`0x623cce`, `0x5efea0`), and drops an ignored sender before that (`0x49dc36`).

What changed:
- The voice plays at attachment `0x11` and stops the unit's previous emote voice once a new one opens (`0x623c10`).
- Race and sex come from the display override before the descriptor (`0x60c6a0`/`0x60c6c0`).
- `SMSG_EMOTE` no longer plays `EventSoundID`. The one-shot path never reads it (`0x5e66b0` → `0x5fcd20`); only the state emote's `$ESD` does.

Tests cover each relation class, an ignored performer and the per-unit voice slot.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 08:32:29 +07:00
Mathih13 e620a0d095 feat: let a crate on top interact with an object it picks (#753)
- A crate on top of benilla interacts with an object it picks through `Interact`, the object's own dispatcher with no select, and reads the streamed objects through `Objects`
- A right-click on a GameObject, a corpse or empty world no longer marks the selection changed; only the unit leg's select touches it, as before the click took the pick
- Interact's attack leg selects its unit through StartAttack, as the click does
- An outside crate test reads Objects and writes Interact
- Wrap the Interact finish's doc lines at the column limit
- The outside-crate test's comment says what it picks

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Sam <samuv.eth@gmail.com>
2026-10-02 06:38:12 +07:00
Mathih13 2ce6a3e881 feat: click to move walks to an out-of-reach target, then interacts (#752)
- Click to Move: with AutoInteract on, a right-click on an NPC, a body to loot or skin, or a GameObject out of reach walks there and runs the verb on arrival
- Click to Move: the arrival scales by the mover's current speed, and a Use still out of range walks again
- Click to Move: a Loot walk stops at the square root of the melee reach
- Put the meeting-stone test's doc comment back on its test
- A GameObject click out of range raises ERR_USE_TOO_FAR unless the walk starts
- Wrap the finish's doc lines at the column limit

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Sam <samuv.eth@gmail.com>
2026-10-02 05:57:47 +07:00
Mathih13 cfd5fd2c8a fix: stop shift-click opening the stack splitter on vendor bundles (#751)
- Shift-clicking water or arrows at a vendor takes one bundle, as 1.12.1 does
- Drop the merchant comments that restate the code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 04:54:20 +07:00
Sam Sung f22ec0016c EditBox caret keys, selection and focus blink follow 1.12.1 (#750)
Several EditBox keys differed from 1.12.1:
- Up/Down did nothing in a multi-line box;
- Home/End went to the text's ends;
- `HighlightText` and Ctrl+A moved the caret;
- focus did not restart the blink;
- a non-digit kept a numeric box's selection;
- Option+Up could not recall chat history on a Mac.

In 1.12.1, Up/Down move a wrapped row, keeping the letter column (`0x77cb20`), or recall history in a single-line box with any modifier but Shift. Home/End stop at a newline (`0x77c980`, `0x77c9f0`). `HighlightText` writes only the selection (`0x77cca0`), and Shift extends by `0x77cd10`. Focus restarts the blink (`0x77dd68`), and Insert deletes the selection before its digit test (`0x77bf41`).

benilla now does each. Down onto the last row lands at the end, and history stays shell-style, both deviations.

Tests cover each key, in the engine and the stock mail and ticket boxes.

Fixes #735
Fixes #736
Fixes #737
Fixes #738
Fixes #745
Fixes #747

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 10:56:03 +07:00
Sam Sung be9e3d8aac Keys are named by the active layout, as 1.12.1 names them (#748)
Letter and punctuation keys were named by their US position, so on AZERTY the key labelled Z bound as W, and Ctrl+A sat on the key labelled Q.

1.12.1 on Windows names a key from the VK its layout produced. `VK_A`–`VK_Z` and digits are named as themselves, and other keys by `MapVirtualKeyA` (`0x42da39`); a key with no character is dropped (`0x42da49`). The EditBox's Ctrl chords switch on that same name (`0x77b1fb`). The Mac client names keys by `KeyTranslate`.

Now:
- Windows takes the same path through `MapVirtualKeyExW`.
- The Mac names keys through the current layout, or the ASCII-capable layout it pairs with.
- Linux follows one documented rule.
- Bindings, Key Bindings capture and Ctrl/Cmd+A/C/X/V all follow the layout.
- A character above U+00FF names its key, as a deviation.

Tests cover AZERTY, Hebrew, Greek, Lithuanian, Turkish, Bulgarian, Hungarian and Dvorak names, layout switches and chords.

Fixes #621
Fixes #746

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 09:55:26 +07:00
Sam Sung e9f0e89a0b EditBox events fire from each box's flush, as 1.12.1 orders them (#734)
EditBox events fired from hoisted drains before any OnUpdate, OnTextChanged before OnCursorChanged, and only on an edit.

In 1.12.1:
- Each box's update `0x77a790` fires its own `OnUpdate` and then its flush `0x77d3e0`, and a key-down or click flushes first (`0x77b1e2`, `0x77b819`).
- The flush runs off dirty bits in `[E+0x31c]`: relayout on bit 0, then `OnCursorChanged` on bit 2, then `OnTextChanged`.
- The ctor sets bit 0, so a box fires at its first show. Every text-region re-seat raises bits 0–2 (`0x77ba7f`), so a resize, new insets, `SetMultiLine` or a font change fires it again.

benilla now keeps the same bits and flushes each box in the walk and at its keys and clicks. So typing DELETE and pressing Enter in one frame enables the popup's button, as in 1.12.1.

Tests cover the order, first show, re-seats, a key's flush and no-op moves.

Fixes #724
Fixes #727

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 07:38:38 +07:00
Mathih13 87f65faffd fix: stamp the chat log and FrameXML.log in local time (#723)
The reference stamps each log line from GetLocalTime (0x65a871); benilla stamped UTC. Unix reads
localtime_r, Windows calls GetLocalTime. Built and tested on macOS; the unix path type-checked for
x86_64 Linux and the Windows path for x86_64-pc-windows-msvc in a scratch crate.

Fixes #586

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 07:21:57 +07:00