docs(vt): fold the citation-pass corrections into KB 04 (Armor Self row, stance legend swap, cr registered twice, G4 global pool, resolved handler-table and HP-yield bullets, MossTankPanel line sweep)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
887242cd15
commit
2e36ea8268
1 changed files with 42 additions and 43 deletions
|
|
@ -80,10 +80,10 @@ sequence, not a generic "everything self-targeted" scan:
|
|||
| 5 | `Life Magic Mastery Self I` | `eq.cs:147` |
|
||||
| 6 | one self-buff per **trained skill** in `PluginCore.dz.q.x` (skill→spell-name map), only for skill classes 2 or 3 (i.e. an actual skill, not an attribute); unknown skill → warns "Unknown skill detected!" and drops it | `eq.cs:96-110,148-151` |
|
||||
| 7 | `Strength/Endurance/Coordination/Quickness/Regeneration/Rejuvenation/Mana Renewal Self I` | `eq.cs:152-158` |
|
||||
| 8 | `Impenetrability I` (physical bane, i.e. Armor Self's item-enchant sibling) | `eq.cs:159` |
|
||||
| 8 | `Armor Self I` (the physical **prot**, `fk.b`, `fk.cs:424`; `Impenetrability I` is `fk.a` and belongs only to `eq.g()` at `eq.cs:246` — citation pass 2026-09-06) | `eq.cs:159` |
|
||||
| 9 | the elemental **prots** named by `BuffProfile-Prots`/`BuffProfile_Prots` (letter string `ALFCBPS`, or a numeric legacy mode) | `eq.cs:160-165` |
|
||||
| 10 | the user's **extra self buffs** list (`PluginCore.dz.m.i`, populated by the `e7.cs` picker) | `eq.cs:166-169` |
|
||||
| 11 | any **untargeted** profile row (`eq.m_e`, item id -1 entries with a spell whose `isUntargeted` is true) | `eq.cs:170-181` |
|
||||
| 11 | any **untargeted** profile row (`eq.m_e` rows whose spell has `isUntargeted` true — the code filters on `isUntargeted` alone) | `eq.cs:170-181` |
|
||||
|
||||
Then two filters remove entries whose *family* is already covered by
|
||||
something in the **blacklist** (`PluginCore.dz.m.j`, populated by
|
||||
|
|
@ -159,8 +159,8 @@ sets `m_h = true` to signal "this is a consumable", `eq.cs:554`).
|
|||
`Build()` reads skills/attributes/active enchantments **once**, decides
|
||||
every wanted line, picks a tier, and returns the whole ordered queue.
|
||||
`MossTankPanel.StartBuffPass` calls `BuildPlan` (→ `BuffPlan.Build`)
|
||||
exactly once per pass (`acdream:MossTankPanel.cs:3607-3656,3679-3698`);
|
||||
`TryCast` (`acdream:MossTankPanel.cs:4265-4303`) then walks the fixed
|
||||
exactly once per pass (`acdream:MossTankPanel.cs:3614-3656,3687-3705`);
|
||||
`TryCast` (`acdream:MossTankPanel.cs:4317-4354`) then walks the fixed
|
||||
queue index-by-index and never re-invokes `BuildPlan` mid-pass. Retail
|
||||
re-evaluates `eq.a()` **every logic tick** (~293 ms,
|
||||
`uTank2/cLogic.cs:145`) against freshly-read server skill/quality data
|
||||
|
|
@ -188,7 +188,7 @@ Nothing marks completion; the rule simply stops returning `true` from
|
|||
exhausted.
|
||||
|
||||
**acdream** has an explicit pass object (`_queue`/`_queueIndex`,
|
||||
`acdream:MossTankPanel.cs:3607-3656`) with its own progress/stall
|
||||
`acdream:MossTankPanel.cs:3614-3656`) with its own progress/stall
|
||||
tracking (`_castThisPass`, `_sinceProgress`) not present in retail —
|
||||
a deliberate modernization, not a port; see §5.
|
||||
|
||||
|
|
@ -287,7 +287,7 @@ casting.", `uTank2.Resources.defaultsettings.usd:1139-1143`) at
|
|||
`gj.cs:171,233,237` — movement is only attempted for non-War/non-Void
|
||||
schools while an instant-cast spell is in flight.
|
||||
|
||||
**acdream** (`acdream:MossTankPanel.cs:4348-4360`,
|
||||
**acdream** (`acdream:MossTankPanel.cs:4400-4412`,
|
||||
`IsVtankInstantCast`) reproduces this **exactly**: same
|
||||
difficulty<50 branch, same untargeted/non-fellowship/≥60s/school-{31
|
||||
(Creature),33 (Life)} branch (AC skill ids 31/33 = VTank's internal
|
||||
|
|
@ -305,7 +305,7 @@ default enchant rows:
|
|||
|
||||
| `LongValueKey 218103822` value | Auto-added spells (StandardBuffs) | "No buffs" fallback | Cite |
|
||||
|---|---|---|---|
|
||||
| `1048576` or `33554432` | Aura of Defender, Blood Drinker, Swift Killer, Heart Seeker Self I | `eq.c(itemId, -1)` | `PluginCore.cs:8355-8375` |
|
||||
| `1048576` or `33554432` | Aura of Defender Self I, Aura of Blood Drinker Self I, Aura of Swift Killer Self I, Aura of Heart Seeker Self I | `eq.c(itemId, -1)` | `PluginCore.cs:8355-8375` |
|
||||
| `4194304` (excluded if `dz.o.c(itemId) == fi.h`) | Aura of Defender, Blood Drinker, Swift Killer Self I | `eq.c(itemId, -1)` | `PluginCore.cs:8377-8397` |
|
||||
| `16777216` | Aura of Defender, Hermetic Link, Spirit Drinker Self I | `eq.c(itemId, -1)` | `PluginCore.cs:8399-8419` |
|
||||
| `2097152` (shield) | all 7 elemental prots (`eDamageElement 0..6`) + Physical (`Armor Self I`) | `eq.c(itemId, -1)` | `PluginCore.cs:8421-8438` |
|
||||
|
|
@ -377,13 +377,13 @@ spell, but structurally fires early on the *opposite* condition). This
|
|||
is quoted, not paraphrased away, per project convention on decomp
|
||||
predicates; flagged again in §5/§6.
|
||||
|
||||
**acdream** (`acdream:MossTankPanel.cs:3996-4071`,
|
||||
**acdream** (`acdream:MossTankPanel.cs:4047-4120`,
|
||||
`TickRandomHelper`): same distance constant (`< 18d`,
|
||||
`MossTankPanel.cs:4019-4020`, matching retail's 0.075 unit band), same
|
||||
11-name stem list verbatim (`MossTankPanel.cs:4026-4033`), same
|
||||
"resolve to best known tier" (`OrderByDescending(Quality).ThenBy(Tier)`,
|
||||
`MossTankPanel.cs:4044-4046`), same interval lock semantics
|
||||
(`_randomHelperRemaining`, floor 0.25s, `MossTankPanel.cs:4062-4064`).
|
||||
`MossTankPanel.cs:4071`, matching retail's 0.075 unit band), same
|
||||
11-name stem list verbatim (`MossTankPanel.cs:4079-4087`), same
|
||||
"resolve to best known tier" (`OrderByDescending(Quality).ThenByDescending(Tier)`,
|
||||
`MossTankPanel.cs:4094-4096`), same interval lock semantics
|
||||
(`_randomHelperRemaining`, floor 0.25s, `MossTankPanel.cs:4112-4114`).
|
||||
It does **not** reproduce the 100-random-draws/early-`true`-on-miss
|
||||
structure: instead it round-robins **deterministically** through every
|
||||
(player × stem) combination from a rolling cursor and only fires on
|
||||
|
|
@ -483,10 +483,10 @@ diffed line-for-line against `af.cs:117-141` in this pass.
|
|||
|
||||
### 4.1 The trigger (`cr.cs` / `cg.cs`)
|
||||
|
||||
`cr` ("RechargeSelf2") is registered **three times** in `cLogic.cs`
|
||||
`cr` ("RechargeSelf2") is registered **twice** in `cLogic.cs`
|
||||
(main position before `BuffSelf`, using `Recharge-Norm-*`; idle
|
||||
position after target-approach, using `Recharge-NoTarg-*`;
|
||||
`cLogic.cs:467,558`) — helper recharge (`gu`, §3.3) is the analogous
|
||||
`cLogic.cs:470,566`) — helper recharge (`gu`, §3.3) is the analogous
|
||||
rule for `Recharge-Helper-*`. Gate: `ItemUse` lock free, then any of
|
||||
`cg.c(healthSetting) || cg.b(stamSetting) || cg.a(manaSetting)`
|
||||
(`cr.cs:75-91`) — note the deliberate **Health > Stamina > Mana**
|
||||
|
|
@ -528,17 +528,17 @@ Seven registered `IRechargeHandler`s (`cRechargeManager.cs:26-36`):
|
|||
come from the `RechargeHandlerSet` settings TABLE
|
||||
(`uTank2.Resources.defaultsettings.usd`, `RechargeHandlerSet` block),
|
||||
parsed row = `(Vital 1/2/3=HitP/Stam/Mana, HandlerString, MinPercent,
|
||||
MaxPercent, Stance 1=Other/2=MagicMode)`. Default table (26 rows,
|
||||
MaxPercent, Stance 1=MagicMode/2=Other — `uTank2/eRechargeStance.cs`: `MagicMode = 1, Other`)`. Default table (26 rows,
|
||||
decoded from the `.usd` TABLE literal):
|
||||
|
||||
| Vital | Stance | Ordered handlers (name, min–max %) |
|
||||
|---|---|---|
|
||||
| Health | Other | Stamina→Health (0–15), Mana→Health (0–15), Regular Spell (0–15), Recharge With Food (0–15), Kit Recharge (0–100), Stamina→Health (0–100), Mana→Health (0–100), Regular Spell (0–100), Recharge With Food (0–100) |
|
||||
| Stamina | Other | Kit Recharge (0–100), Regular Spell (0–100), Recharge With Food (0–100) |
|
||||
| Mana | Other | Kit Recharge (0–100), Recharge With Food (0–100), Regular Spell (0–100) |
|
||||
| Health | MagicMode | Recharge With Food (0–15), Kit Recharge (0–100), Recharge With Food (0–100), Stamina→Health (0–10), Regular Spell (0–100) |
|
||||
| Stamina | MagicMode | Kit Recharge (0–100), Recharge With Food (0–100), Regular Spell (0–100) |
|
||||
| Health | MagicMode | Stamina→Health (0–15), Mana→Health (0–15), Regular Spell (0–15), Recharge With Food (0–15), Kit Recharge (0–100), Stamina→Health (0–100), Mana→Health (0–100), Regular Spell (0–100), Recharge With Food (0–100) |
|
||||
| Stamina | MagicMode | Kit Recharge (0–100), Regular Spell (0–100), Recharge With Food (0–100) |
|
||||
| Mana | MagicMode | Kit Recharge (0–100), Recharge With Food (0–100), Regular Spell (0–100) |
|
||||
| Health | Other | Recharge With Food (0–15), Kit Recharge (0–100), Recharge With Food (0–100), Stamina→Health (0–10), Regular Spell (0–100) |
|
||||
| Stamina | Other | Kit Recharge (0–100), Recharge With Food (0–100), Regular Spell (0–100) |
|
||||
| Mana | Other | Kit Recharge (0–100), Recharge With Food (0–100), Regular Spell (0–100) |
|
||||
|
||||
Dispatch (`cRechargeManager.Recharge`, `cRechargeManager.cs:47-78`):
|
||||
filter rows to the current stance, then walk in table order; a row is
|
||||
|
|
@ -576,7 +576,7 @@ fields present → `fs.g`.
|
|||
|
||||
**acdream** (`acdream:VitalRecharge.cs`, `VitalRechargeMethod` enum
|
||||
`RegularSpell/StaminaToHealth/ManaToHealth/HealthToStamina/
|
||||
HealthToMana/Kit/Food`, `VitalRecharge.cs:25-34`) mirrors the same six
|
||||
HealthToMana/Kit/Food`, `VitalRecharge.cs:25-34`) mirrors the same seven
|
||||
handler shapes plus the constant `HealingSkill = 21` (matching
|
||||
`CharFilterSkillType 21`, `VitalRecharge.cs:43`); the item
|
||||
classification rules (kit/food name-or-flag matching) were not
|
||||
|
|
@ -623,8 +623,11 @@ once `q.a(item, actions)` reports it no longer needs charge,
|
|||
usable-food flagged item" — the gems/food-panel mana charges),
|
||||
partitioned by whether they're stacked (`item2.g()`) — rebuilt whenever
|
||||
the profile changes (`dy.b()` private, `dy.cs:50-73`) by scanning
|
||||
`PluginCore.dz.q.p[worn-item-name]`, i.e. **the charge candidates are
|
||||
keyed to the specific worn item's own name**, not a global pool.
|
||||
`PluginCore.dz.q.p[name]` for every name that `dz.m.h` (item-name →
|
||||
classification, `da.cs:29`) classifies `fs.i` (`dz.q.p` is name → inventory
|
||||
instances, `g6.cs:58`), i.e. **a global pool of charge consumables keyed by
|
||||
the consumable's own name** — NOT scoped per worn item (corrected by the
|
||||
citation pass 2026-09-06).
|
||||
`h()` = "queue non-empty AND a matching charge item exists"
|
||||
(`dy.cs:309-315`); `i()` = `ApplyItem(chargeItemId, dy.c[0])` — a
|
||||
direct item-on-item apply, **oldest-queued worn item first**, not
|
||||
|
|
@ -634,15 +637,11 @@ sorted by current charge percent (`dy.cs:318-322`).
|
|||
Plan` (`ItemManaRecharge.cs:17-54`) instead (a) matches charge
|
||||
consumables by a **global** `ItemType & ManaStoneItemType` flag plus a
|
||||
configured name allowlist (`consumableNames`, not auto-derived from
|
||||
`fs.i`'s per-worn-item keying), and (b) picks the **lowest current
|
||||
`fs.i`'s classification), and (b) picks the **lowest current
|
||||
mana-percent** equipped item under the configured threshold, not the
|
||||
oldest-queued one. Flagged as gap G4 in §5: retail's charge-item
|
||||
pool is scoped per *specific worn item name* (`q.p[name]`), so a charge
|
||||
consumable that only works on item A is never offered for item B;
|
||||
acdream's flat name-allowlist + generic item-type flag does not encode
|
||||
that per-item scoping, which could mean either mismatching a charge to
|
||||
an incompatible worn item or failing to find a match retail would have
|
||||
found via the correct per-name bucket.
|
||||
oldest-queued one. Flagged as gap G4 in §5: since retail's pool is
|
||||
global too, the surviving difference is selection order only (retail
|
||||
applies to `c[0]`, the oldest-queued worn item; acdream the lowest-%).
|
||||
|
||||
### 4.6 Dispel self / dispel items
|
||||
|
||||
|
|
@ -681,7 +680,7 @@ re-verified item-for-item against `cx.cs:74-114` in this pass.
|
|||
| **G1** | `BuffPlan.Build` snapshots skills/attributes/active-enchantments **once** per pass and queues fixed spell picks; retail's `eq.a()` re-reads skill/quality **every logic tick** (~293 ms) and re-picks fresh, so a mid-pass skill raise (e.g. casting a mastery buff) immediately unlocks a higher tier for the *next* candidate in the *same* pass. acdream cannot realize that cascade — it locks in tier choices from before the pass started. | `eq.cs:477-527` (fresh per call), `uTank2/cLogic.cs:145` (293 ms tick) | `BuffPlan.cs:104-213` (one `Build` call), `MossTankPanel.cs:3607-3698` (`BuildPlan` called once at pass start, never mid-pass) | **High** for any character whose unbuffed skill sits close to a tier's difficulty threshold — retail casts a stronger buff line than acdream will, specifically for spells downstream of the Creature-Enchantment-Mastery→Focus/Willpower→Item-Enchantment/Life-Magic dependency chain the acdream code's own comment (`BuffPlan.cs:305-336`) describes but the implementation doesn't dynamically realize. |
|
||||
| **G2** | Retail auto-populates default enchant-spell candidates (auras / prots / banes) for a wand/shield/pet the **moment it's added to the profile**, keyed off the item's `LongValueKey 218103822` mastery mask (§2). No equivalent "profile item → default spell rows" pipeline was found in `BuffPlan.cs`/`BuffProfile.cs`; `BuffProfile.Build` only classifies the character's **own spellbook**. | `PluginCore.cs:8327-8445` | not located in the six named files | **High** if genuinely absent — this is how a fresh VTank user gets working weapon/shield enchants with zero configuration; if it lives elsewhere in the plugin it should be cited, otherwise it's a real feature gap for item-enchantment buffing specifically (as opposed to self-buffing, which G1 covers). |
|
||||
| **G3** | Retail's helper-heal target pool includes **non-fellowship tracked players** (`ai.m`, populated from general server/client vitals echo dispatch, independent 5s-per-vital re-cast cooldown, 2-minute staleness) in addition to fellowship members. acdream's `TryPlanHelper` gates entirely on `settings.HelpOthers && automation.Fellowship.IsInFellowship` and was not seen to consult a non-fellowship tracked pool. | `ai.cs:246-375` (two pools, `l` fellowship + `m` non-fellowship) | `VitalRecharge.cs:94-140` (`TryPlanHelper`, fellowship-gated only) | **Medium** — narrows "help nearby strangers" to "help fellowship only"; likely intentional scoping (`DoHelp`'s own retail description even says "The fellowship window must be open to help fellows", `uTank2.Resources.defaultsettings.usd:499-503`, suggesting the non-fellow pool may be a secondary/legacy path) but worth a deliberate decision rather than an unnoticed drop. |
|
||||
| **G4** | Worn-item mana-charge matching: retail keys charge candidates to the **specific worn item's own name** (`q.p[itemName]`) and drains the **oldest-queued** worn item first; acdream uses a **global** consumable-name allowlist + generic `ManaStoneItemType` flag and picks the **lowest-mana-percent** item first. | `dy.cs:50-73,309-322` | `ItemManaRecharge.cs:17-54` | **Medium** — could either apply an incompatible charge to a worn item that doesn't accept it, or simply pick a different (lower-%) target than retail would on the same tick; low severity in practice since AC mana-charge items are broadly compatible by charge-type, but the per-item keying is a real mechanism difference. |
|
||||
| **G4** | Worn-item mana-charge ordering: both retail and acdream draw charge consumables from a **global** pool (retail: every `fs.i`-classified name via `dz.m.h`/`dz.q.p`; acdream: `ManaStoneItemType` + name allowlist); retail drains the **oldest-queued** worn item first (`c[0]`), acdream the **lowest-mana-percent** one. | `dy.cs:50-73,309-322` | `ItemManaRecharge.cs:17-54` | **Low** — a different target on the same tick, never an incompatible apply (the per-worn-item keying claimed in the first draft was wrong; citation pass 2026-09-06). |
|
||||
| **G5** | `ba.cs`'s `RandomHelper` fires `ValidNow = true` on the *first* of up to 100 random draws that resolves to an **uncastable** spell (doing nothing that tick), and only returns `false` if all 100 draws happen to be castable (§3.1). acdream instead deterministically round-robins every (player × stem) pair and only fires on an actually-castable one. | `ba.cs:116-125,152` | `MossTankPanel.cs:4034-4068` | **Low/positive** — acdream's version helps allies strictly more often and more predictably than retail's likely-buggy sampling loop; flagged as a behavioral divergence worth a conscious call (replicate the retail quirk vs. keep the fix) rather than an oversight, since the retail behavior reads as unintended. |
|
||||
|
||||
## 6. Could not determine
|
||||
|
|
@ -697,16 +696,16 @@ re-verified item-for-item against `cx.cs:74-114` in this pass.
|
|||
object rather than a "real" tradeable AC item, or may key off a
|
||||
specific quest/consumable whose purpose isn't documented in this
|
||||
build's strings).
|
||||
- **Exact retail `RechargeHandlerSet` table values vs. acdream's
|
||||
ported default table** — the 26-row table was fully decoded from
|
||||
`uTank2.Resources.defaultsettings.usd` (§4.2), but acdream's own
|
||||
default table in `VitalRecharge.cs` was not diffed cell-by-cell
|
||||
against it in this pass.
|
||||
- **`he.a`'s HP-yield sizing table vs. any acdream equivalent** — no
|
||||
matching sizing/estimate function was located in
|
||||
`VitalRecharge.cs` during this pass; may exist under a name not
|
||||
grepped, or may be a genuine gap (vital-transfer handlers casting
|
||||
without an HP-net-gain pre-check).
|
||||
- RESOLVED (citation pass 2026-09-06): acdream's default handler table
|
||||
(`VitalRechargePlanner.Handlers`, `VitalRecharge.cs:208-288`, selected by
|
||||
`magicMode: mode == PluginCombatMode.Magic`) reproduces the 26 retail rows
|
||||
cell-for-cell — the `magicMode` arm is stance 1 (MagicMode), the `else` arm
|
||||
stance 2 (Other). And `he.a`'s HP-yield sizing has an exact equivalent:
|
||||
`VitalRechargePlanner.EstimatedTransfer` (`VitalRecharge.cs:725-746`;
|
||||
I 0.9/cap 50 … VI 1.5/uncapped, else 1.75), the Mana −30 floor at
|
||||
`VitalRecharge.cs:699-701`, `EstimatedOrdinaryHeal` (`:712-723`,
|
||||
17/25/32/45/67/87/115/135, default 10) and the net-gain pre-check at
|
||||
`:708-709`.
|
||||
- **`af.cs`'s vuln-severity scoring vs. acdream's `DispelController`
|
||||
ally-dispel target selection**, and **`fb.cs`'s exact two-skill/
|
||||
two-item-name gates vs. `TryHealersHeart`** — both confirmed present
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue