feat: add app skeleton with balance module, arena and tower
This commit is contained in:
120
src/balance/mod.rs
Normal file
120
src/balance/mod.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
//! Balancing data (ADR-0001).
|
||||
//!
|
||||
//! This module holds **only data and data structures**. No gameplay logic, no Bevy systems.
|
||||
//! Gameplay reads `Res<Balance>` and never sees the constants in [`values`].
|
||||
//!
|
||||
//! The numbers themselves live in [`values`] and are all placeholders; this file only gives
|
||||
//! them their shape. `Balance` is deliberately built from plain fields, numbers and vectors —
|
||||
//! no `Handle`, no `Entity` — so it could be serialized without a redesign if the revision
|
||||
//! triggers of ADR-0001 ever fire. A `serde` derive is explicitly *not* added now.
|
||||
|
||||
mod values;
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
/// The numeric type of every stat and balancing value (ADR-0002 §4).
|
||||
///
|
||||
/// An alias, not a newtype. `f32` carries seven significant digits, which is enough for the
|
||||
/// slice; ADR-0002 revision trigger 1 says when this is reconsidered.
|
||||
pub type Stat = f32;
|
||||
|
||||
/// All balancing data of the game, in one place.
|
||||
///
|
||||
/// Built by exactly one function, [`Balance::from_constants`]. Nothing else constructs a
|
||||
/// `Balance`, which is what makes that function the seam an asset loading path would replace.
|
||||
#[derive(Resource, Debug, Clone)]
|
||||
pub struct Balance {
|
||||
pub arena: ArenaBalance,
|
||||
pub tower: TowerBalance,
|
||||
pub enemy: EnemyBalance,
|
||||
pub combat: CombatBalance,
|
||||
pub meta: MetaBalance,
|
||||
pub caps: Caps,
|
||||
}
|
||||
|
||||
/// The playing field.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ArenaBalance {
|
||||
/// Distance from the tower in the centre to the rim enemies spawn on, in world units.
|
||||
pub radius: Stat,
|
||||
}
|
||||
|
||||
/// Base values of the tower, before any modifier from ADR-0002 is applied.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TowerBalance {
|
||||
/// Damage of a single shot.
|
||||
pub damage: Stat,
|
||||
/// Seconds between two shots — an interval, not a rate (ADR-0002 §3).
|
||||
pub attack_interval: Stat,
|
||||
/// Tower health. The tower dying is what ends a run.
|
||||
pub health: Stat,
|
||||
}
|
||||
|
||||
/// Base values of the single enemy archetype the skeleton knows.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnemyBalance {
|
||||
/// Movement speed in world units per second.
|
||||
pub speed: Stat,
|
||||
/// Health at wave 1.
|
||||
pub health: Stat,
|
||||
}
|
||||
|
||||
/// Values that describe how things touch each other.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CombatBalance {
|
||||
/// A projectile within this distance of an enemy counts as a hit, in world units.
|
||||
pub hit_radius: Stat,
|
||||
}
|
||||
|
||||
/// Meta progression income — the currency that survives a run.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MetaBalance {
|
||||
/// Meta currency granted per killed enemy.
|
||||
pub gain_per_kill: Stat,
|
||||
}
|
||||
|
||||
/// Upper bounds for the percentage-point sums of the stat pipeline (ADR-0002 §2).
|
||||
///
|
||||
/// Empty on purpose. ADR-0002 §2 requires every clamp limit to be a balancing value living
|
||||
/// here — one per clamped stat — with no global clamp and no default in code. The skeleton
|
||||
/// has none of the stats that carry a cap (`crit_chance`, `defense_percent`,
|
||||
/// `cooldown_reduction`, `wave_cooldown_reduction`, `free_upgrade_chance`), so there is
|
||||
/// nothing to put in yet.
|
||||
///
|
||||
// ponytail: an empty struct rather than five caps nothing reads. It exists so the place is
|
||||
// named and reachable as `balance.caps.*`; the fields land with the stats that need them in
|
||||
// the thickening phase, and the compiler will point at this struct when they do.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Caps {}
|
||||
|
||||
impl Balance {
|
||||
/// Builds `Balance` from the constants in [`values`].
|
||||
///
|
||||
/// **The one and only constructor** (ADR-0001, decision point 3). Everything that wants
|
||||
/// balancing data goes through the resource this returns; nothing else assembles a
|
||||
/// `Balance`. That makes this function the single seam a later asset loading path — or a
|
||||
/// debug panel that mutates `Balance` in the running process — attaches to.
|
||||
pub fn from_constants() -> Self {
|
||||
Self {
|
||||
arena: ArenaBalance {
|
||||
radius: values::ARENA_RADIUS,
|
||||
},
|
||||
tower: TowerBalance {
|
||||
damage: values::TOWER_DAMAGE,
|
||||
attack_interval: values::TOWER_ATTACK_INTERVAL,
|
||||
health: values::TOWER_HEALTH,
|
||||
},
|
||||
enemy: EnemyBalance {
|
||||
speed: values::ENEMY_SPEED,
|
||||
health: values::ENEMY_HEALTH,
|
||||
},
|
||||
combat: CombatBalance {
|
||||
hit_radius: values::HIT_RADIUS,
|
||||
},
|
||||
meta: MetaBalance {
|
||||
gain_per_kill: values::META_GAIN_PER_KILL,
|
||||
},
|
||||
caps: Caps::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user