pub mod ability;
pub mod contacts;
pub mod crystal;
pub mod custom_id;
pub mod custom_tile;
pub mod event;
pub mod grid;
pub mod hold;
pub mod intent;
pub mod level;
pub mod moves;
pub mod player;
pub mod rect;
pub mod spot;
pub mod surface;
pub mod terrain;
pub mod tuning;
pub use ability::{Ability, Step};
pub use contacts::Contacts;
pub use crystal::Crystal;
pub use custom_id::CustomId;
pub use custom_tile::CustomTile;
pub use event::{Event, JumpKind};
pub use grid::{Grid, GridTerrain};
pub use hold::Hold;
pub use intent::Intent;
pub use level::{Level, TILE, Tile};
pub use moves::Moves;
pub use player::{Mode, Player};
pub use rect::Rect;
pub use spot::Spot;
pub use surface::Surface;
pub use terrain::Terrain;
pub use tuning::{Preset, Tuning};
pub const DT: f32 = 1.0 / 120.0;
#[derive(Clone, Debug)]
pub struct World {
pub player: Player,
pub tuning: Tuning,
pub spawn: Spot,
pub respawn: bool,
riding: Option<Rect>,
belt: f32,
pub tick: u64,
pub events: Vec<Event>,
pub abilities: Vec<Box<dyn Ability>>,
scale: i32,
}
impl World {
pub fn new(tuning: Tuning, spawn: Spot) -> Self {
Self {
player: Player::at(spawn.top_left(tuning.size), &tuning),
tuning,
spawn,
respawn: true,
riding: None,
belt: 0.0,
tick: 0,
events: Vec::new(),
abilities: Vec::new(),
scale: 1,
}
}
pub fn scale(&self) -> i32 {
self.scale
}
pub(crate) fn set_scale(&mut self, k: i32) {
self.scale = k;
}
pub fn on(level: &Level, tuning: Tuning) -> Self {
Self::new(tuning, level.start())
}
pub fn ability<T: Ability + 'static>(&self) -> Option<&T> {
self.abilities
.iter()
.find_map(|a| a.as_any().downcast_ref::<T>())
}
pub fn ability_mut<T: Ability + 'static>(&mut self) -> Option<&mut T> {
self.abilities
.iter_mut()
.find_map(|a| a.as_any_mut().downcast_mut::<T>())
}
pub fn step(&mut self, terrain: &mut dyn Terrain, intent: &Intent) {
self.act(terrain, intent);
terrain.update(DT, &[self.player.rect()]);
self.settle(terrain, None);
}
pub fn act(&mut self, terrain: &mut dyn Terrain, intent: &Intent) {
self.act_among(terrain, intent, &[]);
}
pub fn act_among(&mut self, terrain: &mut dyn Terrain, intent: &Intent, others: &[Rect]) {
self.events.clear();
let mut moves = Moves::ALL;
self.abilities(terrain, intent, &mut moves, others, |a, s| a.before(s));
self.player.step(
&*terrain,
intent,
&moves,
&self.tuning,
DT,
&mut self.events,
);
self.bounce(&*terrain);
self.abilities(terrain, intent, &mut moves, others, |a, s| a.after(s));
let body = self.player.rect();
if self.player.dashes < self.tuning.dashes && terrain.refill(body) {
self.player.dashes = self.tuning.dashes;
self.events.push(Event::Recharged { crystal: true });
}
self.riding = self.player.riding();
self.belt = if self.player.grounded {
terrain.surface(self.player.feet()).belt
} else {
0.0
};
}
fn bounce(&mut self, terrain: &dyn Terrain) {
let impact = self.events.iter().find_map(|e| match e {
Event::Landed { impact, .. } => Some(*impact),
_ => None,
});
let Some(impact) = impact else { return };
let bounce = terrain.surface(self.player.feet()).bounce;
if bounce > 0.0 && impact > Surface::BOUNCE_MIN {
self.player.launch(self.player.vx, -impact * bounce);
}
}
pub fn settle(&mut self, terrain: &mut dyn Terrain, extra: Option<(f32, f32)>) {
let belt = (self.belt != 0.0).then_some((self.belt * DT, 0.0));
let carry = [self.riding.map(|r| terrain.carry(r)), extra, belt]
.into_iter()
.flatten()
.reduce(|(x, y), (ex, ey)| (x + ex, y + ey));
self.player.ride(&*terrain, carry, &self.tuning, DT);
let body = self.player.rect();
if terrain.solid(body) {
self.events.push(Event::Squished);
}
if terrain.fell_out(body) {
self.events.push(Event::FellOut);
if self.respawn {
self.respawn_at(self.spawn);
}
}
self.tick += 1;
}
pub fn respawn_at(&mut self, at: Spot) {
self.player = Player::at(at.top_left(self.tuning.size), &self.tuning);
self.riding = None;
self.belt = 0.0;
self.events.push(Event::Respawned);
}
fn abilities(
&mut self,
terrain: &mut dyn Terrain,
intent: &Intent,
moves: &mut Moves,
others: &[Rect],
hook: fn(&mut dyn Ability, &mut Step<'_>),
) {
let mut s = Step {
player: &mut self.player,
intent,
terrain,
tuning: &self.tuning,
moves,
events: &mut self.events,
others,
dt: DT,
scale: self.scale,
};
for a in &mut self.abilities {
hook(a.as_mut(), &mut s);
}
}
}
#[cfg(test)]
mod tests;