Reviewed-on: #19 Co-authored-by: Dominic <git@msrd0.de> Co-committed-by: Dominic <git@msrd0.de>
242 lines
6.8 KiB
Rust
242 lines
6.8 KiB
Rust
use crate::{
|
|
activities::Activity,
|
|
assets::ASSETS,
|
|
game::{ZLayer, GHOST_DISCHARGE_RATE, GHOST_DISCHARGE_RATE_MOVEMENT},
|
|
State
|
|
};
|
|
use comfy::{
|
|
draw_rect_outline, draw_sprite, error, info, is_key_down, main_camera_mut,
|
|
play_sound_id, stop_sound_id, texture_id, vec2, EngineContext, IVec2, KeyCode, Vec2,
|
|
RED, WHITE
|
|
};
|
|
use std::time::Instant;
|
|
use worldgen::MovementCost;
|
|
|
|
pub mod worldgen;
|
|
|
|
pub fn setup(_state: &State, ctx: &EngineContext<'_>) {
|
|
ctx.load_texture_from_bytes(
|
|
"human_overworld",
|
|
include_bytes!(concat!(
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
"/assets/entities/human_overworld.png"
|
|
))
|
|
);
|
|
}
|
|
|
|
pub fn draw(state: &State, _ctx: &EngineContext<'_>) {
|
|
for (coords, tile) in state.overworld.iter_tiles() {
|
|
for (i, texture) in tile.textures().iter().rev().enumerate() {
|
|
let i = i as i32;
|
|
draw_sprite(
|
|
*texture,
|
|
coords.as_vec2(),
|
|
WHITE,
|
|
ZLayer::MapMax - i,
|
|
Vec2::ONE
|
|
);
|
|
if state.dev {
|
|
draw_rect_outline(coords.as_vec2(), Vec2::ONE, 0.1, RED, 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut ghost_pos = state.ghost.overworld_pos;
|
|
ghost_pos.y += 0.5;
|
|
draw_sprite(
|
|
texture_id("human_overworld"),
|
|
ghost_pos,
|
|
WHITE,
|
|
ZLayer::Ghost.into(),
|
|
vec2(1.0, 1.25)
|
|
);
|
|
}
|
|
|
|
fn update_move_player(state: &mut State, ctx: &mut EngineContext<'_>) {
|
|
let now = Instant::now();
|
|
|
|
if !state.overworld.sound_playing {
|
|
if let Some(house) = state.house_mut(ctx) {
|
|
if house.sound_playing {
|
|
stop_sound_id(ASSETS.music.mesmerizing_galaxy_loop);
|
|
house.sound_playing = false;
|
|
}
|
|
}
|
|
play_sound_id(ASSETS.music.galactic_rap_speedup);
|
|
state.overworld.sound_playing = true;
|
|
}
|
|
|
|
// Are there any pending position updates? If so, we ignore all user input and execute
|
|
// the pending updates.
|
|
if state.ghost.overworld_movement_pending != Vec2::ZERO {
|
|
info!(
|
|
"Pending Movement: {:?}",
|
|
state.ghost.overworld_movement_pending
|
|
);
|
|
state.ghost.update_overworld_pos(now);
|
|
}
|
|
if state.ghost.overworld_movement_pending != Vec2::ZERO {
|
|
return;
|
|
}
|
|
|
|
// Otherwise, we check for user inputs, and update the pending movement accordingly.
|
|
let tile_pos = IVec2 {
|
|
x: state.ghost.overworld_pos.x.round() as _,
|
|
y: state.ghost.overworld_pos.y.round() as _
|
|
};
|
|
let Some(tile) = state.overworld.get_tile(tile_pos).copied() else {
|
|
error!("How can we be standing inside a non-generated tile?");
|
|
return;
|
|
};
|
|
|
|
let mut requested_pos = None;
|
|
let mut requested_pos_diff = None;
|
|
let mut requested_cost_curr = None;
|
|
let mut requested_cost_new = None;
|
|
|
|
if is_key_down(KeyCode::Up) {
|
|
let diff = IVec2 { x: 0, y: 1 };
|
|
let new_pos = tile_pos + diff;
|
|
let new_tile = state.overworld.get_or_generate_tile(new_pos);
|
|
if new_tile.can_stand_inside() {
|
|
requested_pos = Some(new_pos);
|
|
requested_pos_diff = Some(diff);
|
|
requested_cost_curr = Some(tile.movement_cost_up());
|
|
requested_cost_new = Some(new_tile.movement_cost_down());
|
|
} else {
|
|
info!("Rejecting movement - cannot stand in the requested tile.");
|
|
}
|
|
}
|
|
|
|
if is_key_down(KeyCode::Down) {
|
|
let diff = IVec2 { x: 0, y: -1 };
|
|
let new_pos = tile_pos + diff;
|
|
let new_tile = state.overworld.get_or_generate_tile(new_pos);
|
|
if new_tile.can_stand_inside() {
|
|
requested_pos = Some(new_pos);
|
|
requested_pos_diff = Some(diff);
|
|
requested_cost_curr = Some(tile.movement_cost_down());
|
|
requested_cost_new = Some(new_tile.movement_cost_up());
|
|
} else {
|
|
info!("Rejecting movement - cannot stand in the requested tile.");
|
|
}
|
|
}
|
|
|
|
if is_key_down(KeyCode::Left) {
|
|
let diff = IVec2 { x: -1, y: 0 };
|
|
let new_pos = tile_pos + diff;
|
|
let new_tile = state.overworld.get_or_generate_tile(new_pos);
|
|
if new_tile.can_stand_inside() {
|
|
requested_pos = Some(new_pos);
|
|
requested_pos_diff = Some(diff);
|
|
requested_cost_curr = Some(tile.movement_cost_left());
|
|
requested_cost_new = Some(new_tile.movement_cost_right());
|
|
} else {
|
|
info!("Rejecting movement - cannot stand in the requested tile.");
|
|
}
|
|
}
|
|
|
|
if is_key_down(KeyCode::Right) {
|
|
let diff = IVec2 { x: 1, y: 0 };
|
|
let new_pos = tile_pos + diff;
|
|
let new_tile = state.overworld.get_or_generate_tile(new_pos);
|
|
if new_tile.can_stand_inside() {
|
|
requested_pos = Some(new_pos);
|
|
requested_pos_diff = Some(diff);
|
|
requested_cost_curr = Some(tile.movement_cost_right());
|
|
requested_cost_new = Some(new_tile.movement_cost_left());
|
|
} else {
|
|
info!("Rejecting movement - cannot stand in the requested tile.");
|
|
}
|
|
}
|
|
|
|
// only continue if some movement was requested
|
|
let Some(_requested_pos) = requested_pos else {
|
|
return;
|
|
};
|
|
let Some(requested_pos_diff) = requested_pos_diff else {
|
|
return;
|
|
};
|
|
let Some(requested_cost_curr) = requested_cost_curr else {
|
|
return;
|
|
};
|
|
let Some(requested_cost_new) = requested_cost_new else {
|
|
return;
|
|
};
|
|
|
|
state.ghost.overworld_movement_speed = match (requested_cost_curr, requested_cost_new)
|
|
{
|
|
// movement in this direction not possible
|
|
(MovementCost::Infinite, _) | (_, MovementCost::Infinite) => {
|
|
info!("Rejecting movement - movement cost is infinite");
|
|
return;
|
|
},
|
|
|
|
// we are walking on a path
|
|
(MovementCost::Path, MovementCost::Path) => 10.0,
|
|
|
|
// we are walking across an obstacle
|
|
(MovementCost::Obstacle, _) | (_, MovementCost::Obstacle) => 1.0,
|
|
|
|
// we are walking on grass
|
|
_ => 5.0
|
|
};
|
|
state.ghost.overworld_movement_pending = Vec2 {
|
|
x: requested_pos_diff.x as _,
|
|
y: requested_pos_diff.y as _
|
|
};
|
|
state.ghost.overworld_pos_last_update = now;
|
|
}
|
|
|
|
pub fn update(state: &mut State, ctx: &mut EngineContext<'_>) {
|
|
let mut camera = main_camera_mut();
|
|
camera.center = state.ghost.overworld_pos;
|
|
camera.zoom = 30.0;
|
|
|
|
// move player
|
|
update_move_player(state, ctx);
|
|
|
|
//move into house if player stepp at door
|
|
{
|
|
let tile_pos = IVec2 {
|
|
x: state.ghost.overworld_pos.x.round() as _,
|
|
y: state.ghost.overworld_pos.y.round() as _
|
|
};
|
|
let current_tile = state.overworld.get_or_generate_tile(tile_pos);
|
|
if current_tile.can_enter_house() {
|
|
info!("enter house at {tile_pos}");
|
|
state.activity = Activity::House(tile_pos);
|
|
state.house_mut(ctx); // gen new house
|
|
}
|
|
}
|
|
|
|
// energie lost
|
|
{
|
|
let ghost = &mut state.ghost;
|
|
ghost.charge -= GHOST_DISCHARGE_RATE * ctx.delta;
|
|
if ghost.overworld_movement_pending != Vec2::ZERO {
|
|
ghost.charge -= GHOST_DISCHARGE_RATE_MOVEMENT * ctx.delta;
|
|
}
|
|
}
|
|
|
|
// generate more chunks if needed
|
|
{
|
|
let half_viewport = (camera.world_viewport() * 0.5 + 3.0).as_ivec2();
|
|
let rounded_ghost_pos = IVec2 {
|
|
x: state.ghost.overworld_pos.x.round() as _,
|
|
y: state.ghost.overworld_pos.y.round() as _
|
|
};
|
|
state.overworld.get_or_generate_tile(
|
|
rounded_ghost_pos + IVec2::new(half_viewport.x, half_viewport.y)
|
|
);
|
|
state.overworld.get_or_generate_tile(
|
|
rounded_ghost_pos + IVec2::new(half_viewport.x, -half_viewport.y)
|
|
);
|
|
state.overworld.get_or_generate_tile(
|
|
rounded_ghost_pos + IVec2::new(-half_viewport.x, half_viewport.y)
|
|
);
|
|
state.overworld.get_or_generate_tile(
|
|
rounded_ghost_pos + IVec2::new(-half_viewport.x, -half_viewport.y)
|
|
);
|
|
}
|
|
}
|