Room Creating: Place Kitchen Furniture (#11)
Co-authored-by: Glaeder <niklas@vousten.dev> Co-authored-by: Fredi <fredrik.konrad@rwth-aachen.de> Reviewed-on: #11 Co-authored-by: Dominic <git@msrd0.de> Co-committed-by: Dominic <git@msrd0.de>
1
Cargo.lock
generated
|
@ -2129,6 +2129,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"comfy",
|
"comfy",
|
||||||
"heck",
|
"heck",
|
||||||
|
"indexmap",
|
||||||
"resvg",
|
"resvg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ opt-level = 3
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
comfy = { version = "0.4.0", features = ["wayland"] }
|
comfy = { version = "0.4.0", features = ["wayland"] }
|
||||||
|
indexmap = "2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
heck = "0.5"
|
heck = "0.5"
|
||||||
|
|
BIN
assets/furniture/blender.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/furniture/cupboard.png
Normal file
After Width: | Height: | Size: 338 KiB |
BIN
assets/furniture/dishwasher.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
assets/furniture/drawer.png
Normal file
After Width: | Height: | Size: 338 KiB |
BIN
assets/furniture/drawer_cupboard.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/furniture/dryer.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/furniture/elec/blender.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/furniture/elec/dishwasher.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
assets/furniture/elec/dryer.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
assets/furniture/elec/fridge.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
assets/furniture/elec/kettle.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/elec/minifridge.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/furniture/elec/oven.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
assets/furniture/elec/stove.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/elec/toaster.png
Normal file
After Width: | Height: | Size: 126 KiB |
BIN
assets/furniture/elec/washing_machine.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
assets/furniture/fridge.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
assets/furniture/hanging_cupboard.png
Normal file
After Width: | Height: | Size: 298 KiB |
BIN
assets/furniture/kettle.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
assets/furniture/metal/blender.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/furniture/metal/cupboard.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/furniture/metal/dishwasher.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
assets/furniture/metal/drawer.png
Normal file
After Width: | Height: | Size: 338 KiB |
BIN
assets/furniture/metal/drawer_cupboard.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
assets/furniture/metal/dryer.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/furniture/metal/fridge.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/furniture/metal/hanging_cupboard.png
Normal file
After Width: | Height: | Size: 298 KiB |
BIN
assets/furniture/metal/kettle.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/furniture/metal/minifridge.png
Normal file
After Width: | Height: | Size: 7 KiB |
BIN
assets/furniture/metal/oven.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
assets/furniture/metal/sink.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/furniture/metal/stove.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/metal/toaster.png
Normal file
After Width: | Height: | Size: 126 KiB |
BIN
assets/furniture/metal/washing_machine.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
assets/furniture/minifridge.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
assets/furniture/oven.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
assets/furniture/sideboard_1.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/sideboard_2.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/furniture/sideboard_3.png
Normal file
After Width: | Height: | Size: 298 KiB |
BIN
assets/furniture/sideboard_3.png.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/furniture/sink.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
assets/furniture/stove.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/toaster.png
Normal file
After Width: | Height: | Size: 126 KiB |
BIN
assets/furniture/washing_machine.png
Normal file
After Width: | Height: | Size: 16 KiB |
113
src/activities/house/furniture.rs
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
use comfy::{error, texture_id, EngineContext, HashSet, Lazy, Mutex, TextureHandle};
|
||||||
|
use std::{fmt::Debug, fs, io, sync::Arc};
|
||||||
|
|
||||||
|
static ASSETS_LOADED: Lazy<Arc<Mutex<HashSet<String>>>> =
|
||||||
|
Lazy::new(|| Arc::new(Mutex::new(HashSet::new())));
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FurnitureAsset {
|
||||||
|
folder: String,
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FurnitureTextureHandles {
|
||||||
|
human: Option<TextureHandle>,
|
||||||
|
magnet: Option<TextureHandle>,
|
||||||
|
elec: Option<TextureHandle>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FurnitureAsset {
|
||||||
|
fn asset_path(&self) -> String {
|
||||||
|
format!("{}/{}.png", self.folder, self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn asset_path_magnet(&self) -> String {
|
||||||
|
format!("{}/magnet/{}.png", self.folder, self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn asset_path_elec(&self) -> String {
|
||||||
|
format!("{}/elec/{}.png", self.folder, self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_asset_path(
|
||||||
|
&self,
|
||||||
|
path: String,
|
||||||
|
ctx: &mut EngineContext<'_>
|
||||||
|
) -> Option<TextureHandle> {
|
||||||
|
let mut loaded = ASSETS_LOADED.lock();
|
||||||
|
if loaded.contains(&path) {
|
||||||
|
return Some(texture_id(&path));
|
||||||
|
}
|
||||||
|
let bytes = match fs::read(format!(
|
||||||
|
"{}/assets/furniture/{path}",
|
||||||
|
env!("CARGO_MANIFEST_DIR")
|
||||||
|
)) {
|
||||||
|
Ok(bytes) => bytes,
|
||||||
|
Err(err) if err.kind() == io::ErrorKind::NotFound => return None,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to load asset {path:?}: {err}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctx.load_texture_from_bytes(&path, &bytes);
|
||||||
|
let handle = texture_id(&path);
|
||||||
|
loaded.insert(path);
|
||||||
|
Some(handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to load the assets. Silently ignore missing assets.
|
||||||
|
fn load_assets(&self, ctx: &mut EngineContext<'_>) -> FurnitureTextureHandles {
|
||||||
|
FurnitureTextureHandles {
|
||||||
|
human: self.load_asset_path(self.asset_path(), ctx),
|
||||||
|
magnet: self.load_asset_path(self.asset_path_magnet(), ctx),
|
||||||
|
elec: self.load_asset_path(self.asset_path_elec(), ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Furniture {
|
||||||
|
asset: FurnitureAsset,
|
||||||
|
handles: FurnitureTextureHandles,
|
||||||
|
on: Box<dyn Fn() -> bool>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Furniture {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Furniture")
|
||||||
|
.field("asset", &self.asset)
|
||||||
|
.field("handles", &self.handles)
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Furniture {
|
||||||
|
pub fn new<F: Into<String>, N: Into<String>>(
|
||||||
|
folder: F,
|
||||||
|
name: N,
|
||||||
|
ctx: &mut EngineContext<'_>
|
||||||
|
) -> Self {
|
||||||
|
let asset = FurnitureAsset {
|
||||||
|
folder: folder.into(),
|
||||||
|
name: name.into()
|
||||||
|
};
|
||||||
|
let handles = asset.load_assets(ctx);
|
||||||
|
Self {
|
||||||
|
asset,
|
||||||
|
handles,
|
||||||
|
on: Box::new(|| false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_human_texture_handle(&self) -> Option<TextureHandle> {
|
||||||
|
self.handles.human
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_elec_texture_handle(&self) -> Option<TextureHandle> {
|
||||||
|
self.handles.elec
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_magnet_texture_handle(&self) -> Option<TextureHandle> {
|
||||||
|
self.handles.magnet
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,13 @@ impl Default for Grid {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
|
pub fn new(nodes: Vec<Vec2>, connections: Vec<(usize, usize)>) -> Self {
|
||||||
|
let mut grid = Grid { nodes, connections };
|
||||||
|
grid.sanitize();
|
||||||
|
|
||||||
|
grid
|
||||||
|
}
|
||||||
|
|
||||||
fn load() -> Self {
|
fn load() -> Self {
|
||||||
let mut grid = Self {
|
let mut grid = Self {
|
||||||
nodes: vec![
|
nodes: vec![
|
||||||
|
|
|
@ -1,26 +1,52 @@
|
||||||
|
mod furniture;
|
||||||
mod grid;
|
mod grid;
|
||||||
mod player;
|
mod player;
|
||||||
|
mod room;
|
||||||
|
|
||||||
use grid::Grid;
|
use self::{grid::Grid, player::Player, room::Room};
|
||||||
use player::Player;
|
use crate::State;
|
||||||
|
use comfy::{error, EngineContext};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug)]
|
||||||
pub struct HouseState {
|
pub struct HouseState {
|
||||||
grid: Grid,
|
room: Room,
|
||||||
|
//grid: Grid,
|
||||||
player: Player
|
player: Player
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(state: &crate::State, _engine: &comfy::EngineContext<'_>) {
|
impl HouseState {
|
||||||
if let Some(house) = state.house() {
|
pub fn generate_new_house(ctx: &mut EngineContext<'_>) -> Self {
|
||||||
|
let room = Room::new(ctx);
|
||||||
|
let player = Player::new(&room);
|
||||||
|
HouseState { room, player }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(state: &State, _ctx: &comfy::EngineContext<'_>) {
|
||||||
|
let Some(house) = state.house() else {
|
||||||
|
error!("How can I render a house when I'm not in one?!?");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Draw House
|
||||||
|
house.room.draw();
|
||||||
|
|
||||||
//Draw Grid
|
//Draw Grid
|
||||||
house.grid.draw();
|
//state.house.grid.draw();
|
||||||
|
|
||||||
//Draw Player
|
//Draw Player
|
||||||
house.player.draw();
|
house.player.draw();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(state: &mut crate::State, _engine: &mut comfy::EngineContext<'_>) {
|
pub fn update(state: &mut State, ctx: &mut comfy::EngineContext<'_>) {
|
||||||
let house = state.house_mut();
|
let house = state.house_mut(ctx);
|
||||||
house.player.update(&house.grid);
|
house.player.update(&house.room.grid);
|
||||||
|
|
||||||
|
if house.player.is_moving_to_right_room(&house.room) {
|
||||||
|
house.room = Room::new(ctx);
|
||||||
|
house.player.reset_on_room(&house.room, true);
|
||||||
|
} else if house.player.is_moving_to_left_room(&house.room) {
|
||||||
|
house.room = Room::new(ctx);
|
||||||
|
house.player.reset_on_room(&house.room, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use super::Grid;
|
use super::{
|
||||||
|
room::{Room, SCALE},
|
||||||
|
Grid
|
||||||
|
};
|
||||||
use comfy::{delta, draw_circle, is_key_down, vec2, KeyCode, Vec2, RED};
|
use comfy::{delta, draw_circle, is_key_down, vec2, KeyCode, Vec2, RED};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
@ -10,20 +13,23 @@ pub struct Player {
|
||||||
next_connections: Vec<usize>
|
next_connections: Vec<usize>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Player {
|
impl Player {
|
||||||
fn default() -> Self {
|
pub fn new(room: &Room) -> Self {
|
||||||
Self {
|
Player {
|
||||||
position: Default::default(),
|
position: vec2(
|
||||||
|
((0.25) - room.size.0 as f32 / 2.0) * SCALE,
|
||||||
|
room.grid.nodes.first().unwrap().y
|
||||||
|
),
|
||||||
speed: 10.0,
|
speed: 10.0,
|
||||||
connection: 0,
|
connection: 0,
|
||||||
next_connections: vec![1, 2, 3]
|
next_connections: vec![1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
impl Player {
|
|
||||||
pub fn draw(&self) {
|
pub fn draw(&self) {
|
||||||
draw_circle(self.position, 0.5, RED, 0);
|
draw_circle(self.position, 0.5, RED, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, grid: &Grid) {
|
pub fn update(&mut self, grid: &Grid) {
|
||||||
let allowed_movement = get_allowed_movement(self, grid);
|
let allowed_movement = get_allowed_movement(self, grid);
|
||||||
|
|
||||||
|
@ -33,6 +39,27 @@ impl Player {
|
||||||
snap_to_closest_node(self, grid);
|
snap_to_closest_node(self, grid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_moving_to_right_room(&self, room: &Room) -> bool {
|
||||||
|
self.position.x > (room.size.0 as f32 / 2.0) * SCALE
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_moving_to_left_room(&self, room: &Room) -> bool {
|
||||||
|
self.position.x < -(room.size.0 as f32 / 2.0) * SCALE
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_on_room(&mut self, room: &Room, place_left: bool) {
|
||||||
|
let offset = 0.1;
|
||||||
|
let x = if place_left {
|
||||||
|
(offset - room.size.0 as f32 / 2.0) * SCALE
|
||||||
|
} else {
|
||||||
|
(room.size.0 as f32 / 2.0 - offset) * SCALE
|
||||||
|
};
|
||||||
|
|
||||||
|
self.position = vec2(x, room.grid.nodes.first().unwrap().y);
|
||||||
|
self.connection = 0;
|
||||||
|
self.next_connections = vec![1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_player(player: &mut Player, allowed_movement: (bool, bool, bool, bool)) {
|
fn move_player(player: &mut Player, allowed_movement: (bool, bool, bool, bool)) {
|
||||||
|
|
355
src/activities/house/room.rs
Normal file
|
@ -0,0 +1,355 @@
|
||||||
|
use super::{furniture::Furniture, grid::Grid};
|
||||||
|
use crate::game;
|
||||||
|
use comfy::{
|
||||||
|
draw_rect, draw_rect_outline, draw_sprite, error, random_i32, vec2, EngineContext,
|
||||||
|
HashSet, RandomRange as _, Vec2, GREEN, PURPLE, RED, WHITE
|
||||||
|
};
|
||||||
|
use indexmap::IndexSet;
|
||||||
|
|
||||||
|
pub const SCALE: f32 = 4.0;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum RoomType {
|
||||||
|
Kitchen,
|
||||||
|
Bath,
|
||||||
|
Toilett,
|
||||||
|
LivingRoom,
|
||||||
|
SleepingRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Tile {
|
||||||
|
pos: Vec2,
|
||||||
|
size: Vec2,
|
||||||
|
f: Furniture,
|
||||||
|
z: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Room {
|
||||||
|
_room_type: RoomType,
|
||||||
|
pub size: (u8, u8), //(width, height)
|
||||||
|
pub grid: Grid,
|
||||||
|
furnitures: Vec<Tile>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RoomType {
|
||||||
|
pub fn random() -> Self {
|
||||||
|
match random_i32(0, 4) {
|
||||||
|
0 => RoomType::Kitchen,
|
||||||
|
1 => RoomType::Bath,
|
||||||
|
2 => RoomType::Toilett,
|
||||||
|
3 => RoomType::LivingRoom,
|
||||||
|
4 => RoomType::SleepingRoom,
|
||||||
|
_ => panic!("Somehow you where unlucky and got a random number out of range")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Room {
|
||||||
|
pub fn new(ctx: &mut EngineContext<'_>) -> Self {
|
||||||
|
let room_type = RoomType::random();
|
||||||
|
let size = Self::random_size(&room_type);
|
||||||
|
|
||||||
|
let furnitures = Self::random_room_furniture(&room_type, size, ctx);
|
||||||
|
|
||||||
|
Room {
|
||||||
|
_room_type: room_type,
|
||||||
|
size,
|
||||||
|
grid: Self::create_grid(size.0, size.1),
|
||||||
|
furnitures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_size(room_type: &RoomType) -> (u8, u8) {
|
||||||
|
//Kitchen + Living Room 5-8
|
||||||
|
//Bath + sleepingroom 4-6
|
||||||
|
//Toilet 2-3
|
||||||
|
match room_type {
|
||||||
|
RoomType::Kitchen | RoomType::LivingRoom => (random_i32(5, 8) as u8, 3),
|
||||||
|
RoomType::Bath | RoomType::SleepingRoom => (random_i32(4, 6) as u8, 3),
|
||||||
|
RoomType::Toilett => (random_i32(2, 3) as u8, 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_room_furniture(
|
||||||
|
room_type: &RoomType,
|
||||||
|
(width, _height): (u8, u8),
|
||||||
|
ctx: &mut EngineContext<'_>
|
||||||
|
) -> Vec<Tile> {
|
||||||
|
let mut furnitures = Vec::new();
|
||||||
|
|
||||||
|
let mut empty_spots: IndexSet<u8> = (0 .. width).collect();
|
||||||
|
|
||||||
|
fn random_empty_spot<T>(empty_spots: &mut IndexSet<T>) -> Option<T> {
|
||||||
|
if empty_spots.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let random_idx = usize::gen_range(0, empty_spots.len());
|
||||||
|
empty_spots.swap_remove_index(random_idx)
|
||||||
|
}
|
||||||
|
fn random_appliance<T>(empty_spots: &mut Vec<T>) -> Option<T> {
|
||||||
|
if empty_spots.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let random_idx = usize::gen_range(0, empty_spots.len());
|
||||||
|
Some(empty_spots.swap_remove(random_idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIDEBOARD_HEIGHT: f32 = 0.1;
|
||||||
|
const STOVE_HEIGHT: f32 = 0.025;
|
||||||
|
const SINK_HEIGHT: f32 = 0.5;
|
||||||
|
|
||||||
|
#[allow(clippy::single_match)] // we'll add more stuff later
|
||||||
|
match room_type {
|
||||||
|
RoomType::Kitchen => {
|
||||||
|
// in a kitchen, we always add a fridge
|
||||||
|
let fridge_pos = u8::gen_range(0, 2) * (width - 1);
|
||||||
|
empty_spots.swap_remove(&fridge_pos);
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(fridge_pos as f32, 0.0),
|
||||||
|
size: vec2(1.0, 2.0),
|
||||||
|
f: Furniture::new("kitchen", "fridge", ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// and we always add an oven
|
||||||
|
let Some(oven_pos) = random_empty_spot(&mut empty_spots) else {
|
||||||
|
error!("How can I not fit an oven in a kitchen?!?");
|
||||||
|
return furnitures;
|
||||||
|
};
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(oven_pos as f32, 0.0),
|
||||||
|
size: vec2(1.0, 1.0),
|
||||||
|
f: Furniture::new("kitchen", "oven", ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// there's always sideboard above the oven with a stove
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(oven_pos as f32, 1.0),
|
||||||
|
size: vec2(1.0, SIDEBOARD_HEIGHT),
|
||||||
|
f: Furniture::new("kitchen", "sideboard_1", ctx),
|
||||||
|
z: 1
|
||||||
|
});
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(oven_pos as f32, 1.0 + SIDEBOARD_HEIGHT),
|
||||||
|
size: vec2(1.0, STOVE_HEIGHT),
|
||||||
|
f: Furniture::new("kitchen", "stove", ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// and we always add a drawer that houses a sink
|
||||||
|
let Some(sink_pos) = random_empty_spot(&mut empty_spots) else {
|
||||||
|
error!("How can I not fit a sink in a kitchen?!?");
|
||||||
|
return furnitures;
|
||||||
|
};
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(sink_pos as f32, 0.0),
|
||||||
|
size: vec2(1.0, 1.0),
|
||||||
|
f: Furniture::new("kitchen", "drawer_cupboard", ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// there's always sideboard above that drawer with a sink **behind** it
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(sink_pos as f32, 1.0),
|
||||||
|
size: vec2(1.0, SINK_HEIGHT),
|
||||||
|
f: Furniture::new("kitchen", "sink", ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(sink_pos as f32, 1.0),
|
||||||
|
size: vec2(1.0, SIDEBOARD_HEIGHT),
|
||||||
|
f: Furniture::new("kitchen", "sideboard_1", ctx),
|
||||||
|
z: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
// the current list of empty spots is the same list we can use to place
|
||||||
|
// on-the-counter appliances later
|
||||||
|
let mut empty_spots_clone = empty_spots.clone();
|
||||||
|
|
||||||
|
// build a list of the remaining kitchen appliances. we only want them
|
||||||
|
// included once, most kitchens don't contain two washing machines etc
|
||||||
|
let mut remaining_appliances: IndexSet<&'static str> =
|
||||||
|
["dishwasher", "dryer", "minifridge", "washing_machine"]
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// let's add at most half of the remaining positions as big appliances
|
||||||
|
for _ in 0 .. empty_spots.len() / 2 {
|
||||||
|
let Some(asset) = random_empty_spot(&mut remaining_appliances) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let Some(spot) = random_empty_spot(&mut empty_spots) else {
|
||||||
|
error!("WTF I shouldn't've used more than half of the available spots");
|
||||||
|
return furnitures;
|
||||||
|
};
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(spot as f32, 0.0),
|
||||||
|
size: vec2(1.0, 1.0),
|
||||||
|
f: Furniture::new("kitchen", asset, ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(spot as f32, 1.0),
|
||||||
|
size: vec2(1.0, SIDEBOARD_HEIGHT),
|
||||||
|
f: Furniture::new("kitchen", "sideboard_1", ctx),
|
||||||
|
z: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// and fill the remainder with drawers
|
||||||
|
while !empty_spots.is_empty() {
|
||||||
|
let asset = match u8::gen_range(0, 2) {
|
||||||
|
0 => "drawer",
|
||||||
|
1 => "drawer_cupboard",
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
let Some(spot) = random_empty_spot(&mut empty_spots) else {
|
||||||
|
error!("WTF I should still have spots available");
|
||||||
|
return furnitures;
|
||||||
|
};
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(spot as f32, 0.0),
|
||||||
|
size: vec2(1.0, 1.0),
|
||||||
|
f: Furniture::new("kitchen", asset, ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(spot as f32, 1.0),
|
||||||
|
size: vec2(1.0, SIDEBOARD_HEIGHT),
|
||||||
|
f: Furniture::new("kitchen", "sideboard_1", ctx),
|
||||||
|
z: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a list of on-the-counter kitchen appliances. we only want them
|
||||||
|
// included once, most kitchens don't contain two toasters etc
|
||||||
|
let mut remaining_appliances: Vec<(&'static str, f32, f32)> = [
|
||||||
|
("blender", 0.3, 0.45),
|
||||||
|
("kettle", 0.3, 0.4),
|
||||||
|
("toaster", 0.5, 0.25)
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// and then we fill like half the counter with appliances
|
||||||
|
for _ in 0 .. empty_spots_clone.len() / 2 {
|
||||||
|
let Some((asset, asset_w, asset_h)) =
|
||||||
|
random_appliance(&mut remaining_appliances)
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let Some(spot) = random_empty_spot(&mut empty_spots_clone) else {
|
||||||
|
error!("WTF I shouldn't've used more than half of the available spots");
|
||||||
|
return furnitures;
|
||||||
|
};
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(spot as f32 + 0.5, 1.0 + SIDEBOARD_HEIGHT),
|
||||||
|
size: vec2(asset_w, asset_h),
|
||||||
|
f: Furniture::new("kitchen", asset, ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
furnitures
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_grid(width: u8, height: u8) -> Grid {
|
||||||
|
error!("START GRID CREATION!");
|
||||||
|
let left_border = width as f32 / 2.0;
|
||||||
|
let lower_border = height as f32 / 2.0;
|
||||||
|
|
||||||
|
//Lower Cable
|
||||||
|
let lower_cable_y = height as f32 / 6.0;
|
||||||
|
let mut current_x = -0.5;
|
||||||
|
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
let max_offset = ((width / 2) as i32).max(1);
|
||||||
|
|
||||||
|
while current_x < width as f32 {
|
||||||
|
nodes.push(vec2(
|
||||||
|
(current_x - left_border) * SCALE,
|
||||||
|
(lower_cable_y - lower_border) * SCALE
|
||||||
|
));
|
||||||
|
current_x += random_i32(1, max_offset) as f32;
|
||||||
|
}
|
||||||
|
current_x = width as f32 + 0.5;
|
||||||
|
nodes.push(vec2(
|
||||||
|
(current_x - left_border) * SCALE,
|
||||||
|
(lower_cable_y - lower_border) * SCALE
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut connections = Vec::new();
|
||||||
|
for i in 1 .. nodes.len() {
|
||||||
|
connections.push((i - 1, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Lamps
|
||||||
|
let upper_cable_y = height as f32 - 0.25;
|
||||||
|
let max_lamps = (width as f32 / 2.5).round() as i32;
|
||||||
|
let lamp_amount = random_i32(1, max_lamps + 1);
|
||||||
|
let node_indices: HashSet<usize> = (0 .. lamp_amount)
|
||||||
|
.map(|_| random_i32(1, nodes.len() as i32 - 1) as usize)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let last_lower_node_index = nodes.len();
|
||||||
|
for (i, index) in node_indices.iter().enumerate() {
|
||||||
|
nodes.push(vec2(
|
||||||
|
nodes.get(*index).unwrap().x,
|
||||||
|
(upper_cable_y - lower_border) * SCALE
|
||||||
|
));
|
||||||
|
connections.push((*index, last_lower_node_index + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
Grid::new(nodes, connections)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self) {
|
||||||
|
let (width, height) = self.size;
|
||||||
|
|
||||||
|
draw_rect(
|
||||||
|
vec2(0.0, 0.0),
|
||||||
|
vec2(width as f32 * SCALE, height as f32 * SCALE),
|
||||||
|
PURPLE,
|
||||||
|
game::ZLayer::MapMax as i32 - 2
|
||||||
|
);
|
||||||
|
draw_rect_outline(
|
||||||
|
vec2(0.0, 0.0),
|
||||||
|
vec2(width as f32 * SCALE, height as f32 * SCALE),
|
||||||
|
0.3,
|
||||||
|
RED,
|
||||||
|
game::ZLayer::MapMax as i32 - 1
|
||||||
|
);
|
||||||
|
|
||||||
|
for tile in &self.furnitures {
|
||||||
|
let mut pos = tile.pos - vec2(width as f32 / 2.0, height as f32 / 2.0);
|
||||||
|
pos += tile.size * 0.5;
|
||||||
|
|
||||||
|
if let Some(texture) = tile.f.get_human_texture_handle() {
|
||||||
|
draw_sprite(
|
||||||
|
texture,
|
||||||
|
pos * SCALE,
|
||||||
|
WHITE,
|
||||||
|
game::ZLayer::MapMax as i32 + tile.z,
|
||||||
|
tile.size * SCALE
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draw_rect_outline(
|
||||||
|
pos * SCALE,
|
||||||
|
tile.size * SCALE,
|
||||||
|
0.3,
|
||||||
|
GREEN,
|
||||||
|
game::ZLayer::MapMax as i32 + tile.z
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.grid.draw();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,4 @@
|
||||||
use crate::{
|
use crate::{activities::Activity, game::ZLayer, State};
|
||||||
activities::{
|
|
||||||
house::{self, HouseState},
|
|
||||||
Activity
|
|
||||||
},
|
|
||||||
game::ZLayer,
|
|
||||||
State
|
|
||||||
};
|
|
||||||
use comfy::{
|
use comfy::{
|
||||||
draw_circle, draw_rect_outline, draw_sprite, error, info, is_key_down,
|
draw_circle, draw_rect_outline, draw_sprite, error, info, is_key_down,
|
||||||
main_camera_mut, EngineContext, IVec2, KeyCode, Vec2, RED, WHITE
|
main_camera_mut, EngineContext, IVec2, KeyCode, Vec2, RED, WHITE
|
||||||
|
@ -32,7 +25,7 @@ pub fn draw(state: &crate::State, _engine: &comfy::EngineContext<'_>) {
|
||||||
draw_circle(state.ghost.overworld_pos, 0.5, RED, ZLayer::Ghost.into());
|
draw_circle(state.ghost.overworld_pos, 0.5, RED, ZLayer::Ghost.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_move_player(state: &mut State) {
|
fn update_move_player(state: &mut State, ctx: &mut EngineContext<'_>) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
|
||||||
// Are there any pending position updates? If so, we ignore all user input and execute
|
// Are there any pending position updates? If so, we ignore all user input and execute
|
||||||
|
@ -160,18 +153,18 @@ fn update_move_player(state: &mut State) {
|
||||||
if current_tile.can_enter_house() {
|
if current_tile.can_enter_house() {
|
||||||
info!("enter house at {tile_pos}");
|
info!("enter house at {tile_pos}");
|
||||||
state.activity = Activity::House(tile_pos);
|
state.activity = Activity::House(tile_pos);
|
||||||
state.house_mut(); // gen new house
|
state.house_mut(ctx); // gen new house
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(state: &mut State, _ctx: &mut EngineContext<'_>) {
|
pub fn update(state: &mut State, ctx: &mut EngineContext<'_>) {
|
||||||
let mut camera = main_camera_mut();
|
let mut camera = main_camera_mut();
|
||||||
camera.center = Vec2::ZERO;
|
camera.center = Vec2::ZERO;
|
||||||
camera.zoom = 30.0;
|
camera.zoom = 30.0;
|
||||||
|
|
||||||
// move player
|
// move player
|
||||||
update_move_player(state);
|
update_move_player(state, ctx);
|
||||||
|
|
||||||
// generate more chunks if needed
|
// generate more chunks if needed
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{house, overworld, Activity},
|
activities::{house, overworld, Activity},
|
||||||
|
assets::Assets,
|
||||||
State
|
State
|
||||||
};
|
};
|
||||||
use comfy::{EngineContext, Vec2};
|
use comfy::{EngineContext, Vec2};
|
||||||
|
@ -85,6 +86,10 @@ impl Sub<i32> for ZLayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setup(_state: &mut State, ctx: &mut EngineContext<'_>) {
|
||||||
|
Assets::load(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update(state: &mut State, engine: &mut EngineContext<'_>) {
|
pub fn update(state: &mut State, engine: &mut EngineContext<'_>) {
|
||||||
state.score += engine.delta * 10.0;
|
state.score += engine.delta * 10.0;
|
||||||
match state.activity {
|
match state.activity {
|
||||||
|
|
19
src/main.rs
|
@ -12,7 +12,6 @@ mod ui;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
activities::{house::HouseState, overworld::worldgen::Overworld, Activity},
|
activities::{house::HouseState, overworld::worldgen::Overworld, Activity},
|
||||||
assets::Assets,
|
|
||||||
game::Ghost
|
game::Ghost
|
||||||
};
|
};
|
||||||
use comfy::{
|
use comfy::{
|
||||||
|
@ -41,13 +40,13 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn house(&self) -> Option<&HouseState> {
|
fn house(&self) -> Option<&HouseState> {
|
||||||
self.houses.get(&self.get_house_pos().unwrap())
|
self.houses.get(&self.get_house_pos()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn house_mut(&mut self) -> &mut HouseState {
|
fn house_mut(&mut self, ctx: &mut EngineContext<'_>) -> &mut HouseState {
|
||||||
self.houses
|
self.houses
|
||||||
.entry(self.get_house_pos().unwrap())
|
.entry(self.get_house_pos().unwrap())
|
||||||
.or_insert_with(HouseState::default)
|
.or_insert_with(|| HouseState::generate_new_house(ctx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,14 +55,14 @@ impl GameLoop for State {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, engine: &mut EngineContext<'_>) {
|
fn update(&mut self, ctx: &mut EngineContext<'_>) {
|
||||||
if !self.setup_called {
|
if !self.setup_called {
|
||||||
setup(engine);
|
game::setup(self, ctx);
|
||||||
self.setup_called = true;
|
self.setup_called = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
game::update(self, engine);
|
game::update(self, ctx);
|
||||||
game::draw(self, engine);
|
game::draw(self, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,10 +70,6 @@ fn config(config: GameConfig) -> GameConfig {
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(ctx: &mut EngineContext<'_>) {
|
|
||||||
Assets::load(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run() {
|
async fn run() {
|
||||||
init_game_config(GAME_NAME.to_string(), env!("CARGO_PKG_VERSION"), config);
|
init_game_config(GAME_NAME.to_string(), env!("CARGO_PKG_VERSION"), config);
|
||||||
let mut engine = EngineState::new();
|
let mut engine = EngineState::new();
|
||||||
|
|