2024-07-06 13:33:58 +02:00
|
|
|
use crate::{
|
|
|
|
activities::{house, overworld, Activity},
|
2024-07-07 11:54:42 +00:00
|
|
|
assets::Assets,
|
2024-07-06 13:33:58 +02:00
|
|
|
State
|
|
|
|
};
|
2024-07-07 08:49:19 +00:00
|
|
|
use comfy::{EngineContext, Vec2};
|
2024-07-07 16:39:54 +02:00
|
|
|
use std::{
|
|
|
|
ops::{Add, Sub},
|
|
|
|
time::Instant
|
|
|
|
};
|
2024-07-06 20:23:25 +00:00
|
|
|
|
2024-07-06 20:45:05 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Ghost {
|
2024-07-07 08:49:19 +00:00
|
|
|
/// Current electric charge of the Ghost.
|
2024-07-06 20:45:05 +00:00
|
|
|
pub charge: f32,
|
2024-07-07 08:49:19 +00:00
|
|
|
/// Max electric charge of the Ghost.
|
|
|
|
pub max_charge: f32,
|
|
|
|
|
|
|
|
/// The position of the ghost in the overworld. Expressed in tile coordinates, but
|
|
|
|
/// as a float as a ghost takes more than one tick to move.
|
|
|
|
pub overworld_pos: Vec2,
|
|
|
|
/// Pending movement of the ghost in the overworld.
|
|
|
|
pub overworld_movement_pending: Vec2,
|
|
|
|
/// The current movement speed of the ghost in tiles/sec.
|
|
|
|
pub overworld_movement_speed: f32,
|
|
|
|
/// The timestamp of the last overworld position update.
|
|
|
|
pub overworld_pos_last_update: Instant
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ghost {
|
|
|
|
pub fn update_overworld_pos(&mut self, now: Instant) {
|
|
|
|
// This calculation is extremely simplistic. It will not work properly if both
|
|
|
|
// x and y of movement_pending are non-zero. But we only move left,right,up,down
|
|
|
|
// so that should never happen!
|
|
|
|
let secs = now
|
|
|
|
.duration_since(self.overworld_pos_last_update)
|
|
|
|
.as_secs_f32();
|
|
|
|
let mut movement = self.overworld_movement_pending.signum()
|
|
|
|
* self.overworld_movement_speed
|
|
|
|
* secs;
|
|
|
|
|
|
|
|
// limit the movement to the remaining movement
|
|
|
|
if self.overworld_movement_pending.x.abs() < movement.x.abs() {
|
|
|
|
movement.x = self.overworld_movement_pending.x;
|
|
|
|
}
|
|
|
|
if self.overworld_movement_pending.y.abs() < movement.y.abs() {
|
|
|
|
movement.y = self.overworld_movement_pending.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
// execute the movement
|
|
|
|
self.overworld_pos += movement;
|
|
|
|
self.overworld_movement_pending -= movement;
|
|
|
|
self.overworld_pos_last_update = now;
|
|
|
|
}
|
2024-07-06 20:45:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Ghost {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
charge: 1000.0,
|
2024-07-07 08:49:19 +00:00
|
|
|
max_charge: 1000.0,
|
|
|
|
|
|
|
|
overworld_pos: Vec2::ZERO,
|
|
|
|
overworld_movement_pending: Vec2::ZERO,
|
|
|
|
overworld_movement_speed: 0.0,
|
|
|
|
overworld_pos_last_update: Instant::now()
|
2024-07-06 20:45:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-07-06 20:23:25 +00:00
|
|
|
#[repr(i32)]
|
|
|
|
pub enum ZLayer {
|
2024-07-07 14:13:52 +00:00
|
|
|
HumanLayer = -8,
|
|
|
|
MagneticLayer = -5,
|
|
|
|
ElectricLayer = -2,
|
2024-07-06 20:23:25 +00:00
|
|
|
MapMax = -1,
|
|
|
|
Human = 0,
|
2024-07-07 16:39:54 +02:00
|
|
|
Ghost = 1,
|
|
|
|
UI = 100
|
2024-07-06 20:23:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ZLayer> for i32 {
|
|
|
|
fn from(value: ZLayer) -> Self {
|
|
|
|
// safe because #[repr(i32)]
|
|
|
|
value as i32
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sub<i32> for ZLayer {
|
|
|
|
type Output = i32;
|
|
|
|
|
|
|
|
fn sub(self, other: i32) -> Self::Output {
|
|
|
|
i32::from(self) - other
|
|
|
|
}
|
|
|
|
}
|
2024-07-05 23:10:25 +02:00
|
|
|
|
2024-07-07 16:39:54 +02:00
|
|
|
impl Add<i32> for ZLayer {
|
|
|
|
type Output = i32;
|
|
|
|
|
|
|
|
fn add(self, other: i32) -> Self::Output {
|
|
|
|
i32::from(self) + other
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-07 17:39:06 +02:00
|
|
|
pub fn setup(state: &mut State, ctx: &mut EngineContext<'_>) {
|
2024-07-07 11:54:42 +00:00
|
|
|
Assets::load(ctx);
|
2024-07-07 14:13:52 +00:00
|
|
|
|
2024-07-07 17:39:06 +02:00
|
|
|
overworld::setup(state, ctx);
|
2024-07-07 14:13:52 +00:00
|
|
|
//house::setup(state, ctx);
|
2024-07-07 16:24:01 +00:00
|
|
|
|
|
|
|
ctx.load_texture_from_bytes(
|
|
|
|
"ghost_house",
|
|
|
|
include_bytes!(concat!(
|
|
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
|
|
"/assets/entities/ghost.png"
|
|
|
|
))
|
|
|
|
);
|
|
|
|
|
|
|
|
ctx.load_texture_from_bytes(
|
|
|
|
"human_house",
|
|
|
|
include_bytes!(concat!(
|
|
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
|
|
"/assets/entities/human_captured.png"
|
|
|
|
))
|
|
|
|
);
|
2024-07-07 11:54:42 +00:00
|
|
|
}
|
|
|
|
|
2024-07-07 15:18:32 +00:00
|
|
|
/// The amount of energy a ghost consumes idle.
|
|
|
|
pub const GHOST_DISCHARGE_RATE: f32 = 70.0;
|
|
|
|
/// The amount of energy additionally consumed by a moving ghost.
|
|
|
|
pub const GHOST_DISCHARGE_RATE_MOVEMENT: f32 = 70.0;
|
|
|
|
/// The amount of energy a house consumes idle.
|
|
|
|
pub const HOUSE_DISCHARGE_RATE: f32 = 30.0;
|
|
|
|
/// The amount of energy a ghost can charge when inside a house.
|
|
|
|
pub const GHOST_CHARGE_RATE: f32 = 200.0;
|
|
|
|
|
|
|
|
pub fn update(state: &mut State, ctx: &mut EngineContext<'_>) {
|
|
|
|
// Update the score. It's based on time.
|
|
|
|
state.score += ctx.delta * 10.0;
|
|
|
|
|
|
|
|
// Update the currently active activity.
|
2024-07-06 13:33:58 +02:00
|
|
|
match state.activity {
|
2024-07-07 15:18:32 +00:00
|
|
|
Activity::House(_) => house::update(state, ctx),
|
|
|
|
Activity::Overworld => overworld::update(state, ctx)
|
2024-07-06 13:33:58 +02:00
|
|
|
}
|
2024-07-07 15:18:32 +00:00
|
|
|
|
|
|
|
// We update the charge of houses here - the charge will always decrease, even if the
|
|
|
|
// house is not the current activity.
|
|
|
|
for (house_pos, house) in &mut state.houses {
|
|
|
|
house.charge -= ctx.delta * HOUSE_DISCHARGE_RATE;
|
|
|
|
|
|
|
|
match state.activity {
|
|
|
|
Activity::House(pos) if *house_pos == pos => {
|
|
|
|
// The ghost is currently inside the house. Increase its discarge rate.
|
|
|
|
house.charge -= ctx.delta * GHOST_DISCHARGE_RATE;
|
|
|
|
if house.charge < 0.0 {
|
|
|
|
state.ghost.charge += house.charge;
|
|
|
|
house.charge = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// And possibly also charge the ghost when inside a house.
|
|
|
|
if state.ghost.charge < state.ghost.max_charge {
|
|
|
|
let charge_transfer = (ctx.delta * GHOST_CHARGE_RATE)
|
|
|
|
.min(state.ghost.max_charge - state.ghost.charge)
|
|
|
|
.min(house.charge);
|
|
|
|
state.ghost.charge += charge_transfer;
|
|
|
|
house.charge -= charge_transfer;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the ghost's charge never drops below 0.
|
2024-07-07 16:39:54 +02:00
|
|
|
state.ghost.charge = state.ghost.charge.max(0.0);
|
2024-07-05 23:16:53 +02:00
|
|
|
}
|
2024-07-05 23:10:25 +02:00
|
|
|
|
2024-07-06 20:23:25 +00:00
|
|
|
pub fn draw(state: &State, engine: &EngineContext<'_>) {
|
2024-07-06 13:33:58 +02:00
|
|
|
match state.activity {
|
2024-07-07 11:21:08 +00:00
|
|
|
Activity::House(_) => house::draw(state, engine),
|
2024-07-06 13:33:58 +02:00
|
|
|
Activity::Overworld => overworld::draw(state, engine)
|
|
|
|
}
|
2024-07-06 20:45:05 +00:00
|
|
|
crate::ui::draw(state, engine);
|
2024-07-05 23:10:25 +02:00
|
|
|
}
|