Files
Mana-Loop/docs/specs/attunements/enchanter/systems/transference-channel-spec.md
T
n8n-gitea 718aed38b1
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
feat: implement Transference Channel system for Enchanter attunement
- Add isChanneling, channelSpeedMultiplier, channelDrainRate to CombatState
- Add startChanneling/stopChanneling actions to combat store
- Add transference-channeling discipline with 3 perks (channel-efficiency, channel-power, channel-mastery)
- Add channelIntensity and channelEfficiency to KNOWN_BONUS_STATS
- Create combat-channel.ts with drain + speed multiplier computation
- Apply channel speed multiplier to equipment spells and melee attacks
- Add Channel Transference hold-button UI to SpireCombatPage
- Add compact channel status indicator to SpireCombatControls
- Channel state resets on spire exit, persists across room transitions
- All 1235 existing tests pass
2026-06-14 21:56:20 +02:00

443 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Transference Channel — Design Spec
> Describes the Transference Channel system: an active combat mechanic for the
> Enchanter attunement that lets the player hold a button to drain Transference
> mana and boost the cast speed of all equipment spells and melee attacks.
---
## 1. Objective
The Enchanter attunement currently lacks an active combat mechanic. The Invocation
system (Invoker attunement) provides automatic combat acceleration through
guardian channeling. The Transference Channel gives the Enchanter a **manual,
hold-to-channel button** that spends Transference mana to temporarily accelerate
all equipment-based combat actions.
**Design goals:**
- Give the Enchanter an **active, holdable** combat button
- Spend **Transference mana** — the only utility mana type — as the fuel
- Boost **equipment spells and melee attacks only** — not the player's active spell
- Create a **resource management decision**: channel now for burst speed, or
conserve Transference for enchanting and disciplines
- Scale through a new Enchanter discipline that trades mana cost for potency
- Complement (not compete with) Invocation — different resource, different
trigger, different target
---
## 2. The Channel Button
### 2.1 UI Placement
A **"Channel Transference"** button on the `SpireCombatPage`, visible only when:
- The Enchanter attunement is active (`attunements.enchanter.active === true`)
- The player is in `climb` action (combat)
- The player has at least 1 Transference mana
### 2.2 Interaction Model
| Property | Value |
|---|---|
| **Interaction** | Press and hold (mousedown / touchstart) to channel, release to stop |
| **Keyboard** | Optional hotkey (e.g., `C`) for accessibility |
| **Visual** | Button glows while active; shows Transference drain rate |
| **Cooldown** | None — player controls start/stop |
The hold-to-channel interaction is intentional: it creates a "power moment"
where the player chooses to actively engage. The game is fully playable without
it — this is an optional acceleration for players who want active involvement.
### 2.3 Hold Behavior
While the button is held:
1. Transference mana drains at `drainRate` per tick (see §3)
2. All equipment spells and melee attacks gain `speedMultiplier` cast speed
3. If Transference reaches 0: channel stops automatically (no penalty)
4. If player leaves `climb` action: channel stops automatically
5. If player releases button: channel stops immediately
---
## 3. Drain Rate and Speed Multiplier
### 3.1 Mana Economy Context
Transference mana is generated slowly through the Enchanter's automatic
conversion (0.2/hour at level 1, scaling with `1.5^(level-1)`). At level 10,
this is ~7.69/hour. Transference is also the fuel for all Enchanter disciplines
and the enchanting pipeline.
The Channel system is designed as a **burst expenditure** — the player
accumulates Transference over time and then spends it in combat for temporary
acceleration. The drain rate must be high enough to create meaningful decisions
("can I afford to channel right now?") but not so high that it's trivially
exhausted.
### 3.2 Base Values
| Parameter | Base Value | Description |
|---|---|---|
| `baseDrainRate` | 0.08 Transference/tick | 0.4 Transference/second at 200ms/tick |
| `baseSpeedMultiplier` | 1.5× | Equipment spells and melee attack 50% faster |
### 3.3 Duration Estimate
At base values with various Transference pool sizes:
| Transference Pool | Drain Rate | Approximate Duration |
|---|---|---|
| 20 | 0.08/tick | 250 ticks = 50 seconds |
| 50 | 0.08/tick | 625 ticks = 125 seconds |
| 100 | 0.08/tick | 1250 ticks = 250 seconds |
With discipline scaling (e.g., 1.8× speed at ~1.8× drain):
| Transference Pool | Drain Rate | Approximate Duration |
|---|---|---|
| 50 | 0.144/tick | 347 ticks = 69 seconds |
| 100 | 0.144/tick | 694 ticks = 139 seconds |
The discipline makes each "pool of Transference" worth less raw seconds but
more effective combat time because actions complete faster.
### 3.4 Scaling with Discipline
A new discipline (`transference-channeling`) controls the tradeoff between
mana cost and speed:
```
effectiveDrainRate = baseDrainRate × (1 + intensityBonus) × (1 - channelEfficiency)
effectiveSpeedMultiplier = baseSpeedMultiplier + speedBonus
```
Where `intensityBonus` and `speedBonus` come from the discipline's stat bonus
and perks (see §5).
**Design philosophy:** The discipline lets the player invest XP to get more
speed, but at proportionally higher mana cost. Perks can shift the ratio
in the player's favor (more speed per mana).
---
## 4. What Gets Boosted
### 4.1 Affected
| Action | Boosted? | Notes |
|---|---|---|
| **Equipment spell casts** | ✅ Yes | All `equipmentSpellStates` cast progress |
| **Melee sword attacks** | ✅ Yes | All `meleeSwordProgress` accumulators |
### 4.2 Not Affected
| Action | Boosted? | Notes |
|---|---|---|
| **Player's active spell** | ❌ No | The manually-selected spell is not equipment |
| **Invocation spells** | ❌ No | Invocation is pact-based, not equipment-based |
| **Golem attacks** | ❌ No | Golems are independent entities |
| **DoT ticks** | ❌ No | DoTs are time-based, not cast-based |
### 4.3 Implementation — Speed Multiplier Application
The channel state is tracked in the combat store. When active, the multiplier
is applied to `progressPerTick` calculations for equipment spells and melee:
```
// Equipment spells (in combat-actions.ts equipment spell block)
const channelMult = state.isChanneling ? state.channelSpeedMultiplier : 1.0;
const eProgressPerTick = HOURS_PER_TICK * eSpellCastSpeed * totalAttackSpeed * channelMult;
// Melee (in combat-melee.ts)
const channelMult = state.isChanneling ? state.channelSpeedMultiplier : 1.0;
const meleeProgressPerTick = HOURS_PER_TICK * swordAttackSpeed * attackSpeedMult * channelMult;
```
**Important:** The speed multiplier only affects cast *progress accumulation*.
It does NOT affect mana costs per cast. Equipment spells still cost the same
mana per cast — they just complete faster. This prevents the feedback loop
where faster casting drains mana exponentially faster.
---
## 5. New Discipline: Transference Channeling
### 5.1 Definition
| Field | Value |
|---|---|
| **ID** | `transference-channeling` |
| **Name** | Transference Channeling |
| **Attunement** | `enchanter` |
| **Mana Type** | `transference` |
| **Base Cost** | 15 |
| **Stat Bonus** | `channelIntensity` +0.10 (base) |
| **Scaling Factor** | 100 |
| **Difficulty Factor** | 180 |
| **Drain Base** | 4 |
**Main stat: `channelIntensity`**
This stat controls both the speed boost and the drain rate:
```
speedBonus = channelIntensity × 0.5 // added to baseSpeedMultiplier
intensityBonus = channelIntensity × 1.0 // multiplier on drain rate
```
At 0 XP: `channelIntensity = 0.10` → speed = 1.5 + 0.05 = 1.55×, drain = 0.08 × 1.10 = 0.088/tick.
The stat scales with XP via the standard discipline math:
```
StatBonus = baseValue × (XP / scalingFactor)^0.65
= 0.10 × (XP / 100)^0.65
```
### 5.2 Perks
| Perk ID | Type | Threshold | Bonus | Description |
|---|---|---|---|---|
| `channel-efficiency` | `once` | 100 | `channelEfficiency` +0.15 | 15% less drain for same speed — shifts the ratio in the player's favor |
| `channel-power` | `infinite` | 200 | Every 150 XP: `channelIntensity` +0.05 | Core scaling — more speed (and proportionally more drain) |
| `channel-mastery` | `capped` | 400 | Every 200 XP: `channelEfficiency` +0.10, max 3 tiers | Late-game efficiency — up to 45% less drain |
### 5.3 Effective Formulas with Perks
```
channelEfficiency = 0 + sum of efficiency perks (0.15 from once, up to 0.30 from capped)
hard-capped at 0.60 — prevents drain rate from reaching 0
effectiveSpeedMultiplier = 1.5 + (channelIntensity × 0.5)
effectiveDrainRate = 0.08 × (1 + channelIntensity × 1.0) × (1 - channelEfficiency)
```
> **The `channelEfficiency` cap of 0.60** is enforced in the formula itself. Even if
> perk bonuses would exceed 0.60, the effective drain rate can never go below
> `0.08 × (1 + intensity) × 0.40`. This ensures channeling always costs meaningful
> Transference mana.
**Example at 500 XP with all perks:**
- `channelIntensity` = 0.10 × (500/100)^0.65 + 0.05 × 2 (two infinite intervals) ≈ 0.24 + 0.10 = 0.34
- `channelEfficiency` = 0.15 + 0.20 (two capped tiers) = 0.35
- Speed = 1.5 + 0.34 × 0.5 = 1.67×
- Drain = 0.08 × (1 + 0.34) × (1 - 0.35) = 0.08 × 1.34 × 0.65 = 0.070/tick
### 5.4 Discipline Identity
*"I channel transference mana through my equipment, making my enchanted gear
strike and cast faster. The more I master this, the faster I go — but it
costs more mana to sustain."*
---
## 6. Store Changes
### 6.1 Combat Store (`combatStore.ts`)
New state fields:
```typescript
// Transference Channel state
isChanneling: boolean; // true while button is held
channelSpeedMultiplier: number; // current speed multiplier (1.5+)
channelDrainRate: number; // current drain rate per tick
```
New actions:
```typescript
startChanneling: () => void;
stopChanneling: () => void;
```
### 6.2 Combat State Types (`combat-state.types.ts`)
Add to `CombatState`:
```typescript
isChanneling: boolean;
channelSpeedMultiplier: number;
channelDrainRate: number;
```
Add to `CombatActions`:
```typescript
startChanneling: () => void;
stopChanneling: () => void;
```
### 6.3 No Changes To
- `manaStore.ts` — Transference mana is drained via existing element deduction
- `craftingStore.ts` — enchanting is unaffected
- `prestigeStore.ts` — no prestige interaction
- `invocation-utils.ts` — Invocation is completely separate
---
## 7. Combat Tick Integration
### 7.1 Modified Flow in `combat-actions.ts`
The channel state is read from the combat store at the start of each tick.
The speed multiplier is applied to equipment spell and melee progress:
```
1. Read isChanneling from combat store
2. If isChanneling:
a. Drain Transference: transferencePool -= channelDrainRate
b. If transference <= 0: stop channeling (set isChanneling = false)
3. Compute channelMult = isChanneling ? channelSpeedMultiplier : 1.0
4. Equipment spell progress: eProgressPerTick *= channelMult
5. Melee progress: meleeProgressPerTick *= channelMult
```
> **File size note:** `combat-actions.ts` is currently 377 lines. Adding channel drain
> logic and speed multiplier application may push it toward the 400-line limit. If so,
> extract the channel logic into a new file (e.g., `combat-channel.ts`) and call it
> from `processCombatTick`.
### 7.2 Transference Drain
Transference is drained from the mana store. The drain uses the existing
element deduction pattern:
```typescript
// Per tick while channeling:
const transferencePool = useManaStore.getState().elements.transference;
if (transferencePool.current >= channelDrainRate) {
useManaStore.getState().deductElement('transference', channelDrainRate);
} else {
// Insufficient mana — stop channeling
useCombatStore.getState().stopChanneling();
}
```
### 7.3 Auto-Stop Conditions
Channeling stops when any of the following is true:
1. Player releases the button
2. Transference mana reaches 0
3. Player leaves `climb` action
4. Enchanter attunement is deactivated mid-combat
> **Room transitions:** Channeling **persists** across room transitions within the same
> climb. If the player is channeling and kills an enemy (triggering
> `advanceRoomOrFloor`), channeling continues into the next room. The `isChanneling`
> state is stored on the combat store and is not reset by room transitions — only by
> the auto-stop conditions above or by spire exit.
---
## 8. UI Changes
### 8.1 SpireCombatPage
Add a **"Channel Transference"** button between the SpireHeader and RoomDisplay:
- **Visible when:** Enchanter active + in `climb` action
- **Button style:** Teal-colored (#1ABC9C, matching Enchanter), with Transference icon (🔗)
- **While channeling:** Button glows, shows drain rate per second
- **Transference bar:** Small bar below button showing remaining Transference mana
- **Tooltip:** "Hold to channel transference mana through your equipment, boosting attack speed"
### 8.2 SpireCombatControls
Add a compact channel status indicator:
- Shows "⚡ Channeling" with speed multiplier when active
- Hidden when not channeling
### 8.3 No New Tabs
All channel information is visible in the existing SpireCombatPage layout.
---
## 9. Data Flow Summary
```
Player holds button
→ startChanneling() [combatStore]
→ isChanneling = true, channelSpeedMultiplier = 1.5+, channelDrainRate = 0.08+
gameStore.tick()
→ buildTickContext() [snapshots all stores]
→ processCombatTick() [combat-actions.ts]
→ If isChanneling:
- Deduct transference mana from manaStore
- If transference <= 0: stopChanneling()
→ channelMult = isChanneling ? channelSpeedMultiplier : 1.0
→ Equipment spell progress: eProgressPerTick *= channelMult
→ Melee progress: meleeProgressPerTick *= channelMult
→ applyTickWrites() [writes combat store changes back]
Player releases button
→ stopChanneling() [combatStore]
→ isChanneling = false
```
---
## 10. Acceptance Criteria
| # | Criterion |
|---|---|
| AC-1 | "Channel Transference" button is visible on SpireCombatPage when Enchanter is active and player is in `climb` action. |
| AC-2 | Button is hidden when Enchanter is inactive, player is not in `climb`, or Transference pool is 0. |
| AC-3 | Holding the button (mousedown) activates channeling; releasing deactivates it. |
| AC-4 | While channeling, Transference mana drains at `channelDrainRate` per tick. |
| AC-5 | When Transference reaches 0, channeling stops automatically with no penalty. |
| AC-6 | While channeling, all equipment spell cast progress is multiplied by `channelSpeedMultiplier`. |
| AC-7 | While channeling, all melee sword attack progress is multiplied by `channelSpeedMultiplier`. |
| AC-8 | The player's active spell is NOT affected by channeling. |
| AC-9 | Invocation spells are NOT affected by channeling. |
| AC-10 | Golem attacks are NOT affected by channeling. |
| AC-11 | DoT ticks are NOT affected by channeling. |
| AC-12 | Base drain rate is 0.08 Transference/tick; base speed multiplier is 1.5×. |
| AC-13 | `transference-channeling` discipline scales `channelIntensity` stat with XP. |
| AC-14 | `channel-efficiency` once perk (100 XP) grants 15% drain reduction. |
| AC-15 | `channel-power` infinite perk (200 XP, every 150 XP) grants +0.05 `channelIntensity`. |
| AC-16 | `channel-mastery` capped perk (400 XP, every 200 XP, max 3 tiers) grants +0.10 `channelEfficiency` per tier. |
| AC-17 | `channelEfficiency` is capped at 0.60 (prevents drain from reaching 0). |
| AC-18 | Channel state resets on spire exit (`isChanneling = false`). |
| AC-19 | Existing saves without channel fields get default values (`isChanneling = false`, `channelSpeedMultiplier = 1.5`, `channelDrainRate = 0.08`). |
| AC-20 | Channel stops automatically when player leaves `climb` action. |
| AC-21 | Speed multiplier affects cast progress accumulation only — mana costs per cast are unchanged. |
---
## 11. Files Reference
| File | Role |
|---|---|
| `src/lib/game/stores/combatStore.ts` | New channel state fields + `startChanneling`/`stopChanneling` actions |
| `src/lib/game/stores/combat-state.types.ts` | Type definitions for new state |
| `src/lib/game/stores/combat-actions.ts` | Channel drain + speed multiplier application for equipment spells (or extracted to `combat-channel.ts` if file size exceeds 400 lines) |
| `src/lib/game/stores/combat-melee.ts` | Speed multiplier application for melee attacks |
| `src/lib/game/data/disciplines/enchanter.ts` | Add `transference-channeling` discipline definition |
> **Implementation note:** `enchanter.ts` currently has 4 disciplines (146 lines). If
> adding `transference-channeling` would push it toward the 400-line limit (e.g. when
> combined with the `room-enchanting` discipline from the room enchantments spec),
> create a new file `enchanter-combat.ts` (following the existing pattern of
> `enchanter-utility.ts`, `enchanter-spells.ts`, etc.) and re-export from
> `data/disciplines/index.ts`.
| `src/lib/game/effects/discipline-effects.ts` | Add `channelIntensity` and `channelEfficiency` to known bonus stats |
| `src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx` | Channel button UI |
| `src/components/game/tabs/SpireCombatPage/SpireCombatControls.tsx` | Compact channel status indicator |
| `docs/specs/attunements/enchanter/systems/transference-channel-spec.md` | **THIS FILE** |
---
## 12. Out of Scope
- Channel affecting the player's active spell
- Channel affecting Invocation spells
- Channel affecting golem attacks
- Channel affecting DoT ticks
- Auto-channel toggle (manual hold only in v1)
- Channel working outside of combat (`climb` action only)
- Prestige upgrades that affect channeling
- Channel interacting with the enchanting pipeline (Design/Prepare/Apply)