Merge remote-tracking branch 'origin/main' into room-creation-placing-furniture
Conflicts: Cargo.lock Cargo.toml src/activities/house/mod.rs src/activities/mod.rs src/main.rs
1
Cargo.lock
generated
|
@ -2130,7 +2130,6 @@ dependencies = [
|
|||
"comfy",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"log",
|
||||
"resvg",
|
||||
]
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ opt-level = 3
|
|||
[dependencies]
|
||||
comfy = { version = "0.4.0", features = ["wayland"] }
|
||||
indexmap = "2"
|
||||
log = "0.4.22"
|
||||
|
||||
[build-dependencies]
|
||||
heck = "0.5"
|
||||
|
|
BIN
assets/entities/ghost.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/furniture/bedroom/ac.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
assets/furniture/bedroom/bed.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
assets/furniture/bedroom/bookshelf.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/furniture/bedroom/couch.png
Normal file
After Width: | Height: | Size: 8 KiB |
BIN
assets/furniture/bedroom/cupboard.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
assets/furniture/bedroom/display_off.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
assets/furniture/bedroom/display_on.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/furniture/bedroom/drawer.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
assets/furniture/bedroom/elec/ac.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/furniture/bedroom/elec/display_off.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
assets/furniture/bedroom/elec/display_on.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
assets/furniture/bedroom/elec/keyboard.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/elec/laptop_off.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/elec/laptop_on.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/elec/mini_ac.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/furniture/bedroom/elec/mouse.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/elec/nightlight.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/furniture/bedroom/elec/pc.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/elec/phone.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/elec/receiver.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/elec/tv.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/furniture/bedroom/elec/window_ac.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/furniture/bedroom/keyboard.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/laptop_off.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
assets/furniture/bedroom/laptop_on.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/furniture/bedroom/metal/ac.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
assets/furniture/bedroom/metal/bed.png
Normal file
After Width: | Height: | Size: 6 KiB |
BIN
assets/furniture/bedroom/metal/cupboard.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/furniture/bedroom/metal/display_off.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/metal/display_on.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/metal/drawer.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
assets/furniture/bedroom/metal/keyboard.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/metal/laptop_off.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/furniture/bedroom/metal/laptop_on.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/furniture/bedroom/metal/mini_ac.png
Normal file
After Width: | Height: | Size: 6 KiB |
BIN
assets/furniture/bedroom/metal/mouse.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
assets/furniture/bedroom/metal/nightlight.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
assets/furniture/bedroom/metal/nightstand.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/furniture/bedroom/metal/pc.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/furniture/bedroom/metal/phone.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/metal/receiver.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/bedroom/metal/tv.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/bedroom/metal/wardrobe.png
Normal file
After Width: | Height: | Size: 7 KiB |
BIN
assets/furniture/bedroom/metal/window_ac.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
assets/furniture/bedroom/mini_ac.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
assets/furniture/bedroom/mouse.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
assets/furniture/bedroom/nightlight.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
assets/furniture/bedroom/nightstand.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/furniture/bedroom/pc.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/bedroom/phone.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/receiver.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/furniture/bedroom/tv.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/furniture/bedroom/tv_stand.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/bedroom/wardrobe.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
assets/furniture/bedroom/window.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
assets/furniture/bedroom/window_ac.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/sounds/bzz.mp3
Normal file
|
@ -16,7 +16,7 @@ pub struct HouseState {
|
|||
}
|
||||
|
||||
pub fn setup(state: &mut crate::State, ctx: &mut EngineContext<'_>) {
|
||||
let house = {
|
||||
if let Some(house) = state.house() {
|
||||
let room = Room::new(ctx);
|
||||
let player = Player::new(&room);
|
||||
HouseState { room, player }
|
||||
|
@ -39,8 +39,8 @@ pub fn draw(state: &crate::State, _ctx: &comfy::EngineContext<'_>) {
|
|||
}
|
||||
|
||||
pub fn update(state: &mut crate::State, ctx: &mut comfy::EngineContext<'_>) {
|
||||
if let Some(house) = &mut state.house {
|
||||
house.player.update(&house.room.grid);
|
||||
let house = state.house_mut();
|
||||
house.player.update(&house.grid);
|
||||
|
||||
if house.player.is_moving_to_right_room(&house.room) {
|
||||
house.room = Room::new(ctx);
|
||||
|
@ -49,5 +49,4 @@ pub fn update(state: &mut crate::State, ctx: &mut comfy::EngineContext<'_>) {
|
|||
house.room = Room::new(ctx);
|
||||
house.player.reset_on_room(&house.room, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use comfy::IVec2;
|
||||
|
||||
pub mod house;
|
||||
pub mod overworld;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub enum Activity {
|
||||
House(IVec2),
|
||||
#[default]
|
||||
House,
|
||||
#[allow(dead_code)]
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
use comfy::{
|
||||
draw_rect_outline, draw_sprite, main_camera_mut, EngineContext, IVec2, Vec2, RED,
|
||||
WHITE
|
||||
use crate::{
|
||||
activities::{
|
||||
house::{self, HouseState},
|
||||
Activity
|
||||
},
|
||||
game::ZLayer,
|
||||
State
|
||||
};
|
||||
|
||||
use crate::game::ZLayer;
|
||||
use comfy::{
|
||||
draw_circle, draw_rect_outline, draw_sprite, error, info, is_key_down,
|
||||
main_camera_mut, EngineContext, IVec2, KeyCode, Vec2, RED, WHITE
|
||||
};
|
||||
use std::time::Instant;
|
||||
use worldgen::MovementCost;
|
||||
|
||||
pub mod worldgen;
|
||||
|
||||
|
@ -21,11 +29,168 @@ pub fn draw(state: &crate::State, _engine: &comfy::EngineContext<'_>) {
|
|||
draw_rect_outline(coords.as_vec2(), Vec2::ONE, 0.1, RED, 10);
|
||||
}
|
||||
}
|
||||
draw_circle(state.ghost.overworld_pos, 0.5, RED, ZLayer::Ghost.into());
|
||||
}
|
||||
|
||||
pub fn update(state: &mut crate::State, _engine: &mut EngineContext<'_>) {
|
||||
fn update_move_player(state: &mut State) {
|
||||
let now = Instant::now();
|
||||
|
||||
// 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);
|
||||
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;
|
||||
|
||||
//move into house if player stepp at door
|
||||
{
|
||||
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(); // gen new house
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(state: &mut State, _ctx: &mut EngineContext<'_>) {
|
||||
let mut camera = main_camera_mut();
|
||||
camera.center = Vec2::ZERO;
|
||||
camera.zoom = 30.0;
|
||||
state.overworld.get_or_generate_tile(IVec2::ZERO);
|
||||
|
||||
// move player
|
||||
update_move_player(state);
|
||||
|
||||
// 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use crate::assets::ASSETS;
|
||||
use comfy::{IVec2, TextureHandle, UVec2};
|
||||
use log::info;
|
||||
use comfy::{info, IVec2, TextureHandle, UVec2};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub enum MovementCost {
|
||||
|
@ -117,28 +116,61 @@ impl Tile {
|
|||
}
|
||||
|
||||
pub fn can_stand_inside(&self) -> bool {
|
||||
unimplemented!()
|
||||
#[allow(clippy::match_like_matches_macro)] // I believe this is better readable
|
||||
match self {
|
||||
Self::House { door: false, .. } => false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
pub fn movement_cost_left(&self) -> MovementCost {
|
||||
unimplemented!()
|
||||
match self {
|
||||
Self::Grass => MovementCost::Default,
|
||||
Self::Path { left: true, .. } => MovementCost::Path,
|
||||
Self::Path { left: false, .. } => MovementCost::Default,
|
||||
Self::Fence { left: false, .. } => MovementCost::Obstacle,
|
||||
_ => MovementCost::Infinite
|
||||
}
|
||||
}
|
||||
pub fn movement_cost_right(&self) -> MovementCost {
|
||||
unimplemented!()
|
||||
match self {
|
||||
Self::Grass => MovementCost::Default,
|
||||
Self::Path { right: true, .. } => MovementCost::Path,
|
||||
Self::Path { right: false, .. } => MovementCost::Default,
|
||||
Self::Fence { right: false, .. } => MovementCost::Obstacle,
|
||||
_ => MovementCost::Infinite
|
||||
}
|
||||
}
|
||||
pub fn movement_cost_up(&self) -> MovementCost {
|
||||
unimplemented!()
|
||||
match self {
|
||||
Self::Grass => MovementCost::Default,
|
||||
Self::Path { top: true, .. } => MovementCost::Path,
|
||||
Self::Path { top: false, .. } => MovementCost::Default,
|
||||
Self::Fence { top: false, .. } => MovementCost::Obstacle,
|
||||
_ => MovementCost::Infinite
|
||||
}
|
||||
}
|
||||
pub fn movement_cost_down(&self) -> MovementCost {
|
||||
unimplemented!()
|
||||
match self {
|
||||
Self::Grass => MovementCost::Default,
|
||||
Self::Path { bottom: true, .. } => MovementCost::Path,
|
||||
Self::Path { bottom: false, .. } => MovementCost::Default,
|
||||
Self::Fence { bottom: false, .. } => MovementCost::Obstacle,
|
||||
Self::House { door: true, .. } => MovementCost::Path,
|
||||
_ => MovementCost::Infinite
|
||||
}
|
||||
}
|
||||
pub fn can_enter_house(&self) -> bool {
|
||||
unimplemented!()
|
||||
#[allow(clippy::match_like_matches_macro)] // I believe this is better readable
|
||||
match self {
|
||||
Self::House { door: true, .. } => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The size of a chunk (both width and height). This value squared gives the amount of
|
||||
/// tiles in the chunk.
|
||||
const CHUNK_SIZE: u32 = 100;
|
||||
const CHUNK_SIZE: u32 = 50;
|
||||
|
||||
/// Chunks
|
||||
#[derive(Debug)]
|
||||
|
@ -286,7 +318,10 @@ impl Chunk {
|
|||
fence_vert,
|
||||
grass,
|
||||
house(ASSETS.overworld.house_bottom_left),
|
||||
house(ASSETS.overworld.house_bottom_door),
|
||||
Tile::House {
|
||||
door: true,
|
||||
texture: ASSETS.overworld.house_bottom_door
|
||||
},
|
||||
house(ASSETS.overworld.house_bottom_window),
|
||||
house(ASSETS.overworld.house_bottom_window),
|
||||
house(ASSETS.overworld.house_bottom_right),
|
||||
|
@ -403,7 +438,15 @@ fn world_to_chunk_and_local_coords(world_coords: IVec2) -> (IVec2, UVec2) {
|
|||
}
|
||||
|
||||
impl Overworld {
|
||||
fn get_tile(&self, world_coords: IVec2) -> Option<&Tile> {
|
||||
/// Return a [`Tile`] at the given world coordinates, or `None` if that tile has not
|
||||
/// been generated yet. Uses engine/world cordinates.
|
||||
pub fn get_tile(&self, world_coords: IVec2) -> Option<&Tile> {
|
||||
let mut coords = world_coords;
|
||||
coords.y *= -1;
|
||||
self.get_tile_private(coords)
|
||||
}
|
||||
|
||||
fn get_tile_private(&self, world_coords: IVec2) -> Option<&Tile> {
|
||||
let (chunk_coords, local_chunk_coords) =
|
||||
world_to_chunk_and_local_coords(world_coords);
|
||||
|
||||
|
@ -411,15 +454,15 @@ impl Overworld {
|
|||
chunk.get_tile(local_chunk_coords)
|
||||
}
|
||||
|
||||
/// Return a [`Tile`] at the given world coordinates, or `None` if that tile has not
|
||||
/// been generated yet. use engine/world cordinates.
|
||||
/// Return a [`Tile`] at the given world coordinates. Generates tiles if necessary.
|
||||
/// Uses engine/world cordinates.
|
||||
pub fn get_or_generate_tile(&mut self, world_coords: IVec2) -> &Tile {
|
||||
let mut coords = world_coords;
|
||||
coords.y *= -1;
|
||||
self.get_or_generate_tiles_private(coords)
|
||||
self.get_or_generate_tile_private(coords)
|
||||
}
|
||||
|
||||
fn get_or_generate_tiles_private(&mut self, world_coords: IVec2) -> &Tile {
|
||||
fn get_or_generate_tile_private(&mut self, world_coords: IVec2) -> &Tile {
|
||||
let (chunk_coords, local_chunk_coords) =
|
||||
world_to_chunk_and_local_coords(world_coords);
|
||||
|
||||
|
@ -430,21 +473,21 @@ impl Overworld {
|
|||
chunk.get_tile(local_chunk_coords).unwrap()
|
||||
}
|
||||
|
||||
/// Iterate over all generated tiles and its engine/world cordinates. using a [`Tile`] at the given world coordinates, or `None` if that tile has not
|
||||
/// been generated yet.
|
||||
/// Iterate over all generated tiles in all generated chunks and their engine/world
|
||||
/// cordinates.
|
||||
pub fn iter_tiles(&self) -> impl Iterator<Item = (IVec2, &Tile)> {
|
||||
self.iter_tilese_private().map(|(coords, tile)| {
|
||||
self.iter_tiles_private().map(|(coords, tile)| {
|
||||
let mut w_coords = coords;
|
||||
w_coords.y *= -1;
|
||||
(w_coords, tile)
|
||||
})
|
||||
}
|
||||
|
||||
/// iterate over all tiles and its global coords
|
||||
fn iter_tilese_private(&self) -> impl Iterator<Item = (IVec2, &Tile)> {
|
||||
/// Iterate over all generated tiles in all generated chunks.
|
||||
fn iter_tiles_private(&self) -> impl Iterator<Item = (IVec2, &Tile)> {
|
||||
self.chunks.iter().flat_map(|(chunk_coords, chunk)| {
|
||||
chunk.iter_tiles().map(|(local_coords, tile)| {
|
||||
// never fail because chunksize fits alswas into i32
|
||||
// never fail because chunksize fits always into i32
|
||||
let local_coords: IVec2 = local_coords.try_into().unwrap();
|
||||
(local_coords + (*chunk_coords * CHUNK_SIZE as i32), tile)
|
||||
})
|
||||
|
|
59
src/game.rs
|
@ -3,22 +3,64 @@ use crate::{
|
|||
assets::Assets,
|
||||
State
|
||||
};
|
||||
use comfy::EngineContext;
|
||||
use std::ops::Sub;
|
||||
use comfy::{EngineContext, Vec2};
|
||||
use std::{ops::Sub, time::Instant};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ghost {
|
||||
/// current electric charge of the Ghost
|
||||
/// Current electric charge of the Ghost.
|
||||
pub charge: f32,
|
||||
/// max electric charge of the Ghost
|
||||
pub max_charge: f32
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Ghost {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
charge: 1000.0,
|
||||
max_charge: 1000.0
|
||||
max_charge: 1000.0,
|
||||
|
||||
overworld_pos: Vec2::ZERO,
|
||||
overworld_movement_pending: Vec2::ZERO,
|
||||
overworld_movement_speed: 0.0,
|
||||
overworld_pos_last_update: Instant::now()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,15 +93,16 @@ pub fn setup(state: &mut State, ctx: &mut EngineContext<'_>) {
|
|||
}
|
||||
|
||||
pub fn update(state: &mut State, engine: &mut EngineContext<'_>) {
|
||||
state.score += engine.delta * 10.0;
|
||||
match state.activity {
|
||||
Activity::House => house::update(state, engine),
|
||||
Activity::House(_) => house::update(state, engine),
|
||||
Activity::Overworld => overworld::update(state, engine)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(state: &State, engine: &EngineContext<'_>) {
|
||||
match state.activity {
|
||||
Activity::House => house::draw(state, engine),
|
||||
Activity::House(_) => house::draw(state, engine),
|
||||
Activity::Overworld => overworld::draw(state, engine)
|
||||
}
|
||||
crate::ui::draw(state, engine);
|
||||
|
|
24
src/main.rs
|
@ -16,7 +16,7 @@ use self::{
|
|||
};
|
||||
use comfy::{
|
||||
init_game_config, pollster, run_comfy_main_async, EngineContext, EngineState,
|
||||
GameConfig, GameLoop
|
||||
GameConfig, GameLoop, HashMap, IVec2
|
||||
};
|
||||
|
||||
const GAME_NAME: &str = "Powercreep";
|
||||
|
@ -27,7 +27,27 @@ struct State {
|
|||
activity: Activity,
|
||||
ghost: Ghost,
|
||||
overworld: Overworld,
|
||||
house: Option<HouseState>
|
||||
houses: HashMap<IVec2, HouseState>,
|
||||
score: f32
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn get_house_pos(&self) -> Option<IVec2> {
|
||||
match self.activity {
|
||||
Activity::House(pos) => Some(pos),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn house(&self) -> Option<&HouseState> {
|
||||
self.houses.get(&self.get_house_pos().unwrap())
|
||||
}
|
||||
|
||||
fn house_mut(&mut self) -> &mut HouseState {
|
||||
self.houses
|
||||
.entry(self.get_house_pos().unwrap())
|
||||
.or_insert_with(HouseState::default)
|
||||
}
|
||||
}
|
||||
|
||||
impl GameLoop for State {
|
||||
|
|
26
src/ui.rs
|
@ -1,10 +1,11 @@
|
|||
use crate::State;
|
||||
use comfy::{
|
||||
draw_rect, draw_rect_outline, screen_height, screen_to_world, screen_width,
|
||||
EngineContext, Vec2, BLUE, RED
|
||||
draw_rect, draw_rect_outline, egui, screen_height, screen_to_world, screen_width,
|
||||
EngineContext, Vec2, BLUE, RED, WHITE
|
||||
};
|
||||
use egui::widget_text::RichText;
|
||||
|
||||
pub fn draw(state: &State, _engine: &EngineContext<'_>) {
|
||||
pub fn draw_batterie(state: &State, _engine: &EngineContext<'_>) {
|
||||
// seperate fill state into smaller section for better readability
|
||||
let section_count = 5;
|
||||
let mut start_positon = screen_to_world(Vec2::new(screen_width(), screen_height()));
|
||||
|
@ -29,3 +30,22 @@ pub fn draw(state: &State, _engine: &EngineContext<'_>) {
|
|||
draw_rect_outline(position, section_size, 0.1, RED, 100);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_highscore(state: &State, _engine: &EngineContext<'_>) {
|
||||
egui::Area::new("score")
|
||||
.anchor(egui::Align2::RIGHT_TOP, egui::vec2(0.0, 0.0))
|
||||
.show(egui(), |ui| {
|
||||
ui.label(
|
||||
RichText::new(format!("{:.0}", state.score))
|
||||
.color(WHITE)
|
||||
.monospace()
|
||||
.size(16.0)
|
||||
.strong()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn draw(state: &State, engine: &EngineContext<'_>) {
|
||||
draw_batterie(state, engine);
|
||||
draw_highscore(state, engine);
|
||||
}
|
||||
|
|