10 KiB
Blood Energy Economy
Status: In Design Author: SepComet + Claude Last Updated: 2026-04-27 Implements Pillar: 操作优于数值 (Skill Over Stats), 切换即承诺 (Switch is Commitment)
Overview
Blood Energy Economy 是夜裔的核心资源系统——管理玩家用于切换形态的"血能"资源。血能通过攻击敌人和承受伤害两种方式获取,仅用于支付形态切换的消耗。它不是"攒大招"的能量槽,而是一个强制性的战术节奏控制器:你必须在前线积极战斗来赚取血能,然后在正确的时机消费它来切换形态。没有血能系统,形态切换退化为"CD好了就切",失去"切换即承诺"的战术重量。
Player Fantasy
此系统是纯基础设施——玩家感知的是它赋能的操作(切形态),而非血能数字本身。不展开独立幻想章节。
Detailed Design
Core Rules
1. Blood Energy Gain
Blood energy is earned through aggressive play:
- Attack Hit:
baseAttackGain × enemyTypeMultiplierper hit - Enemy Kill:
baseKillGain × enemyTypeMultiplierper kill - Taking Damage:
baseHurtGain × (damageTaken / maxHealth)— compensation ensures the player is never fully locked out of switching - Wave Clear:
fixedBonus × waveNumber— reward for completing a wave
2. Blood Energy Spending
Blood energy has exactly one use: form switching.
- Switch Cost:
baseSwitchCost × (1.0 - skillDiscount) - Interrupted Switch: Blood energy is consumed on switch request (at Windup start). If the switch is interrupted, the energy is NOT refunded. This is the core punishment for switching at the wrong time.
3. Constraints
| Rule | Value |
|---|---|
| Valid range | [0.0, maxEnergy] |
| Initial value (game start) | 0 |
| On death | Reset to 0 |
| Maximum value | baseMax × (1.0 + upgradeBonus) |
| Cannot exceed max | Add() clamps to max |
| Cannot go below 0 | Spend() returns false if insufficient |
States and Transitions
Blood Energy Economy has no complex states — it is a single float value with three meaningful thresholds:
| Threshold | Event | Significance |
|---|---|---|
Current == 0 |
OnEnergyEmpty |
Player is locked out of switching — must attack or take damage |
Current >= switchCost |
(check via CanSpend()) |
Switching is possible |
Current == Max |
OnEnergyFull |
Further gains are wasted — signals "you should switch now" |
Every value change fires OnEnergyChanged(current, max) for HUD/L1 consumption.
Interactions with Other Systems
| System | Direction | Interface |
|---|---|---|
| Form Switch SM | Consumes | Calls CanSpend(cost) before switching, Spend(cost) on switch start |
| HUD (L2) | Reads | Subscribes to OnEnergyChanged for display |
| Skill Tree | Modifies | Can set GainRate, baseSwitchCost, baseMax, skillDiscount |
| Death/Respawn | Resets | Calls Reset() on player death |
Formulas
Attack Gain
energyGain_attack = baseAttackGain × enemyTypeMultiplier
| Variable | Symbol | Type | Range | Description |
|---|---|---|---|---|
| Base attack gain | baseAttackGain |
float | 2–8 | Energy gained per hit |
| Enemy type multiplier | enemyTypeMultiplier |
float | 0.5–2.0 | Riskier enemies reward more |
Output: 1–16 per hit | Extreme: Swarm enemies low (0.5x), Brute/Stalker high (1.5–2.0x)
Kill Gain
energyGain_kill = baseKillGain × enemyTypeMultiplier
| Variable | Symbol | Type | Range | Description |
|---|---|---|---|---|
| Base kill gain | baseKillGain |
float | 10–25 | Kill reward significantly higher than hit |
| Enemy type multiplier | enemyTypeMultiplier |
float | 0.5–2.0 | Same as attack formula |
Output: 5–50 | Example: Brute kill (2.0x) = 25 × 2.0 = 50 energy
Hurt Gain
energyGain_hurt = baseHurtGain × (damageTaken / maxHealth)
| Variable | Symbol | Type | Range | Description |
|---|---|---|---|---|
| Base hurt gain | baseHurtGain |
float | 8–20 | Compensation to prevent deadlock |
| Damage ratio | damageTaken / maxHealth |
float | 0.0–1.0 | Bigger hits give more energy |
Intent: Ensures pressured players can earn a switch-out resource.
Switch Cost
switchCost = baseSwitchCost × (1.0 - skillDiscount)
| Variable | Symbol | Type | Range | Description |
|---|---|---|---|---|
| Base switch cost | baseSwitchCost |
float | 25–60 | ~5-12 attack hits to earn one switch (at default ratio ≈8) |
| Skill discount | skillDiscount |
float | 0.0–0.4 | Skill Tree cost reduction |
Output: 18–50 | Example: baseSwitchCost=40, no discount → cost=40
Wave Clear Bonus
waveClearBonus = min(fixedBonus × waveNumber, 2 × fixedBonus)
| Variable | Symbol | Type | Range | Description |
|---|---|---|---|---|
| Fixed bonus | fixedBonus |
float | 20–40 | Base reward per cleared wave |
| Wave number | waveNumber |
int | 1–N | Caps at 2× fixedBonus to prevent late-wave overflow |
Output: 20–80 | Cap ensures wave bonuses don't exceed ~80% of maxEnergy in one reward
Max Energy
maxEnergy = baseMax × (1.0 + upgradeBonus)
| Variable | Symbol | Type | Range | Description |
|---|---|---|---|---|
| Base max | baseMax |
float | 80–150 | Initial energy cap (default 100) |
| Upgrade bonus | upgradeBonus |
float | 0.0–0.5 | Skill Tree capacity growth |
Output: 100–150
Edge Cases
| # | Condition | Resolution | Rationale |
|---|---|---|---|
| 1 | Add() would exceed MaxEnergy |
Clamp to Max, fire OnEnergyFull |
Overflow waste — incentivizes spending before full |
| 2 | Spend() amount exceeds current |
Return false, no side effects, no events |
Silent failure — UI should prevent this via CanSpend |
| 3 | Add() called while dead |
Ignore, return immediately | Dead players don't accumulate resources |
| 4 | Switch interrupted (Interrupt()) |
Energy NOT refunded — deducted at Windup start | Core punishment of "Switch is Commitment" |
| 5 | skillDiscount pushes cost near 0 |
Enforce minimum cost of 1.0 |
Switching always has a cost |
| 6 | Simultaneous Gain and Spend in same frame | Process Gains first, then Spends | Kill reward can enable immediate switch |
| 7 | Wave bonus causes overflow | Clamp to Max (same as #1) | Incentivize spending before wave end |
| 8 | skillTree undo reduces maxEnergy below current |
Force current = min(current, newMax) |
Cannot hold more than new cap |
Dependencies
| System | Relationship | Data Flow |
|---|---|---|
| Form Switch SM | Downstream consumer | Calls CanSpend() / Spend() |
| HUD (L2) | Downstream display | Subscribes to OnEnergyChanged |
| Skill Tree | Upstream modifier | Sets GainRate, baseMax, skillDiscount |
| Death/Respawn | Upstream reset | Calls Reset() |
No upstream dependencies — this is a Foundation layer system.
Tuning Knobs
| Parameter | Default | Safe Range | Too Low | Too High |
|---|---|---|---|---|
baseAttackGain |
5 | 2–8 | Switching too slow, stuck in one form | Switching too frequent, no tactical weight |
baseKillGain |
20 | 10–30 | Kills not worth prioritizing | Kill-chasing replaces form strategy |
baseHurtGain |
12 | 8–20 | Being pressured = no comeback | Deliberate self-damage becomes optimal |
baseSwitchCost |
40 | 25–60 | Switching has almost no threshold | Gap too long, player afraid to switch |
baseMax |
100 | 80–150 | Energy always capped → waste anxiety | Decision window too large → no urgency |
fixedBonus |
30 | 20–40 | Wave bonus imperceptible | Between-wave switching too lenient |
Key tuning pair: baseSwitchCost / baseAttackGain ≈ 8 — roughly 8 attack hits to earn one switch. This ratio defines the core combat rhythm.
Visual/Audio Requirements
This system has no direct visual output. Its presentation is via:
- HUD (L2): Blood energy bar with fill animation, drain animation on switch, color flash on value change
- VFX Spawner (L1): Brief player glow on
OnEnergyFull— visual cue "switch now" - Audio Player (L1): Warning sound on
OnEnergyEmpty— audio cue "cannot switch"
UI Requirements
| Element | Position | Content |
|---|---|---|
| Blood Energy Bar | HUD bottom/side | Amber Gold gradient, fill ratio current / max |
| Form Switch Buttons | HUD corner | One per form: grey = insufficient energy, bright = can switch, highlighted = current |
| Full Energy Indicator | Next to bar | Pulse effect when OnEnergyFull fires |
Acceptance Criteria
| # | GIVEN | WHEN | THEN |
|---|---|---|---|
| 1 | Blood energy is 0 | Player attacks and hits an enemy | Energy increases by baseAttackGain × enemyTypeMultiplier |
| 2 | Blood energy is 50, cost is 40 | Player requests a form switch | CanSpend(40) returns true, Spend(40) succeeds, energy = 10 |
| 3 | Blood energy is 10, cost is 40 | Player requests a form switch | CanSpend(40) returns false, Spend(40) returns false, energy stays 10 |
| 4 | Blood energy is 95, gain is 10 | Player gains energy | Energy becomes 100 (clamped), OnEnergyFull fires |
| 5 | Energy was 60, 40 deducted on switch | Player is hit during Windup | Switch interrupted, energy stays at 20 (deducted cost not refunded, remainder preserved) |
| 6 | Player dies | Reset() called |
Energy becomes 0 |
| 7 | Energy reaches 0 | Any cause | OnEnergyEmpty fires |
| 8 | skillDiscount=0.4, baseSwitchCost=40 |
Player switches | Cost = 24, energy decreased by 24 |
| 9 | Energy changes (any cause) | Per frame or event | OnEnergyChanged(current, max) fires with correct values |
| 10 | baseMax changed from 100 to 50, current is 80 |
Skill tree update | Current clamps to 50 |
| 11 | skillDiscount set to 1.0, baseSwitchCost = 40 |
Player requests switch | Cost = max(40 × 0.0, 1.0) = 1.0 (minimum cost enforced) |
| 12 | Player is dead, Add(10) called |
Energy gain event | Gain ignored, energy unchanged |
Open Questions
- Final
baseSwitchCostvalue (recommended 40) needs prototype validation — actual game feel will determine the right "attacks-per-switch" rhythm - Should there be a "low health → increased energy gain" comeback mechanic? Currently handled by hurt gain; revisit if playtest shows deadlock issues in low-health situations