turtlegame/src/activities/house/room.rs

357 lines
9.3 KiB
Rust
Raw Normal View History

2024-07-07 12:33:22 +02:00
use super::{furniture::Furniture, grid::Grid};
use crate::game;
use comfy::{
2024-07-07 12:33:22 +02:00
draw_rect, draw_rect_outline, draw_sprite, error, random_i32, vec2, EngineContext,
HashSet, RandomRange as _, Vec2, GREEN, PURPLE, RED, WHITE
};
2024-07-07 12:33:22 +02:00
use indexmap::IndexSet;
2024-07-07 00:01:46 +02:00
pub const SCALE: f32 = 4.0;
#[derive(Debug, PartialEq)]
2024-07-07 00:01:46 +02:00
enum RoomType {
Kitchen,
Bath,
Toilett,
LivingRoom,
SleepingRoom
}
2024-07-07 12:43:33 +02:00
#[derive(Debug)]
struct Tile {
pos: Vec2,
size: Vec2,
f: Furniture,
z: i32
}
2024-07-07 11:50:35 +02:00
2024-07-07 00:01:46 +02:00
#[derive(Debug)]
pub struct Room {
room_type: RoomType,
2024-07-07 00:48:37 +02:00
pub size: (u8, u8), //(width, height)
pub grid: Grid,
2024-07-07 12:33:22 +02:00
furnitures: Vec<Tile>
2024-07-07 00:01:46 +02:00
}
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 {
2024-07-07 00:01:46 +02:00
let room_type = RoomType::random();
let size = Self::random_size(&room_type);
2024-07-07 12:33:22 +02:00
let furnitures = Self::random_room_furniture(&room_type, size, ctx);
Room {
room_type,
size,
grid: Self::create_grid(size.0, size.1),
furnitures
}
}
2024-07-07 00:01:46 +02:00
fn random_size(room_type: &RoomType) -> (u8, u8) {
2024-07-07 11:50:35 +02:00
//Kitchen + Living Room 5-8
//Bath + sleepingroom 4-6
//Toilet 2-3
2024-07-07 00:01:46 +02:00
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)
2024-07-07 00:01:46 +02:00
}
}
2024-07-07 12:33:22 +02:00
fn random_room_furniture(
room_type: &RoomType,
(width, _height): (u8, u8),
ctx: &mut EngineContext<'_>
) -> Vec<Tile> {
2024-07-07 11:50:35 +02:00
let mut furnitures = Vec::new();
2024-07-07 12:33:22 +02:00
let mut empty_spots: IndexSet<u8> = (0 .. width).collect();
let mut assets_used: HashSet<&'static str> = HashSet::new();
2024-07-07 13:35:48 +02:00
fn random_empty_spot<T>(empty_spots: &mut IndexSet<T>) -> Option<T> {
2024-07-07 12:33:22 +02:00
if empty_spots.is_empty() {
return None;
}
let random_idx = usize::gen_range(0, empty_spots.len());
empty_spots.swap_remove_index(random_idx)
}
2024-07-07 13:35:48 +02:00
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))
}
2024-07-07 12:33:22 +02:00
const SIDEBOARD_HEIGHT: f32 = 0.1;
const STOVE_HEIGHT: f32 = 0.025;
const SINK_HEIGHT: f32 = 0.5;
2024-07-07 13:40:07 +02:00
#[allow(clippy::single_match)] // we'll add more stuff later
2024-07-07 11:50:35 +02:00
match room_type {
2024-07-07 13:40:07 +02:00
RoomType::Kitchen => {
2024-07-07 12:33:22 +02:00
// in a kitchen, we always add a fridge
let fridge_pos = u8::gen_range(0, 2) * (width - 1);
empty_spots.swap_remove(&fridge_pos);
2024-07-07 12:43:33 +02:00
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
});
2024-07-07 12:33:22 +02:00
// 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;
};
2024-07-07 12:43:33 +02:00
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
});
2024-07-07 12:33:22 +02:00
// there's always sideboard above the oven with a stove
2024-07-07 12:43:33 +02:00
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
});
2024-07-07 12:33:22 +02:00
// 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;
};
2024-07-07 12:43:33 +02:00
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
});
2024-07-07 12:33:22 +02:00
// there's always sideboard above that drawer with a sink **behind** it
2024-07-07 12:43:33 +02:00
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
});
2024-07-07 12:33:22 +02:00
2024-07-07 13:35:48 +02:00
// 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
2024-07-07 12:33:22 +02:00
for _ in 0 .. empty_spots.len() / 2 {
2024-07-07 13:35:48 +02:00
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() {
2024-07-07 12:33:22 +02:00
let asset = match u8::gen_range(0, 2) {
0 => "drawer",
1 => "drawer_cupboard",
_ => unreachable!()
};
let Some(spot) = random_empty_spot(&mut empty_spots) else {
2024-07-07 13:35:48 +02:00
error!("WTF I should still have spots available");
2024-07-07 12:33:22 +02:00
return furnitures;
};
2024-07-07 12:43:33 +02:00
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
});
2024-07-07 12:33:22 +02:00
}
2024-07-07 13:35:48 +02:00
// 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
});
}
2024-07-07 12:33:22 +02:00
},
2024-07-07 11:50:35 +02:00
_ => {}
}
furnitures
}
fn create_grid(width: u8, height: u8) -> Grid {
2024-07-07 00:48:37 +02:00
error!("START GRID CREATION!");
2024-07-07 00:01:46 +02:00
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();
2024-07-07 00:48:37 +02:00
let max_offset = ((width / 2) as i32).max(1);
2024-07-07 00:01:46 +02:00
while current_x < width as f32 {
nodes.push(vec2(
(current_x - left_border) * SCALE,
(lower_cable_y - lower_border) * SCALE
));
2024-07-07 00:48:37 +02:00
current_x += random_i32(1, max_offset) as f32;
2024-07-07 00:01:46 +02:00
}
current_x = width as f32 + 0.5;
nodes.push(vec2(
(current_x - left_border) * SCALE,
(lower_cable_y - lower_border) * SCALE
));
2024-07-07 00:01:46 +02:00
let mut connections = Vec::new();
for i in 1 .. nodes.len() {
connections.push((i - 1, i));
2024-07-07 00:01:46 +02:00
}
//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();
2024-07-07 00:01:46 +02:00
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
));
2024-07-07 00:01:46 +02:00
connections.push((*index, last_lower_node_index + i));
}
Grid::new(nodes, connections)
}
pub fn draw(&self) {
let (width, height) = self.size;
2024-07-07 12:33:22 +02:00
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,
2024-07-07 12:33:22 +02:00
game::ZLayer::MapMax as i32 - 1
);
2024-07-07 12:43:33 +02:00
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;
2024-07-07 12:43:33 +02:00
if let Some(texture) = tile.f.get_human_texture_handle() {
draw_sprite(
texture,
pos * SCALE,
WHITE,
2024-07-07 12:43:33 +02:00
game::ZLayer::MapMax as i32 + tile.z,
tile.size * SCALE
);
} else {
draw_rect_outline(
pos * SCALE,
2024-07-07 12:43:33 +02:00
tile.size * SCALE,
0.3,
GREEN,
2024-07-07 12:43:33 +02:00
game::ZLayer::MapMax as i32 + tile.z
);
}
}
2024-07-07 00:01:46 +02:00
self.grid.draw();
}
}