- inject IFaceManager into MapHeroRenderer; degrade/next use Dir4FaceHandler via FaceGroup.Dir4, movement uses hero.mover.faceHandler (Dir8)
- IMapExtensionManager.addHero gains faceManager param; manager.ts forwards it
- split MapHeroHook into MapHeroLocationHook / MapHeroMoverHook; onSetPos unconditional
- handle ObjectMoveType.AnimDir via step.dir
- replace mutate-animate TimingFn with @motajs/animate ExcitationCurve2D in hero.ts
- clone() now binds each cloned modifier to the clone, records its modifierName, mirrors the source save flag and recomputes the attribute, matching addModifier's bookkeeping
- a cloned attribute can enumerate, index and persist its modifiers, and a cloned modifier's setValue notifies the clone
- add the bookkeeping and save-ability regression cases
- both multiplier checks tighten from < 1 to <= 1, matching warning 149's own text (must be greater than 1)
- an exact multiplier of 1 now warns 149 and falls back to the existing factor 2, so buffer expansion always terminates
- add the multiplier-of-one regression case (two code 149 warnings, 15 appended steps all read back in order)
- normalizeParam returns null for an unencodable param value, matching its own jsDoc and the documented discard semantics of normalizeParamList
- the command param count, the bytes actually written and the param cursor stay consistent so later commands keep their correct byte offsets
- add the discard-alignment regression case (count 0, later command bytes intact, read stream aligned)
- compareEquip clones each compared item's modifier before adding it to the comparison clone and removes the same clone objects afterwards
- the live attribute's already-bound modifiers never enter the clone and are never unbound
- add the equipped-item diff regression case (both directions, no warning 108, live bookkeeping untouched)
- add private clearDynamics() that snapshots the managed dynamic tiles and drops each via the deleteDynamic semantics (syncStaticEvent + removeTile + onDeleteDynamic), without awaiting the hook
- call it as the first line of loadState so all three compression tiers and the missing-dynamicBlocks case start from a clean slate (#06-17-8)
- add regressions: mapLayer hook/instance witness and the three-tier repeated-load non-accumulation case
- deleteEnemy removes the deleted view from point.affectedBy and drops its point.damages entry so lazy refreshIndex never revisits a dead view (IN-01)
- deleteMapDamage removes the sourcelessDamage IPointInfo once both damages and affectedBy are empty, keeping markDirtyIndex
- add IN-01 regressions: dead-view witness (getDamageWithoutCheck not called) and the two arms of the sourceless empty-point cleanup
removeEnemyAffecting collects removed coordinate indexes and markDirtyIndex each; refreshEnemyAndClearCache/refreshEnemy register enemyStore.set(view, set) before the empty-set return so later markEnemyDirty uses local refresh instead of refreshAll(). Adds a Chinese comment before each new it.
49aacb0 renamed ReplayMoveCommand -> ReplayMove (and siblings); sync the two stale expectations in data-state/src/replay/commands.test.ts (constructor names + core.ts source assertions).
Adds a third metric that calls the real calculateCritical for every monster on
the merged map and fully consumes the generator, plus a second console.table
with monsters / total ms (sampled median of one full-map sweep) / avg ms.
avg ms equals total ms divided by the monster count.
Merges the first 1/5/13 real 13x13 maps into one grid-tiled map
(13x13, 39x26, 52x52 with 11/79/204 monsters), builds the scene from a real
CoreState (real converters/reducer/calculator/final effect), assigns 12 real
Enemy prefabs (3 CommonAura ranges + GuardAura) via a seeded mulberry32, and
records buildup + map damage build timings with performance.mark/measure.
Zero assertions.
- measureCase sampling loop now pairs mark(startTag)/mark(endTag) with
measure(measureName, startTag, endTag) and clears marks/measures per sample
- tag names derive from caseName+scale so they never collide across cases
- WARMUP_RUNS/SAMPLE_RUNS/median/min/p95/toFixed(3)/columns unchanged
- HeroAttribute.loadState replaces its private base object; the HeroAttribute wrapper identity is preserved so equipment/combat references stay valid across load (#06-17-1/#06-17-2).
- Clone the incoming base attribute in the constructor so the shared HERO_DEFAULT_ATTRIBUTE is no longer mutated across CoreState instances.
- Use any for the hero position in combat handler/aura/enemy-view generics to clear the 45 new type errors introduced by HeroAttribute implementing ISaveableContent.
- migrate the top-level snapshot assertion to heroSave.attribute.values.hp
- add CoreState enemyContext.getBindedHero() regressions: same live attribute before/after load, no rebinding, across all compressions
- IHeroStateSave.attribute becomes IHeroAttributeSave<THero>; top-level modifiers removed
- HeroState.attribute is readonly and loadState delegates to attribute.loadState (never reassigns)
- delete attachAttribute from IHeroState and HeroState, and its only test case
- thin-delegate registerModifier/createModifier/createAndInsertModifier to the attribute registry
- loadEquipEffect mounts equipment modifiers with save=false so equipment bonuses only return via re-equip
- add #06-17-1 regressions: same attribute instance, bonuses kept, no double-count when type is registered
- expand COMPRESSIONS to NoCompression / LowCompression / HighCompression so the matrix is 3 map counts x 3 compressions x 3 timing items = 27 rows
- keep measureCase, fixture build order, side load and 06-16 files untouched; still zero assertions
- one full run: 5 perf files / 45 tests passed, 0 WARNING/ERROR codes, saveablesReal.perf.ts (27 tests) in 379ms -- no case approaches the 30000ms testTimeout
- move root floors.json into packages-user/data-state/test/fixtures/ (byte-identical) and delete the root copy
- add saveablesReal.perf.ts: toFlat/clearCodes/registerTiles/createRealMapFixture with a one-shot compareWith baseline
- seed the minimum 1 equipment instance so HeroEquipsStore.loadState does not raise error 58
- 3 rows: save / load / round trip for 1 map x NoCompression, zero assertions
- D-06: equipment.test.ts code-147 case stays it.skip with a Chinese note that 147 is a reserved code, currently unreachable by design; no production code touched
- WINDOWS.md ids 25/26 marked fixed (open_count 13 -> 11); no new ledger entries
- getCouldEquipSlot tests empty === -1 so the first empty named slot wins instead of always replacing the first match
- un-skip the named-slot empty-preference case; canEquipTo string branch untouched
- recalculateAttribute writes the base value into finalAttribute when no modifier is attached
- un-skip the base-only-change case; catchCalculateProgress is intentionally left untouched
- saveState returns new Map(this.equips) and [...this.slots] so the snapshot is decoupled from the live state
- un-skip the independent-snapshot and container round-trip cases
- loadNoCompression reads value entries from state.value instead of state.percentage
- loadDiff seeds this.value/this.percentage from the item.equip base definition before applying save diffs
- un-skip the four correct-expectation EquipmentState round-trip cases (Low/High percentage, NoCompression value, compressed unchanged value)
- Move paramArray bytes to paramStart + length so later steps keep their data
- Move indexArray entries to index + 1 so get(i) sees the inserted step
- Un-skip both insert order cases
- Add a private getParamRange helper using paramUsed as the last-step sentinel
- Roll back indexArray from the command index instead of the param byte offset
- Un-skip the middle-step delete cases
- Encode bigint bytes by bit-shift mask instead of the broken accumulator
- Decode the bigint length prefix and payload bytes unsigned
- Encode negative bigint as dedicated type 8 with magnitude payload
- Un-skip the multi-byte and heterogeneous bigint cases, add focused cases
- Correct the int64 decode multiplier to 2147483648
- Encode negative int64 as dedicated type 5 with magnitude payload
- Shift float/bigint/long-string/short-string type codes to 6/7/9/length+9
- Un-skip the int64 round-trip case and add negative int64 focused cases
- createEnemy/createEnemyById now resolve prefabs through internalGetPrefab,
matching getPrefab/deletePrefab/modifyPrefabAttribute behaviour
- add a Chinese invariant comment above createEnemyById covering all
code/id prefab entry points
- un-skip the two correct-expectation reuse mapping cases in manager.test.ts
(assertions unchanged) and sync their Chinese comments
- read the before result into proceed and abandon only on falsy, matching the
ICombatScript.before contract in types.ts
- un-skip the correct-expectation case and correct the three existing cases that
encoded the inverted semantics (explicit truthy beforeResult, complementary
truthy-branch assertion, synced FakeScript comment)
- register viewStore/damageStore reverse indexes when sourced damage is written
and re-register them after a refreshIndex recompute (route A)
- deleteEnemy now finds and removes the enemy-sourced damage instead of leaving
stale entries behind
- un-skip the correct-expectation case in mapDamage.test.ts (#06-01-2)
- reset every EnemyView before a full buildup so repeated builds start from
the base enemy instead of compounding on the previous result
- un-skip the correct-expectation cases in damage.test.ts (#06-01-4) and
context.test.ts (#06-15-1)
- move targetInfo assignment into the damage < referenceDamage branch so the
reported critical info matches the yielded next value
- un-skip the correct-expectation case in damage.test.ts (#06-01-1)
- add pplies a global aura after addAura and stops applying it after deleteAura (it.skip, D-05)
- reuse in-file createContextFixture/createEnemy/FakeAura/FakeConverter, no new helpers
- register finding #06-15-1 in 06-TEST-FINDINGS.md (same root cause as #06-01-4)
- unskip-verified: addAura applies (atk 2 -> 5) but deleteAura + buildup stays 5, expected 2
- add correct-expectation it.skip for the heterogeneous route after delete(1) (anchor #06-04-3)
- add correct-expectation it.skip for the heterogeneous route after insert(2, 9, [true, 5]) (anchor #06-04-4)
- add file-inline expectRouteStream helper (stream-only typed reads with index 1..N and terminal null)
- append the create-or-append 06-14 section to 06-COVERAGE-MAP.md; existing 5 skips unchanged
- rewrite expectHeterogeneousRead to assert per-param typeof + value, stream index i+1, terminal null and stream.index === length
- add per-param typeof/value and stream.index progression to the sequence and expand stream cases
- add 'expires a read stream after the heterogeneous route is mutated': add() expires the old stream (warn 155) and a fresh stream reads the new 7-step order
- seedState/mutateState/assertRestored now cover every key state: hero base attrs + modifier final atk + location, flags score/coins/stage, map two blocks + active status, enemy hp/atk, replay steps
- Replay seeded with 10 @system/replay steps mixing Up/Right/Down/Left/Teleport/UseItem/Equip/Unequip and varied param types, asserted per step
- New container test loops flags and replay through CoreState across the three compression tiers
- Append 06-13 create-or-append section to 06-COVERAGE-MAP.md; existing #06-09-1..5 skips unchanged
- StaticTile covered-event round trip looped over NoCompression/LowCompression/HighCompression
- DynamicTile covered-event round trip looped over the three tiers, keeping the tile num assertion
- Existing #06-09-3 it.skip unchanged
- mapLayer.test.ts: both keepEvent branches verified after a real-timer move,
with exact event maps, dirty flags, block write-back and dynamic index updates
- 06-COVERAGE-MAP.md: append the 06-11 gap-fill section (create-or-append)
- gameMap.test.ts: three layers at z-index 1/5/9 keep independent block data and
are judged separately by z-index during compareWith (missing ref marks dirty)
- equipment.test.ts: G-06-05-A asserts combined atk/def for weapon+armor and keeps the other slot's modifier after unequip
- mapState.test.ts: G-06-06-B writes/reads block, point event, event layer binding and dirty flag on a createMap floor
- drive real MainDamageCalculator over a monster with 10 top-level specials,
real aura 25, real support 26, common/special query and final effects
- assert one exact {damage,turn} with full per-branch derivation in comments
- add a control asserting the result differs without the support pipeline
- append the 06-10 coverage-map section (create-or-append, no new codes)
- G-06-01-A: four effect kinds together, assert stage order and final
atk/def/hp/special on the computed enemy
- G-06-01-B: m1 -> A1 -> m2 -> A2 -> all enemies, assert per-enemy final
atk plus A1 range boundary via special 21 ownership
- play a recorded small-map route with bounded waiting and recording disabled
- assert a second recording equals the first step-by-step (code and param type/value)
- trigger warn 176 and errors 2001-2008 through the real replay commands
- append 06-07 code coverage map rows and record finding #06-07-1
- combine multiple top-level specials through the real damage calculator
- cover real support recursion, aura base/query combination and stage order
- cover final-effect priority order and damage cache markDirty/deleteEnemy/with
- add real-API single-special damage baselines via EnemyManager prefab
- add real EnemyContext single common aura and single guard aura baselines
- inline synthetic fixtures with no production changes
- add data-state/test/saveablesRoundTrip.test.ts: public CoreState.saveState/loadState over 5 saveables x 3 compressions, codes 112/113/177/178
- append 06-09 section to 06-COVERAGE-MAP.md (11 codes, create-or-append)
- register #06-09-1..5 suspected defects and the pre-existing test:ci blocker in 06-TEST-FINDINGS.md
- cover setPos/setFaceDir/setMoveDir/jump/stepFace/forward/backward/speed/face/animDir/push/clear/start
- add controller stop and start-while-moving contract checks with real timers and await onEnd
- mark the multi-step backward case as skipped suspected bug #06-08-1
- mover.test.ts: successful step and layer reindex, cumulative/diagonal movement, warn 126 on an unexpected move code, lifecycle hook order, concurrent start rejection, initial face direction
- registry/attribute defaults incl. error 53 for non-serializable values
- prefab add/get/create/delete/change and reuse mapping reads
- modifyPrefabAttribute callback and index rebuild
- comparer lifecycle: warn 117 on repeat compareWith, warn 118 without comparer
- skip #06-03-1 (createEnemy does not resolve reuse mapping) per D-05
- enemy.test.ts: attribute get/set/add independence and cloneAttributes deep copy
- special.test.ts: CommonSerializableSpecial value/name/description and NonePropertySpecial units
- fixtures inline, legacy and save/load excluded per D-30/D-32
- add a normal case for GuardAuraConverter.convert (code 26 -> GuardAura)
- assert GuardAura priority/couldApplyBase/couldApplySpecial/range/applySpecial
- keeps D-30 per-public-method coverage symmetric with CommonAura
- add D-26 attribute-to-damage linkage and getCalculator coverage in damage.test.ts
- cover markEnemyDirty refresh and unregistered delete in mapDamage.test.ts
- cover off-map handler and standalone computed enemy in combat.test.ts
- register suspected buildup compounding bug as #06-01-4 it.skip with a findings entry
- complete the 06-COVERAGE-MAP.md code rows for 106/107/102/103/104/138/139/140/141
- cover full buildup aura conversion and one/two-layer nested auras
- assert four-stage order specials -> base -> query -> final with forward-only visibility
- cover local requestRefresh, needTotallyRefresh full fallback, and warn codes 97/98/99/100/101/110
- fill the 06-COVERAGE-MAP.md rows for the stage 2 warning codes
- Zone/Repulse/Laser/Between/Ambush views: range params, damage info, guards
- MainMapDamageConverter: per-special view order and laser face lookup
- MainMapDamageReducer: sum, max-damage type and catch/repulse union
- MainDamageCalculator: base, defeat, all special branches, guard recursion/warn 137, negative-damage flag, critical limit
- MainEnemyFinalEffect: solid/mimic/unchanged branches and priority
- MainEnemyComparer: base attributes, special count/code/value deep equality
- add combat.test.ts for same-state binding, foreign-state warn 138, script priority sort and duplicate warn 140, guard warns 139/141, awaited script/hook ordering with real timers, and battleComputed delegation
- extend the 06-TEST-FINDINGS.md sink with the #06-01-3 before-return semantics row
- add mapDamage.test.ts for sourceless add/delete, sourced conversion, reduction caching, warns 102/103/104 and the #06-01-2 deleteEnemy finding
- add context.test.ts for the enemy registry (register/lookup/unknown-null/delete/resize/scan) and effect-registry add/remove behavior
- extend the 06-TEST-FINDINGS.md sink with the #06-01-2 row
- add damage.test.ts covering DamageContext/DamageSystem results, handler identity, warns 106/107, cache invalidation, with(hero) and calculateCritical
- declare api-coverage in COVERAGE.md for the test-only phase
- open the shared 06-TEST-FINDINGS.md sink with schema and #06-01-1 finding
- rewrite every built-in as EventXxx implements BuiltInFunction<P, E>
(setBlock/moveBlock/deleteBlock/moveHero/moveHeroStep/touchFront/
insertEvents/insertEvent), matching the EventSetBlock reference style
- move shared helpers to event/utils.ts (getPossibleMap, getPossibleLayer,
getEventExecutor, enter/exitEventInsert); utils is not exported via index
- move per-class helpers into private methods (EventTouchFront and
EventInsertEvents collectInvocations)
- use IObjectMover.push for move sequences instead of manual step calls
- registrations.ts: one createEventRegistrations() writing the eight entries
in order, categories separated by single-line comments
- remove EventBuiltinName; classes own short string names
- update event tests; core.ts uses createEventRegistrations
- rename all anon-tokyo imports to @motajs/anon-tokyo in data-common,
data-system and data-state
- declare @motajs/anon-tokyo workspace dependency in data-common and
data-system; refresh pnpm-lock.yaml