- Platformer endless runner: fixed player x, world scrolls left - Logarithmic speed curve: initial + factor * ln(1 + t / time_scale) - Enemies stomped from above; side/bottom contact kills player - Procedural level generation with StdRng seeded from SystemTime - Object pooling via Vec::retain + frontier-based generator - Coyote time: grace window after leaving platform edge - Data-driven pickup system with trait-based effects (ActiveEffect) - Invulnerability, JumpBoost, ScoreMultiplier — extend via config - Score accumulates with per-effect multiplier; stomp bonuses scaled - Game over screen with score, best score, restart prompt - HUD: score, speed bar, active effect timer bars Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
81 lines
2.1 KiB
Rust
81 lines
2.1 KiB
Rust
use crate::config::Config;
|
|
|
|
pub struct Player {
|
|
pub x: f32,
|
|
pub y: f32,
|
|
pub prev_y: f32,
|
|
pub vy: f32,
|
|
pub width: f32,
|
|
pub height: f32,
|
|
pub on_ground: bool,
|
|
pub alive: bool,
|
|
/// Countdown from `coyote_time` to 0. Positive value = jump still allowed.
|
|
pub coyote_timer: f32,
|
|
}
|
|
|
|
impl Player {
|
|
pub fn new(cfg: &Config) -> Self {
|
|
let y = cfg.start_platform_y - cfg.player_height;
|
|
Self {
|
|
x: cfg.player_x,
|
|
y,
|
|
prev_y: y,
|
|
vy: 0.0,
|
|
width: cfg.player_width,
|
|
height: cfg.player_height,
|
|
on_ground: true,
|
|
alive: true,
|
|
coyote_timer: cfg.coyote_time,
|
|
}
|
|
}
|
|
|
|
/// Apply gravity and integrate position. Must be called before collision resolution.
|
|
pub fn update(&mut self, dt: f32, cfg: &Config) {
|
|
self.prev_y = self.y;
|
|
|
|
// Refresh coyote window while grounded; tick it down while airborne.
|
|
if self.on_ground {
|
|
self.coyote_timer = cfg.coyote_time;
|
|
} else {
|
|
self.coyote_timer = (self.coyote_timer - dt).max(0.0);
|
|
}
|
|
|
|
self.vy += cfg.gravity * dt;
|
|
self.y += self.vy * dt;
|
|
|
|
// Reset ground flag; platform collision will set it back.
|
|
self.on_ground = false;
|
|
|
|
if self.y > cfg.screen_height as f32 + 50.0 {
|
|
self.alive = false;
|
|
}
|
|
}
|
|
|
|
/// Jump if the coyote window is open (on ground OR just left a platform).
|
|
/// `jump_multiplier` comes from active effects.
|
|
pub fn jump(&mut self, cfg: &Config, jump_multiplier: f32) {
|
|
if self.coyote_timer > 0.0 {
|
|
self.vy = cfg.jump_velocity * jump_multiplier;
|
|
self.coyote_timer = 0.0; // consume the window
|
|
self.on_ground = false;
|
|
}
|
|
}
|
|
|
|
pub fn stomp_bounce(&mut self, cfg: &Config) {
|
|
self.vy = cfg.stomp_bounce_velocity;
|
|
self.on_ground = false;
|
|
}
|
|
|
|
pub fn bottom(&self) -> f32 {
|
|
self.y + self.height
|
|
}
|
|
|
|
pub fn prev_bottom(&self) -> f32 {
|
|
self.prev_y + self.height
|
|
}
|
|
|
|
pub fn right(&self) -> f32 {
|
|
self.x + self.width
|
|
}
|
|
}
|