718aed38b1
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
- 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
443 lines
17 KiB
Markdown
443 lines
17 KiB
Markdown
# 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)
|