fix(ui): D.2b-B — address phase-boundary review (burden % saturation + equipped filter)

Opus phase-boundary review findings:
- I1 (faithfulness): LoadToPercent now computes from the CLAMPED fill
  (floor(LoadToFill(load)*300)), so the burden % SATURATES at 300% like retail
  (decomp 176544-176576 clamps arg2 to [0,1] BEFORE the *300). The old
  floor(load*100) over-read to 400% at 4x capacity. Golden test corrected.
- I2 (partition): exclude equipped items (CurrentlyEquippedLocation != None) from
  the contents grid + selector — a mid-session self-wield routes them through
  MoveItem(item, WielderGuid=player) into GetContents(player); retail's gm3DItemsUI
  shows pack contents only. New conformance test.
- N1: drop dead 'using System.Collections.Generic' (left after the 383e8b7 cleanup).
- N3: AP-48 risk wording (drift can be low OR high vs server EncumbranceVal).

Build green; BurdenMath 17, InventoryController/UiMeter 10 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-21 09:28:29 +02:00
parent 7bb4bd3ae8
commit 1ccf07b705
5 changed files with 33 additions and 9 deletions

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using AcDream.Core.Items;
@ -137,6 +136,11 @@ public sealed class InventoryController
{
var item = _objects.Get(guid);
if (item is null) continue;
// Equipped items belong on the paperdoll (Sub-phase C), never in the pack grid or
// the side-bag list. A mid-session self-wield routes an item through
// MoveItem(item, WielderGuid=player), so it transiently lands in GetContents(player);
// retail's gm3DItemsUI shows pack contents only, gated on the wielded location.
if (item.CurrentlyEquippedLocation != EquipMask.None) continue;
bool isBag = item.Type.HasFlag(ItemType.Container) || item.ItemsCapacity > 0;
var list = isBag ? _containerList : _contentsGrid;
if (list is null) continue;

View file

@ -258,12 +258,14 @@ public static class BurdenMath
}
/// <summary>
/// Retail <c>gmBackpackUI::SetLoadLevel</c> percent text (decomp 176576): after
/// <c>arg2 = load/3</c>, the text is <c>floor(arg2 * 300) = floor(load * 100)</c>,
/// NOT clamped (reads up to 300%+).
/// Retail <c>gmBackpackUI::SetLoadLevel</c> percent text (decomp 176542-176576): the
/// percent is computed from the CLAMPED fill — <c>arg2 = load/3</c> is clamped to [0,1]
/// (the 176544-176563 block sets <c>arg2 = 1.0</c> when fill &gt;= 1) BEFORE
/// <c>floor(arg2 * 300)</c>. So the number SATURATES at 300% (it does NOT read 400% at
/// 4x capacity). Equivalent to <c>floor(LoadToFill(load) * 300)</c>.
/// </summary>
public static int LoadToPercent(float load)
=> (int)System.MathF.Floor(load * 100f);
=> (int)System.MathF.Floor(LoadToFill(load) * 300f);
public static int ComputeMax(int strength, int bonusBurden)
=> BurdenPerStrength * strength + strength * bonusBurden;