From da6cb22b373ea2abd421db8cb20a24a8e89c90e0 Mon Sep 17 00:00:00 2001 From: William Quintal Date: Thu, 2 May 2024 13:36:17 -0400 Subject: [PATCH] add release pipeline --- .github/workflows/publish.yaml | 51 +++ .gitignore | 5 +- README.md | 5 + build | 37 ++ crates/map/src/generation/config.rs | 10 +- crates/map/src/generation/context.rs | 71 ++- crates/map/src/generation/entity/door.rs | 3 +- .../map/src/generation/entity/location/mod.rs | 17 +- crates/map/src/generation/entity/mod.rs | 3 +- crates/map/src/generation/entity/window.rs | 3 +- crates/map/src/generation/imp/basic.rs | 186 +++++--- crates/map/src/generation/imp/mod.rs | 5 +- crates/map/src/generation/mod.rs | 42 +- crates/map/src/generation/position.rs | 5 +- crates/map/src/generation/room.rs | 83 ++-- crates/map/src/ldtk/generation.rs | 411 ++++++++++++------ crates/map/src/ldtk/loader/mod.rs | 47 +- crates/map/src/ldtk/map_const.rs | 4 +- crates/map/src/ldtk/mod.rs | 2 +- crates/map/src/lib.rs | 2 +- crates/utils/Cargo.toml | 6 +- crates/utils/src/camera/mod.rs | 2 +- crates/utils/src/camera/tod.rs | 5 +- crates/utils/src/lib.rs | 1 + crates/utils/src/web/mod.rs | 26 ++ examples/map_generation.rs | 30 +- examples/map_preview.rs | 69 +-- website/game.html | 30 ++ website/index.html | 14 + 29 files changed, 767 insertions(+), 408 deletions(-) create mode 100644 .github/workflows/publish.yaml create mode 100644 README.md create mode 100755 build create mode 100644 crates/utils/src/web/mod.rs create mode 100644 website/game.html create mode 100644 website/index.html diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..7309ccf --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,51 @@ +name: Publish to GitHub Pages + +on: + push: + branches: + - main + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v1 + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo build + uses: actions/cache@v1 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} + #- uses: actions-rs/toolchain@v1 + # with: + # toolchain: nightly-2022-04-13 + # override: true + - name: Generate website + run: ./build install_web_dependency && ./build build_web_release + - name: Publish generated content to GitHub Pages + uses: tsunematsu21/actions-publish-gh-pages@v1.0.2 + with: + dir: website/ + branch: gh-pages + token: ${{ secrets.ACCESS_TOKEN }} + #release_dev: + # runs-on: macos-latest + # steps: + # - uses: actions/checkout@v2 + # - name: rustup + # run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && rustup default nightly-2022-04-13 && cargo install --force cargo-make && cd game && cargo make build-native + # - name: Create release folder + # run: mkdir -p release && cp ./game/target/debug/cod-zombie-2d-clone ./release + # - uses: "marvinpinto/action-automatic-releases@latest" + # with: + # repo_token: "${{ secrets.GITHUB_TOKEN }}" + # automatic_release_tag: "latest" + # prerelease: true + # title: "Development Build macos" + # files: | + # ./release/* \ No newline at end of file diff --git a/.gitignore b/.gitignore index 24993fc..e01d4d3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,7 @@ **/public/*.wasm **/public/assets/ *generated.ldtk -Cargo.lock \ No newline at end of file +Cargo.lock + +website/wasm* +website/assets \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..42a059a --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Zombie roguelike + +The goal is to create an open-source 2d cod zombie with roguelike mechanics + +* [game in web browser](https://berlingoqc.github.io/public/cod-zombie-2d-clone/) \ No newline at end of file diff --git a/build b/build new file mode 100755 index 0000000..10fe2fd --- /dev/null +++ b/build @@ -0,0 +1,37 @@ +#! /bin/bash + + +CARGO_TARGET=./target +CARGO_WEB_TARGET=./target_wasm32 + + +function install_web_dependency { + export CARGO_TARGET_DIR=$CARGO_WEB_TARGET + + rustup target add wasm32-unknown-unknown + cargo install wasm-bindgen-cli +} + +function build_web { + export CARGO_TARGET_DIR=$CARGO_WEB_TARGET + + cargo build --example map_preview --target wasm32-unknown-unknown --features bevy_ecs_tilemap/atlas + wasm-bindgen --out-dir ./website/ --out-name wasm --target web $CARGO_WEB_TARGET/wasm32-unknown-unknown/debug/examples/map_preview.wasm + + cp -r ./assets ./website/ +} + +function build_web_release { + export CARGO_TARGET_DIR=$CARGO_WEB_TARGET + + cargo build --example map_preview --target wasm32-unknown-unknown --features bevy_ecs_tilemap/atlas --release + wasm-bindgen --out-dir ./website/ --out-name wasm --target web $CARGO_WEB_TARGET/wasm32-unknown-unknown/release/examples/map_preview.wasm + + cp -r ./assets ./website/ +} + + + + +command=$1; shift; +$command "$@" \ No newline at end of file diff --git a/crates/map/src/generation/config.rs b/crates/map/src/generation/config.rs index d59cc2d..bf42bcc 100644 --- a/crates/map/src/generation/config.rs +++ b/crates/map/src/generation/config.rs @@ -1,8 +1,7 @@ - use std::ops::RangeInclusive; use bevy::prelude::Resource; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum MapGenerationMode { @@ -11,7 +10,6 @@ pub enum MapGenerationMode { #[derive(Debug, Resource, Serialize, Deserialize)] pub struct MapGenerationConfig { - pub map_path: String, pub seed: i32, @@ -26,18 +24,17 @@ pub struct MapGenerationConfig { impl Default for MapGenerationConfig { fn default() -> Self { - Self { + Self { seed: 1, max_width: 1000, max_heigth: 1000, map_path: "".into(), max_room: 10, - mode: MapGenerationMode::Basic + mode: MapGenerationMode::Basic, } } } - impl MapGenerationConfig { pub fn get_range_x(&self, my_size: i32) -> RangeInclusive { -self.max_width..=(self.max_width - my_size) @@ -46,5 +43,4 @@ impl MapGenerationConfig { pub fn get_range_y(&self, my_size: i32) -> RangeInclusive { -self.max_heigth..=(self.max_heigth - my_size) } - } diff --git a/crates/map/src/generation/context.rs b/crates/map/src/generation/context.rs index 0700047..01f5d19 100644 --- a/crates/map/src/generation/context.rs +++ b/crates/map/src/generation/context.rs @@ -1,4 +1,3 @@ - use super::{config::MapGenerationConfig, entity::location::EntityLocations}; use std::{fmt::Display, rc::Rc, usize}; @@ -7,21 +6,19 @@ use std::{fmt::Display, rc::Rc, usize}; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Side { - N, - S, - W, - E, + N, + S, + W, + E, } impl Side { - - pub fn get_opposite(&self) -> Self { match self { Side::N => Side::S, Side::S => Side::N, Side::W => Side::E, - Side::E => Side::W + Side::E => Side::W, } } @@ -30,7 +27,7 @@ impl Side { Side::N => "n", Side::E => "e", Side::S => "s", - Side::W => "w" + Side::W => "w", } } @@ -38,17 +35,14 @@ impl Side { match self { Side::N | Side::W => -1, Side::S | Side::E => 1, - } } pub fn is_opposite(&self, other: Side) -> bool { other == self.get_opposite() } - } - #[derive(Clone, Debug, PartialEq)] pub enum LevelType { Spawn, @@ -61,32 +55,31 @@ pub struct Connection { pub size: usize, pub side: Side, pub starting_at: usize, - + //pub level_iid: String, pub level_id: String, pub compatiable_levels: Vec<(String, usize)>, - //pub to: Option, } impl Display for Connection { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "side={:?} starting_at={} size={}", self.size, self.starting_at, self.size) + write!( + f, + "side={:?} starting_at={} size={}", + self.size, self.starting_at, self.size + ) } } impl Connection { - fn are_matching(&self, other: &Connection) -> bool { - self.side.is_opposite(other.side) && - self.starting_at == other.starting_at && - self.size == other.size + self.side.is_opposite(other.side) + && self.starting_at == other.starting_at + && self.size == other.size } } - - #[derive(Debug, Clone)] pub struct AvailableLevel { // identifier of the original level @@ -105,12 +98,11 @@ pub struct AvailableLevel { pub connections: Vec, - pub entity_locations: EntityLocations + pub entity_locations: EntityLocations, } pub type AvailableLevels = Vec>; - pub fn scan_width_side( connections: &mut Vec, index: &mut usize, @@ -131,7 +123,7 @@ pub fn scan_width_side( size += 1; } - connections.push(Connection { + connections.push(Connection { index: *index, size, side: side.clone(), @@ -141,7 +133,7 @@ pub fn scan_width_side( }); *index = *index + 1; - + i += size; } else { i += 1; @@ -169,7 +161,7 @@ pub fn scan_height_side( size += 1; } - connections.push(Connection { + connections.push(Connection { index: *index, size, side: side.clone(), @@ -185,13 +177,9 @@ pub fn scan_height_side( i += 1; } } - } - - pub fn populate_level_connections(available_levels: &mut Vec) { - let mut to_add_elements: Vec<(usize, usize, (AvailableLevel, usize))> = vec![]; let mut i = 0; @@ -199,14 +187,12 @@ pub fn populate_level_connections(available_levels: &mut Vec) { let mut y = 0; while y < available_levels[i].connections.len() { - let level = &available_levels[i]; let connection = level.connections.get(y).unwrap(); let mut ii = 1; while ii + i < available_levels.len() { - let mut yy = 0; if available_levels[ii + i].level_type == LevelType::Spawn { @@ -214,21 +200,18 @@ pub fn populate_level_connections(available_levels: &mut Vec) { } while yy < available_levels[ii + i].connections.len() { - - let other_level = &available_levels[ii + i]; let other_connection = other_level.connections.get(yy).unwrap(); if connection.are_matching(&other_connection) { - //if other_level.level_type != LevelType::Spawn { - to_add_elements.push((i, y, (available_levels[ii + i].clone(), yy))); + to_add_elements.push((i, y, (available_levels[ii + i].clone(), yy))); //} // adding otherlevel to add to level //if level.level_type != LevelType::Spawn { - to_add_elements.push((i + ii, yy, (available_levels[i].clone(), y))); + to_add_elements.push((i + ii, yy, (available_levels[i].clone(), y))); //} } @@ -237,7 +220,7 @@ pub fn populate_level_connections(available_levels: &mut Vec) { ii += 1; } - + y += 1; } @@ -245,12 +228,12 @@ pub fn populate_level_connections(available_levels: &mut Vec) { } for to_add in to_add_elements { - available_levels[to_add.0].connections[to_add.1].compatiable_levels.push((to_add.2.0.level_id.clone(), to_add.2.1)); + available_levels[to_add.0].connections[to_add.1] + .compatiable_levels + .push((to_add.2 .0.level_id.clone(), to_add.2 .1)); } - } - pub struct MapGenerationContext { pub tile_size: (i32, i32), pub level_size: (i32, i32), @@ -268,13 +251,9 @@ pub struct MapGenerationData { } impl MapGenerationData { - pub fn from_context(context: &MapGenerationContext) -> Self { Self { rng: rand::rngs::StdRng::seed_from_u64(context.config.seed as u64), } } - } - - diff --git a/crates/map/src/generation/entity/door.rs b/crates/map/src/generation/entity/door.rs index aa654c1..6c0d3f6 100644 --- a/crates/map/src/generation/entity/door.rs +++ b/crates/map/src/generation/entity/door.rs @@ -1,6 +1,5 @@ use crate::generation::{position::Position, room::ConnectionTo}; - #[derive(Debug, Clone)] pub struct DoorConfig { pub connection: ConnectionTo, @@ -12,4 +11,4 @@ pub struct DoorConfig { pub cost: i32, // if the door need electricity to be open pub electrify: bool, -} \ No newline at end of file +} diff --git a/crates/map/src/generation/entity/location/mod.rs b/crates/map/src/generation/entity/location/mod.rs index 9a5fdff..dbf5683 100644 --- a/crates/map/src/generation/entity/location/mod.rs +++ b/crates/map/src/generation/entity/location/mod.rs @@ -1,6 +1,5 @@ use crate::generation::position::Position; - #[derive(Debug, Clone)] pub struct MultiTileEntityLocation { pub position: Position, @@ -24,11 +23,15 @@ pub struct EntityLocations { } impl EntityLocations { - - pub fn to_world_position(&self, room_position: Position) -> EntityLocations { - - - EntityLocations { doors: vec![], sodas: vec![], player_spawns: vec![], zombie_spawns: vec![], weapon_crates: vec![], weapons: vec![], windows: vec![] } + EntityLocations { + doors: vec![], + sodas: vec![], + player_spawns: vec![], + zombie_spawns: vec![], + weapon_crates: vec![], + weapons: vec![], + windows: vec![], + } } -} \ No newline at end of file +} diff --git a/crates/map/src/generation/entity/mod.rs b/crates/map/src/generation/entity/mod.rs index 3760208..70affd4 100644 --- a/crates/map/src/generation/entity/mod.rs +++ b/crates/map/src/generation/entity/mod.rs @@ -1,4 +1,3 @@ - -pub mod location; pub mod door; +pub mod location; pub mod window; diff --git a/crates/map/src/generation/entity/window.rs b/crates/map/src/generation/entity/window.rs index 068a5bc..4139ae6 100644 --- a/crates/map/src/generation/entity/window.rs +++ b/crates/map/src/generation/entity/window.rs @@ -1,9 +1,8 @@ use crate::generation::position::Position; - #[derive(Debug, Clone)] pub struct WindowConfig { pub position: Position, pub level_iid: String, pub size: (i32, i32), -} \ No newline at end of file +} diff --git a/crates/map/src/generation/imp/basic.rs b/crates/map/src/generation/imp/basic.rs index 06ccfa5..0b01dd6 100644 --- a/crates/map/src/generation/imp/basic.rs +++ b/crates/map/src/generation/imp/basic.rs @@ -1,10 +1,15 @@ use std::rc::Rc; -use crate::generation::{context::{AvailableLevel, LevelType, MapGenerationContext, MapGenerationData}, entity::{door::DoorConfig, window::WindowConfig}, position::Position, room::{ConnectionTo, RoomConnection}, IMapGeneration, Room}; +use crate::generation::{ + context::{AvailableLevel, LevelType, MapGenerationContext, MapGenerationData}, + entity::{door::DoorConfig, window::WindowConfig}, + position::Position, + room::{ConnectionTo, RoomConnection}, + IMapGeneration, Room, +}; use rand::Rng; - // private struct to store data during the map generation struct Map { // index of the last room that we iterate on @@ -15,12 +20,11 @@ struct Map { rooms_possible: Vec, } - pub struct BasicMapGeneration { context: MapGenerationContext, data: MapGenerationData, - map: Map + map: Map, } impl BasicMapGeneration { @@ -28,23 +32,28 @@ impl BasicMapGeneration { BasicMapGeneration { data: MapGenerationData::from_context(&context), context, - map: Map { last_generated_room_index: None, rooms: vec![], rooms_possible: vec![] } + map: Map { + last_generated_room_index: None, + rooms: vec![], + rooms_possible: vec![], + }, } } } impl BasicMapGeneration { - fn get_next_room_recursize(&mut self) -> Option<(Room, RoomConnection, RoomConnection)> { - - if self.context.config.max_room > 0 && self.map.rooms.len() >= self.context.config.max_room { - println!("max room stopping generation {} {}", self.map.rooms.len(), self.context.config.max_room); + if self.context.config.max_room > 0 && self.map.rooms.len() >= self.context.config.max_room + { + println!( + "max room stopping generation {} {}", + self.map.rooms.len(), + self.context.config.max_room + ); return None; } - loop { - if self.map.last_generated_room_index.is_none() { println!("no room mark to continue generation"); return None; @@ -55,12 +64,19 @@ impl BasicMapGeneration { let previous_room = self.map.rooms.get_mut(previous_room_index).unwrap(); let previous_room_def = previous_room.level_def.clone(); - let free_connection_len = previous_room.connections.iter() + let free_connection_len = previous_room + .connections + .iter() .filter(|i| i.to.is_none()) .count(); if free_connection_len == 0 { - if let Some(index) = self.map.rooms_possible.iter().position(|&x| x == previous_room_index) { + if let Some(index) = self + .map + .rooms_possible + .iter() + .position(|&x| x == previous_room_index) + { self.map.rooms_possible.remove(index); } @@ -69,81 +85,120 @@ impl BasicMapGeneration { return None; } - self.map.last_generated_room_index = self.map.rooms_possible.get(self.data.rng.gen_range(0..=(self.map.rooms_possible.len() - 1))).copied(); + self.map.last_generated_room_index = self + .map + .rooms_possible + .get( + self.data + .rng + .gen_range(0..=(self.map.rooms_possible.len() - 1)), + ) + .copied(); continue; } else { - // get the connection def let connection_def = { - let connection = previous_room.connections.iter() + let connection = previous_room + .connections + .iter() .filter(|i| i.to.is_none()) - .skip(self.data.rng.gen_range(0..=free_connection_len- 1)) + .skip(self.data.rng.gen_range(0..=free_connection_len - 1)) .last() .unwrap(); - + previous_room_def.connections.get(connection.index).unwrap() }; - if connection_def.compatiable_levels.len() == 0 { - - previous_room.connections.get_mut(connection_def.index).unwrap().to = Some(ConnectionTo::DeadEnd); + previous_room + .connections + .get_mut(connection_def.index) + .unwrap() + .to = Some(ConnectionTo::DeadEnd); println!("no compatible levels marking as DeadEnd"); continue; } else { - - - let compatible_level = connection_def.compatiable_levels + let compatible_level = connection_def + .compatiable_levels .iter() - .skip(self.data.rng.gen_range(0..=connection_def.compatiable_levels.len() - 1)) + .skip( + self.data + .rng + .gen_range(0..=connection_def.compatiable_levels.len() - 1), + ) .last() .unwrap(); - let compatible_level_def = self.context.available_levels.iter() + let compatible_level_def = self + .context + .available_levels + .iter() .find(|l| l.level_id == compatible_level.0) .unwrap(); - let level_connection = compatible_level_def.connections.get( compatible_level.1).unwrap(); + let level_connection = compatible_level_def + .connections + .get(compatible_level.1) + .unwrap(); let my_position = previous_room.get_connecting_room_position( - &connection_def, &compatible_level_def, compatible_level.1, - &self.context.tile_size + &connection_def, + &compatible_level_def, + compatible_level.1, + &self.context.tile_size, ); let mut new_room = Room::create(compatible_level_def.clone(), my_position); if new_room.is_outside(&self.context.config) { - previous_room.connections.get_mut(connection_def.index).unwrap().to = Some(ConnectionTo::OutSide); + previous_room + .connections + .get_mut(connection_def.index) + .unwrap() + .to = Some(ConnectionTo::OutSide); continue; } else { - - new_room.set_connection_between(level_connection.index, previous_room, connection_def.index); - - let new_room_level_connection = new_room.connections.get(level_connection.index).unwrap().clone(); - - return Some((new_room, new_room_level_connection, previous_room.connections.get(connection_def.index).unwrap().clone())); + new_room.set_connection_between( + level_connection.index, + previous_room, + connection_def.index, + ); + + let new_room_level_connection = new_room + .connections + .get(level_connection.index) + .unwrap() + .clone(); + + return Some(( + new_room, + new_room_level_connection, + previous_room + .connections + .get(connection_def.index) + .unwrap() + .clone(), + )); } - } } } - } - } impl IMapGeneration for BasicMapGeneration { - fn get_spawning_room(&mut self) -> Room { - - - let spawning_levels: Vec<&Rc> = self.context.available_levels.iter() + let spawning_levels: Vec<&Rc> = self + .context + .available_levels + .iter() .filter(|i| i.level_type == LevelType::Spawn) .collect(); - let spawning_room_def = spawning_levels.iter() + let spawning_room_def = spawning_levels + .iter() .skip(self.data.rng.gen_range(0..=spawning_levels.len() - 1)) .last(); @@ -153,17 +208,22 @@ impl IMapGeneration for BasicMapGeneration { let spawning_room_def = (*spawning_room_def.unwrap()).clone(); - let x: i32 = self.data.rng.gen_range(self.context.config.get_range_x(spawning_room_def.level_size_p.0)); - let y: i32 = self.data.rng.gen_range(self.context.config.get_range_y(spawning_room_def.level_size_p.1)); - - - let spawning_room_def = Room::create(spawning_room_def.clone(), Position(x,y)); + let x: i32 = self.data.rng.gen_range( + self.context + .config + .get_range_x(spawning_room_def.level_size_p.0), + ); + let y: i32 = self.data.rng.gen_range( + self.context + .config + .get_range_y(spawning_room_def.level_size_p.1), + ); + + let spawning_room_def = Room::create(spawning_room_def.clone(), Position(x, y)); self.map.rooms.push(spawning_room_def.clone()); self.map.last_generated_room_index = Some(0); - spawning_room_def - } fn get_next_room(&mut self) -> Option<(Room, RoomConnection, RoomConnection)> { @@ -179,25 +239,27 @@ impl IMapGeneration for BasicMapGeneration { fn get_doors(&mut self) -> Vec { // get all my level , get all the doors in each level - self.map.rooms.iter() + self.map + .rooms + .iter() .flat_map(|x| { - x.entity_locations.doors.iter().map(|y| { - DoorConfig{ + x.entity_locations + .doors + .iter() + .map(|y| DoorConfig { position: Position(0, 0), - size: (0,0), + size: (0, 0), level_iid: x.level_iid.clone(), connection: ConnectionTo::DeadEnd, cost: 100, - electrify: false - } - }).collect::>() - }).collect() + electrify: false, + }) + .collect::>() + }) + .collect() } fn get_windows(&mut self) -> Vec { - vec![] } - - } diff --git a/crates/map/src/generation/imp/mod.rs b/crates/map/src/generation/imp/mod.rs index 8b7bebc..00bc6b9 100644 --- a/crates/map/src/generation/imp/mod.rs +++ b/crates/map/src/generation/imp/mod.rs @@ -1,12 +1,11 @@ - use self::basic::BasicMapGeneration; -use super::{config:: MapGenerationMode, IMapGeneration, context::MapGenerationContext}; +use super::{config::MapGenerationMode, context::MapGenerationContext, IMapGeneration}; mod basic; pub fn get_implementation(context: MapGenerationContext) -> Box { match context.config.mode { - MapGenerationMode::Basic => Box::new(BasicMapGeneration::create(context)) + MapGenerationMode::Basic => Box::new(BasicMapGeneration::create(context)), } } diff --git a/crates/map/src/generation/mod.rs b/crates/map/src/generation/mod.rs index 2beb542..e7b765b 100644 --- a/crates/map/src/generation/mod.rs +++ b/crates/map/src/generation/mod.rs @@ -1,38 +1,44 @@ mod imp; -pub mod position; -pub mod entity; -pub mod room; pub mod config; pub mod context; - +pub mod entity; +pub mod position; +pub mod room; use crate::generation::imp::get_implementation; -use self::{context::MapGenerationContext, entity::{door::DoorConfig, window::WindowConfig}, room::{Room, RoomConnection}}; - - +use self::{ + context::MapGenerationContext, + entity::{door::DoorConfig, window::WindowConfig}, + room::{Room, RoomConnection}, +}; trait IMapGeneration { // generate the first room that will be the game starting point - fn get_spawning_room(&mut self) -> Room; + fn get_spawning_room(&mut self) -> Room; // generate the next room and provide the two connection used to create this room fn get_next_room(&mut self) -> Option<(Room, RoomConnection, RoomConnection)>; - fn get_doors(&mut self) -> Vec; fn get_windows(&mut self) -> Vec; } pub trait IMapGenerator { - fn add_room(&mut self, room: &Room, connection_used: Option<&RoomConnection>, connected_to: Option<&RoomConnection>); + fn add_room( + &mut self, + room: &Room, + connection_used: Option<&RoomConnection>, + connected_to: Option<&RoomConnection>, + ); fn add_doors(&mut self, doors: &Vec); fn add_windows(&mut self, windows: &Vec); } - -pub fn map_generation(context: MapGenerationContext, map_generator: &mut impl IMapGenerator) -> Result<(), ()> { - +pub fn map_generation( + context: MapGenerationContext, + map_generator: &mut impl IMapGenerator, +) -> Result<(), ()> { //let mut generated_map = GeneratedMap::create(map_json.levels); let mut generator = get_implementation(context); @@ -41,8 +47,14 @@ pub fn map_generation(context: MapGenerationContext, map_generator: &mut impl IM map_generator.add_room(&room, None, None); - while let Some((next_room, next_room_connection, other_room_connection)) = generator.get_next_room() { - map_generator.add_room(&next_room, Some(&next_room_connection), Some(&other_room_connection)); + while let Some((next_room, next_room_connection, other_room_connection)) = + generator.get_next_room() + { + map_generator.add_room( + &next_room, + Some(&next_room_connection), + Some(&other_room_connection), + ); } let doors = generator.get_doors(); diff --git a/crates/map/src/generation/position.rs b/crates/map/src/generation/position.rs index 53ddeb3..abfe5ad 100644 --- a/crates/map/src/generation/position.rs +++ b/crates/map/src/generation/position.rs @@ -1,11 +1,8 @@ - #[derive(Debug, Default, Clone, Copy)] -pub struct Position(pub i32,pub i32); +pub struct Position(pub i32, pub i32); impl std::fmt::Display for Position { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "({}x{})", self.0, self.1) } - } diff --git a/crates/map/src/generation/room.rs b/crates/map/src/generation/room.rs index 2ea68f2..d457e7a 100644 --- a/crates/map/src/generation/room.rs +++ b/crates/map/src/generation/room.rs @@ -2,7 +2,12 @@ use std::rc::Rc; use bevy::utils::Uuid; -use super::{config::MapGenerationConfig, context::{AvailableLevel, Connection, Side}, entity::location::EntityLocations, position::Position}; +use super::{ + config::MapGenerationConfig, + context::{AvailableLevel, Connection, Side}, + entity::location::EntityLocations, + position::Position, +}; #[derive(Debug, Clone)] pub enum ConnectionTo { @@ -10,10 +15,9 @@ pub enum ConnectionTo { DeadEnd, OutSide, } - #[derive(Debug, Clone)] -pub struct RoomConnection{ +pub struct RoomConnection { pub index: usize, pub level_iid: String, pub level_id: String, @@ -22,44 +26,68 @@ pub struct RoomConnection{ } #[derive(Debug, Clone)] -pub struct Room{ +pub struct Room { pub level_iid: String, pub position: Position, pub connections: Vec, pub entity_locations: EntityLocations, pub level_def: Rc, - } impl Room { pub fn create(level: Rc, position: Position) -> Self { - let level_iid: String = Uuid::new_v4().into(); - let connections: Vec<_> = level.connections.iter() - .map(|x| RoomConnection{index: x.index, to: None, level_id: x.level_id.clone(), level_iid: level_iid.clone(), side: x.side, }) + let connections: Vec<_> = level + .connections + .iter() + .map(|x| RoomConnection { + index: x.index, + to: None, + level_id: x.level_id.clone(), + level_iid: level_iid.clone(), + side: x.side, + }) .collect(); - Self { level_iid, position: position, entity_locations: level.entity_locations.clone(), connections, level_def: level, } + Self { + level_iid, + position: position, + entity_locations: level.entity_locations.clone(), + connections, + level_def: level, + } } - fn set_connection(&mut self, my_connection_index: usize, their_room: &mut Room, their_connection_index: usize) { + fn set_connection( + &mut self, + my_connection_index: usize, + their_room: &mut Room, + their_connection_index: usize, + ) { let my_connection = self.connections.get_mut(my_connection_index).unwrap(); if my_connection.to.is_some() { panic!("connection is already used"); } - my_connection.to = Some(ConnectionTo::Room((their_room.level_iid.clone(), their_connection_index))); + my_connection.to = Some(ConnectionTo::Room(( + their_room.level_iid.clone(), + their_connection_index, + ))); } - pub fn set_connection_between(&mut self, my_connection_index: usize, their_room: &mut Room, their_connection_index: usize) { + pub fn set_connection_between( + &mut self, + my_connection_index: usize, + their_room: &mut Room, + their_connection_index: usize, + ) { // throw if one is already link self.set_connection(my_connection_index, their_room, their_connection_index); their_room.set_connection(their_connection_index, self, my_connection_index); } pub fn is_overlapping(&self, other: &Room) -> bool { - // find if we are overlapping let left_of_other = self.position.0 + self.level_def.level_size_p.0 < other.position.0; let left_of_self = other.position.0 + other.level_def.level_size_p.0 < self.position.0; @@ -74,20 +102,19 @@ impl Room { // check if top-left corner is outside or not pub fn is_outside(&self, config: &MapGenerationConfig) -> bool { - let position = &self.position; - - (position.0 > config.max_width || position.0 < (config.max_width * -1)) || - (position.1 > config.max_heigth || position.1 < (config.max_heigth * -1)) + (position.0 > config.max_width || position.0 < (config.max_width * -1)) + || (position.1 > config.max_heigth || position.1 < (config.max_heigth * -1)) } - pub fn get_connecting_room_position( - &self, my_connection: &Connection, their_level: &AvailableLevel, their_connection: usize, + &self, + my_connection: &Connection, + their_level: &AvailableLevel, + their_connection: usize, tile_size: &(i32, i32), ) -> Position { - let my_position = &self.position; let their_connection = their_level.connections.get(their_connection).unwrap(); @@ -101,25 +128,19 @@ impl Room { Position( my_position.0 + (their_connection.side.get_factor() * (offset_pixel)), - my_position.1 + (their_connection.side.get_factor() * -1 * their_level.level_size_p.1), + my_position.1 + + (their_connection.side.get_factor() * -1 * their_level.level_size_p.1), ) - }, + } Side::W | Side::E => { let offset_pixel = (offset as i32) * tile_size.1; Position( - my_position.0 + (their_connection.side.get_factor() * -1 * their_level.level_size_p.0), + my_position.0 + + (their_connection.side.get_factor() * -1 * their_level.level_size_p.0), my_position.1 + (their_connection.side.get_factor() * (offset_pixel)), ) } } - - } - } - - - - - diff --git a/crates/map/src/ldtk/generation.rs b/crates/map/src/ldtk/generation.rs index b84091e..4de4bb1 100644 --- a/crates/map/src/ldtk/generation.rs +++ b/crates/map/src/ldtk/generation.rs @@ -1,82 +1,153 @@ use std::rc::Rc; -use bevy::{math::{IVec2, Vec2}, utils::Uuid}; -use bevy_ecs_ldtk::{ldtk::{FieldInstance, FieldValue, LayerInstance, LdtkJson, Level, NeighbourLevel}, EntityInstance}; - -use crate::generation::{config::MapGenerationConfig, position::Position, context::{populate_level_connections, scan_height_side, scan_width_side, AvailableLevel, LevelType, MapGenerationContext, Side}, entity::location::{EntityLocation, EntityLocations, MultiTileEntityLocation}, room::{Room, RoomConnection}, IMapGenerator}; +use bevy::{ + math::{IVec2, Vec2}, + utils::Uuid, +}; +use bevy_ecs_ldtk::{ + ldtk::{FieldInstance, FieldValue, LayerInstance, LdtkJson, Level, NeighbourLevel}, + EntityInstance, +}; + +use crate::generation::{ + config::MapGenerationConfig, + context::{ + populate_level_connections, scan_height_side, scan_width_side, AvailableLevel, LevelType, + MapGenerationContext, Side, + }, + entity::location::{EntityLocation, EntityLocations, MultiTileEntityLocation}, + position::Position, + room::{Room, RoomConnection}, + IMapGenerator, +}; use super::map_const::{self, LAYER_ENTITY}; fn get_level_field(level: &Level, name: &str) -> Option { - level.field_instances.iter() + level + .field_instances + .iter() .find(|instance| name == instance.identifier) .map(|instance| instance.value.clone()) } - macro_rules! get_entities { ($self: expr, $identifier: expr, $type: ident) => { - $self.iter().filter(|x| x.identifier == $identifier).map(|x| { - let position = Position(x.grid.x, x.grid.y); - $type{ - position: position, - } - }).collect() + $self + .iter() + .filter(|x| x.identifier == $identifier) + .map(|x| { + let position = Position(x.grid.x, x.grid.y); + $type { position: position } + }) + .collect() }; } macro_rules! get_wall_entities { ($self: expr, $tile_size: expr, $identifier: expr, $type: ident) => { - $self.iter().filter(|x| x.identifier == $identifier).map(|x| { - let position = Position(x.grid.x, x.grid.y); - $type{ - size: (x.width / $tile_size.0, x.height / $tile_size.1), - position: position, - } - }).collect() + $self + .iter() + .filter(|x| x.identifier == $identifier) + .map(|x| { + let position = Position(x.grid.x, x.grid.y); + $type { + size: (x.width / $tile_size.0, x.height / $tile_size.1), + position: position, + } + }) + .collect() }; } - fn extract_entity_locations(level: &Level, tile_size: &(i32, i32)) -> EntityLocations { - - let entity_layer = level.layer_instances.as_ref().unwrap().iter() + let entity_layer = level + .layer_instances + .as_ref() + .unwrap() + .iter() .find(|x| x.identifier == map_const::LAYER_ENTITY); if let Some(entity_layer) = entity_layer { EntityLocations { - doors: get_wall_entities!(entity_layer.entity_instances, tile_size, map_const::ENTITY_WINDOW_LOCATION, MultiTileEntityLocation), - sodas: get_entities!(entity_layer.entity_instances, map_const::ENTITY_SODA_LOCATION, EntityLocation), - player_spawns: get_entities!(entity_layer.entity_instances, map_const::ENTITY_PLAYER_SPAWN_LOCATION, EntityLocation), - zombie_spawns: get_entities!(entity_layer.entity_instances, map_const::ENTITY_ZOMBIE_SPAWN_LOCATION, EntityLocation), - weapon_crates: get_entities!(entity_layer.entity_instances, map_const::ENTITY_WEAPON_LOCATION, EntityLocation), - weapons: get_entities!(entity_layer.entity_instances, map_const::ENTITY_WEAPON_LOCATION, EntityLocation), - windows: get_wall_entities!(entity_layer.entity_instances, tile_size, map_const::ENTITY_WINDOW_LOCATION, MultiTileEntityLocation) + doors: get_wall_entities!( + entity_layer.entity_instances, + tile_size, + map_const::ENTITY_WINDOW_LOCATION, + MultiTileEntityLocation + ), + sodas: get_entities!( + entity_layer.entity_instances, + map_const::ENTITY_SODA_LOCATION, + EntityLocation + ), + player_spawns: get_entities!( + entity_layer.entity_instances, + map_const::ENTITY_PLAYER_SPAWN_LOCATION, + EntityLocation + ), + zombie_spawns: get_entities!( + entity_layer.entity_instances, + map_const::ENTITY_ZOMBIE_SPAWN_LOCATION, + EntityLocation + ), + weapon_crates: get_entities!( + entity_layer.entity_instances, + map_const::ENTITY_WEAPON_LOCATION, + EntityLocation + ), + weapons: get_entities!( + entity_layer.entity_instances, + map_const::ENTITY_WEAPON_LOCATION, + EntityLocation + ), + windows: get_wall_entities!( + entity_layer.entity_instances, + tile_size, + map_const::ENTITY_WINDOW_LOCATION, + MultiTileEntityLocation + ), } } else { - EntityLocations { doors: vec![], sodas: vec![], player_spawns: vec![], zombie_spawns: vec![], weapon_crates: vec![], weapons: vec![], windows: vec![] } + EntityLocations { + doors: vec![], + sodas: vec![], + player_spawns: vec![], + zombie_spawns: vec![], + weapon_crates: vec![], + weapons: vec![], + windows: vec![], + } } } - fn to_available_level(level: &Level, tile_size: &(i32, i32)) -> AvailableLevel { let level_size: (usize, usize) = ( (level.px_wid / tile_size.0) as usize, - (level.px_hei / tile_size.1) as usize + (level.px_hei / tile_size.1) as usize, ); // identify each level connection - let connection_layer: &LayerInstance = level.layer_instances.as_ref().map(|layer_instance| { - layer_instance.into_iter() - .find(|item| map_const::LAYER_CONNECTION == item.identifier) - .ok_or_else(|| "Failed to find LevelConnetion Layer on level") - }).unwrap_or_else(|| Err("No Layers present")).unwrap(); - - let grid: Vec<&[i32]> = connection_layer.int_grid_csv.chunks(level_size.0 as usize).collect(); + let connection_layer: &LayerInstance = level + .layer_instances + .as_ref() + .map(|layer_instance| { + layer_instance + .into_iter() + .find(|item| map_const::LAYER_CONNECTION == item.identifier) + .ok_or_else(|| "Failed to find LevelConnetion Layer on level") + }) + .unwrap_or_else(|| Err("No Layers present")) + .unwrap(); + + let grid: Vec<&[i32]> = connection_layer + .int_grid_csv + .chunks(level_size.0 as usize) + .collect(); let level_type = { let is_spawn = get_level_field(&level, map_const::LEVEL_FIELD_SPAWN).map_or(false, |x| { - if let FieldValue::Bool(value) =x { + if let FieldValue::Bool(value) = x { value } else { false @@ -90,47 +161,80 @@ fn to_available_level(level: &Level, tile_size: &(i32, i32)) -> AvailableLevel { } }; - // get my entity layer from the level and extract all entity - - - let mut available_level = AvailableLevel { + let mut available_level = AvailableLevel { level_id: level.identifier.clone(), connections: vec![], level_size, - level_size_p: (level_size.0 as i32 * tile_size.0, level_size.1 as i32 * tile_size.1), + level_size_p: ( + level_size.0 as i32 * tile_size.0, + level_size.1 as i32 * tile_size.1, + ), level_type, entity_locations: extract_entity_locations(level, tile_size), }; - - let mut connections= vec![]; + let mut connections = vec![]; let mut index = 0; - scan_width_side(&mut connections, &mut index, &available_level, &level_size, &grid, 0, Side::N); - scan_width_side(&mut connections, &mut index, &available_level, &level_size, &grid, level_size.1 - 1, Side::S); + scan_width_side( + &mut connections, + &mut index, + &available_level, + &level_size, + &grid, + 0, + Side::N, + ); + scan_width_side( + &mut connections, + &mut index, + &available_level, + &level_size, + &grid, + level_size.1 - 1, + Side::S, + ); - scan_height_side(&mut connections, &mut index, &available_level, &level_size, &grid, 0, Side::W); - scan_height_side(&mut connections, &mut index, &available_level, &level_size, &grid, level_size.0 - 1, Side::E); + scan_height_side( + &mut connections, + &mut index, + &available_level, + &level_size, + &grid, + 0, + Side::W, + ); + scan_height_side( + &mut connections, + &mut index, + &available_level, + &level_size, + &grid, + level_size.0 - 1, + Side::E, + ); available_level.connections = connections; available_level } - pub fn from_map(map_json: &LdtkJson, config: MapGenerationConfig) -> MapGenerationContext { if map_json.levels.len() < 1 { eprintln!("to few level present in the project"); } - let tile_size = (map_json.default_entity_width, map_json.default_entity_height); + let tile_size = ( + map_json.default_entity_width, + map_json.default_entity_height, + ); let first_level = map_json.levels.get(0).unwrap(); let level_size = ( first_level.px_wid / tile_size.0, - first_level.px_hei / tile_size.1 + first_level.px_hei / tile_size.1, ); println!("starting level generation with config \nseed={} \ntilse_size={}x{} \nlevel_size={}x{}\nmap_size={}x{}", @@ -138,16 +242,16 @@ pub fn from_map(map_json: &LdtkJson, config: MapGenerationConfig) -> MapGenerati config.max_width, config.max_heigth ); - let mut available_levels: Vec = map_json.levels.iter() - .map(|item| { - to_available_level(&item, &tile_size) - }).collect(); - + let mut available_levels: Vec = map_json + .levels + .iter() + .map(|item| to_available_level(&item, &tile_size)) + .collect(); populate_level_connections(&mut available_levels); - - let available_levels = available_levels.iter() + let available_levels = available_levels + .iter() .map(|x| Rc::new(x.clone())) .collect(); @@ -159,20 +263,17 @@ pub fn from_map(map_json: &LdtkJson, config: MapGenerationConfig) -> MapGenerati } } - - - #[derive(Debug, Clone)] pub struct GeneratedRoom { level: Level, ldtk: Rc, } - impl GeneratedRoom { - pub fn create(ldtk_json: Rc, room: &Room) -> Self { - let mut level = ldtk_json.levels.iter() + let mut level = ldtk_json + .levels + .iter() .find(|item| item.identifier == room.level_def.level_id) .expect("failed to find level from original") .clone(); @@ -182,75 +283,84 @@ impl GeneratedRoom { level.world_x = room.position.0; level.world_y = room.position.1; level.neighbours.clear(); - level.layer_instances.as_mut().unwrap() - .iter_mut().find(|x| x.identifier == LAYER_ENTITY) - .unwrap().entity_instances.clear(); - - GeneratedRoom{ + level + .layer_instances + .as_mut() + .unwrap() + .iter_mut() + .find(|x| x.identifier == LAYER_ENTITY) + .unwrap() + .entity_instances + .clear(); + + GeneratedRoom { level, ldtk: ldtk_json, } } - } - - pub struct GeneratedMap { ldtk_json: Rc, generated_rooms: Vec, } pub fn get_new_entity( - room: &GeneratedRoom, - original_entity_identifier: &str, - grid_position: Position, - size: (i32, i32), - tile_size: (i32, i32), - // identifier and value - fields: Vec<(String, FieldValue)> + room: &GeneratedRoom, + original_entity_identifier: &str, + grid_position: Position, + size: (i32, i32), + tile_size: (i32, i32), + // identifier and value + fields: Vec<(String, FieldValue)>, ) -> EntityInstance { - - let entity = room.ldtk.defs.entities.iter() - .find(|x| x.identifier == original_entity_identifier) - .unwrap(); - - let px = (grid_position.0 * tile_size.0, grid_position.1 * tile_size.1); - let world_px = (px.0 + room.level.world_x, px.1 + room.level.world_y); - - let identifiers = fields.iter().map(|x| { - - let field = entity.field_defs.iter() + let entity = room + .ldtk + .defs + .entities + .iter() + .find(|x| x.identifier == original_entity_identifier) + .unwrap(); + + let px = (grid_position.0 * tile_size.0, grid_position.1 * tile_size.1); + let world_px = (px.0 + room.level.world_x, px.1 + room.level.world_y); + + let identifiers = fields + .iter() + .map(|x| { + let field = entity + .field_defs + .iter() .find(|fd| fd.identifier == x.0) .unwrap(); - FieldInstance{ + FieldInstance { identifier: field.identifier.clone(), def_uid: field.uid, field_instance_type: field.field_definition_type.clone(), value: x.1.clone(), tile: None, - real_editor_values: vec![] + real_editor_values: vec![], } - }).collect(); - - EntityInstance { - identifier: original_entity_identifier.into(), - def_uid: entity.uid, - grid: IVec2::new(grid_position.0, grid_position.1), - pivot: Vec2::new(entity.pivot_x, entity.pivot_y), - tags: vec![], - tile: None, - smart_color: entity.color, - iid: Uuid::new_v4().to_string(), - width: size.0, - height: size.1, - field_instances: identifiers, - px: IVec2::new(px.0, px.1), - world_x: Some(world_px.0), - world_y: Some(world_px.1), - } -} + }) + .collect(); + EntityInstance { + identifier: original_entity_identifier.into(), + def_uid: entity.uid, + grid: IVec2::new(grid_position.0, grid_position.1), + pivot: Vec2::new(entity.pivot_x, entity.pivot_y), + tags: vec![], + tile: None, + smart_color: entity.color, + iid: Uuid::new_v4().to_string(), + width: size.0, + height: size.1, + field_instances: identifiers, + px: IVec2::new(px.0, px.1), + world_x: Some(world_px.0), + world_y: Some(world_px.1), + } +} impl GeneratedMap { pub fn create(ldtk_json: LdtkJson) -> Self { @@ -260,40 +370,51 @@ impl GeneratedMap { } } - pub fn get_generated_map(&self) -> LdtkJson { let mut new_map: LdtkJson = (*self.ldtk_json).clone(); - new_map.levels = self.generated_rooms.iter().enumerate().map(|(i,x)| { - let mut r = x.level.clone(); - r.identifier = format!("Level_{}", i); - r - }).collect(); - + new_map.levels = self + .generated_rooms + .iter() + .enumerate() + .map(|(i, x)| { + let mut r = x.level.clone(); + r.identifier = format!("Level_{}", i); + r + }) + .collect(); new_map } - } impl IMapGenerator for GeneratedMap { - fn add_room(&mut self, room: &Room, connection_used: Option<&RoomConnection>, connected_to: Option<&RoomConnection>) { + fn add_room( + &mut self, + room: &Room, + connection_used: Option<&RoomConnection>, + connected_to: Option<&RoomConnection>, + ) { let mut generated_room = GeneratedRoom::create(self.ldtk_json.clone(), room); - println!("adding room id={} type={:?} from_level={} position={}", room.level_iid, room.level_def.level_type, room.level_def.level_id, room.position); + println!( + "adding room id={} type={:?} from_level={} position={}", + room.level_iid, room.level_def.level_type, room.level_def.level_id, room.position + ); if let Some(connected_to) = connected_to { - let connection_used = connection_used.unwrap(); - generated_room.level.neighbours.push(NeighbourLevel{ + generated_room.level.neighbours.push(NeighbourLevel { level_iid: connected_to.level_iid.clone(), dir: connection_used.side.to_dir_str().into(), ..Default::default() }); // find the other room and me as it's neighbours - let linked_room = self.generated_rooms.iter_mut() + let linked_room = self + .generated_rooms + .iter_mut() .find(|r| r.level.iid == connected_to.level_iid) .unwrap(); @@ -301,45 +422,51 @@ impl IMapGenerator for GeneratedMap { connection_used.side, connection_used.index, connected_to.side, connected_to.index, connected_to.level_iid, connected_to.level_id, linked_room.level.world_x, linked_room.level.world_y, ); - - linked_room.level.neighbours.push(NeighbourLevel { + + linked_room.level.neighbours.push(NeighbourLevel { dir: connected_to.side.to_dir_str().into(), level_iid: room.level_iid.clone(), ..Default::default() }) - } println!(""); self.generated_rooms.push(generated_room); - } fn add_doors(&mut self, doors: &Vec) { for door in doors.iter() { - - let level = self.generated_rooms.iter_mut() + let level = self + .generated_rooms + .iter_mut() .find(|x| x.level.iid == door.level_iid) .unwrap(); let new_entity = get_new_entity( - &level, - map_const::ENTITY_DOOR_LOCATION, + &level, + map_const::ENTITY_DOOR_LOCATION, door.position, door.size, - (self.ldtk_json.default_entity_width, self.ldtk_json.default_entity_height), - vec![] + ( + self.ldtk_json.default_entity_width, + self.ldtk_json.default_entity_height, + ), + vec![], ); - level.level.layer_instances.as_mut().unwrap().iter_mut() + level + .level + .layer_instances + .as_mut() + .unwrap() + .iter_mut() .find(|x| x.identifier == map_const::LAYER_ENTITY) .unwrap() - .entity_instances.push(new_entity); + .entity_instances + .push(new_entity); } } - fn add_windows(&mut self, windows: &Vec) { - - } + fn add_windows(&mut self, windows: &Vec) {} } diff --git a/crates/map/src/ldtk/loader/mod.rs b/crates/map/src/ldtk/loader/mod.rs index 6234ce4..afad326 100644 --- a/crates/map/src/ldtk/loader/mod.rs +++ b/crates/map/src/ldtk/loader/mod.rs @@ -1,7 +1,6 @@ - use bevy::prelude::*; +use bevy_ecs_ldtk::assets::{LdtkProjectLoader, LdtkProjectLoaderSettings}; use bevy_ecs_ldtk::prelude::*; -use bevy_ecs_ldtk::assets::{LdtkProjectLoaderSettings, LdtkProjectLoader}; use once_cell::sync::Lazy; @@ -10,10 +9,7 @@ use crate::generation::map_generation; use super::generation::{from_map, GeneratedMap}; - -static mut CONFIG: Lazy = Lazy::new(|| { - MapGenerationConfig::default() -}); +static mut CONFIG: Lazy = Lazy::new(|| MapGenerationConfig::default()); fn set_global_config(config: &MapGenerationConfig) { unsafe { @@ -26,34 +22,28 @@ fn set_global_config(config: &MapGenerationConfig) { } } - pub fn get_asset_loader_generation() -> LdtkProjectLoader { - - LdtkProjectLoader{ + LdtkProjectLoader { callback: Some(Box::new(|map_json, config| { - let config: MapGenerationConfig = serde_json::from_value(serde_json::Value::Object(config)) + let config: MapGenerationConfig = + serde_json::from_value(serde_json::Value::Object(config)) .expect("Failed to convert value to struct"); - + let context = from_map(&map_json, config); let mut generator = GeneratedMap::create(map_json); map_generation(context, &mut generator).unwrap(); generator.get_generated_map() - })), + })), } - } -pub fn reload_map( - asset_server: &Res, - config: &MapGenerationConfig, -) { +pub fn reload_map(asset_server: &Res, config: &MapGenerationConfig) { set_global_config(config); asset_server.reload(config.map_path.clone()); } - pub fn load_map( commands: &mut Commands, asset_server: &Res, @@ -61,15 +51,16 @@ pub fn load_map( ) { set_global_config(config); - let ldtk_handle = asset_server.load_with_settings(config.map_path.clone(), |s: &mut LdtkProjectLoaderSettings| { - unsafe { + let ldtk_handle = asset_server.load_with_settings( + config.map_path.clone(), + |s: &mut LdtkProjectLoaderSettings| unsafe { s.data = serde_json::to_value(&*CONFIG) - .expect("Failed to convert struct to value") - .as_object() - .expect("Failed to convert value to object") - .clone(); - } - }); + .expect("Failed to convert struct to value") + .as_object() + .expect("Failed to convert value to object") + .clone(); + }, + ); let level_set = LevelSet::default(); @@ -78,11 +69,11 @@ pub fn load_map( level_set, ..Default::default() }); - } pub fn setup_generated_map( - mut commands: Commands, asset_server: Res, + mut commands: Commands, + asset_server: Res, config: Res, ) { load_map(&mut commands, &asset_server, config.as_ref()) diff --git a/crates/map/src/ldtk/map_const.rs b/crates/map/src/ldtk/map_const.rs index 1bfd57c..c33827a 100644 --- a/crates/map/src/ldtk/map_const.rs +++ b/crates/map/src/ldtk/map_const.rs @@ -1,14 +1,12 @@ - pub const LEVEL_FIELD_SPAWN: &str = "spawn"; pub const LAYER_CONNECTION: &str = "LevelConnection"; pub const LAYER_ENTITY: &str = "Entities"; - pub const ENTITY_DOOR_LOCATION: &str = "Door"; pub const ENTITY_PLAYER_SPAWN_LOCATION: &str = "PlayerSpawnLocation"; pub const ENTITY_ZOMBIE_SPAWN_LOCATION: &str = "ZombieSpawnLocation"; pub const ENTITY_CRATE_LOCATION: &str = "CrateLocation"; pub const ENTITY_WEAPON_LOCATION: &str = "WeaponLocation"; pub const ENTITY_WINDOW_LOCATION: &str = "Window"; -pub const ENTITY_SODA_LOCATION: &str = "SodaLocation"; \ No newline at end of file +pub const ENTITY_SODA_LOCATION: &str = "SodaLocation"; diff --git a/crates/map/src/ldtk/mod.rs b/crates/map/src/ldtk/mod.rs index ad26332..6594628 100644 --- a/crates/map/src/ldtk/mod.rs +++ b/crates/map/src/ldtk/mod.rs @@ -1,4 +1,4 @@ mod map_const; -pub mod loader; pub mod generation; +pub mod loader; diff --git a/crates/map/src/lib.rs b/crates/map/src/lib.rs index fc3c4bd..ff730b4 100644 --- a/crates/map/src/lib.rs +++ b/crates/map/src/lib.rs @@ -1,2 +1,2 @@ pub mod generation; -pub mod ldtk; \ No newline at end of file +pub mod ldtk; diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index 3781241..6cde81a 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -4,4 +4,8 @@ version = "0.2.0" edition = "2021" [dependencies] -bevy = "*" \ No newline at end of file +bevy = "*" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +console_error_panic_hook = "0.1.6" +web-sys = "0.3.57" \ No newline at end of file diff --git a/crates/utils/src/camera/mod.rs b/crates/utils/src/camera/mod.rs index 0affd03..eb8e4e7 100644 --- a/crates/utils/src/camera/mod.rs +++ b/crates/utils/src/camera/mod.rs @@ -1 +1 @@ -pub mod tod; \ No newline at end of file +pub mod tod; diff --git a/crates/utils/src/camera/tod.rs b/crates/utils/src/camera/tod.rs index d2fad18..616d56a 100644 --- a/crates/utils/src/camera/tod.rs +++ b/crates/utils/src/camera/tod.rs @@ -1,5 +1,5 @@ -use bevy::prelude::*; use bevy::input::ButtonInput; +use bevy::prelude::*; pub fn move_camera( mut players: Query<&mut Transform, With>, @@ -21,13 +21,10 @@ pub fn move_camera( transform.translation.x += movement_direction.0 as f32 * 3.0; transform.translation.y += movement_direction.1 as f32 * 3.0; } - } - pub fn setup_camera(mut commands: Commands) { let mut camera = Camera2dBundle::default(); camera.projection.scale = 1.3; commands.spawn(camera); } - diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index b84959a..34135a3 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1 +1,2 @@ pub mod camera; +pub mod web; \ No newline at end of file diff --git a/crates/utils/src/web/mod.rs b/crates/utils/src/web/mod.rs new file mode 100644 index 0000000..dae417c --- /dev/null +++ b/crates/utils/src/web/mod.rs @@ -0,0 +1,26 @@ +use bevy::{prelude::*, window::PrimaryWindow}; + +pub struct WebPlugin {} + +impl Plugin for WebPlugin { + fn build(&self, app: &mut App) { + #[cfg(target_arch = "wasm32")] + { + console_error_panic_hook::set_once(); + app.add_systems(Update, update_window_size); + } + } +} + + +#[cfg(target_arch = "wasm32")] +fn update_window_size(mut window: Query<&mut Window, With>) { + (|| { + let mut window = window.get_single_mut().ok()?; + let browser_window = web_sys::window()?; + let width = browser_window.inner_width().ok()?.as_f64()?; + let height = browser_window.inner_height().ok()?.as_f64()?; + window.resolution.set(width as f32, height as f32); + Some(()) + })(); +} \ No newline at end of file diff --git a/examples/map_generation.rs b/examples/map_generation.rs index 29b1d79..153957f 100644 --- a/examples/map_generation.rs +++ b/examples/map_generation.rs @@ -1,15 +1,12 @@ - use bevy_ecs_ldtk::ldtk::LdtkJson; use map::ldtk::generation::{from_map, GeneratedMap}; use serde_json::{from_str, to_string_pretty}; use std::fs::File; use std::io::{Read, Write}; -use map::generation::{map_generation, config::MapGenerationConfig}; - +use map::generation::{config::MapGenerationConfig, map_generation}; fn main() { - let usage = "Usage: [seed]"; // Get the file path from the command line arguments @@ -23,38 +20,44 @@ fn main() { let map_path = &args[1]; let map_output_path = &args[2]; - let default_seed = 1; + let default_seed = 1; // Parse the second argument as a number or use the default value let seed: i32 = args.get(3).map_or(default_seed, |arg| { arg.parse().unwrap_or_else(|_| { - eprintln!("Error parsing the seed argument. Using default value: {}", default_seed); + eprintln!( + "Error parsing the seed argument. Using default value: {}", + default_seed + ); default_seed }) }); println!("loading base map {}", map_path); - // Open the file + // Open the file let mut file = File::open(map_path).expect("Failed to open file"); // Read the file content into a string let mut contents = String::new(); - file.read_to_string(&mut contents).expect("Failed to read file"); + file.read_to_string(&mut contents) + .expect("Failed to read file"); // Deserialize the JSON string into your data structure let data: LdtkJson = from_str(&contents).expect("Failed to deserialize JSON"); - let config = MapGenerationConfig{seed, ..Default::default()}; + let config = MapGenerationConfig { + seed, + ..Default::default() + }; let context = from_map(&data, config); let mut generator = GeneratedMap::create(data); - - map_generation(context, &mut generator ).expect("Failed to generate map"); + map_generation(context, &mut generator).expect("Failed to generate map"); let data = generator.get_generated_map(); - // Convert JSON data to a pretty formatted string + // Convert JSON data to a pretty formatted string let pretty_json_string = to_string_pretty(&data).expect("Failed to serialize JSON"); // Open the file for writing @@ -65,7 +68,4 @@ fn main() { .expect("Failed to write to file"); println!("Generated map written to {}", map_output_path); - - } - diff --git a/examples/map_preview.rs b/examples/map_preview.rs index c8cf38a..ee46082 100644 --- a/examples/map_preview.rs +++ b/examples/map_preview.rs @@ -1,9 +1,11 @@ - -use bevy::prelude::*; +use bevy::{prelude::*, window::WindowResolution}; use bevy_ecs_ldtk::prelude::*; -use utils::camera::tod::{move_camera, setup_camera}; -use map::{generation::config::MapGenerationConfig, ldtk::loader::{get_asset_loader_generation, reload_map, setup_generated_map}}; +use map::{ + generation::config::MapGenerationConfig, + ldtk::loader::{get_asset_loader_generation, reload_map, setup_generated_map}, +}; +use utils::{camera::tod::{move_camera, setup_camera}, web::WebPlugin}; use bevy_inspector_egui::quick::WorldInspectorPlugin; @@ -12,50 +14,58 @@ use rand::Rng; use std::env; fn main() { - let level_loader = get_asset_loader_generation(); let map_generation_config = get_config(); + let window_plugin = WindowPlugin { + primary_window: Some(Window { + title: "Zombie".to_string(), + resolution: WindowResolution::new(800., 600.), + + resizable: true, + #[cfg(target_arch = "wasm32")] + canvas: Some("#bevy-canvas".to_string()), + ..Default::default() + }), + ..Default::default() + }; App::new() - .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) + .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()).set(window_plugin)) .add_plugins(WorldInspectorPlugin::new()) .add_plugins(LdtkPlugin) .add_systems(Startup, setup_camera) .add_systems(Startup, setup_generated_map) - .insert_resource(LdtkSettings{ - level_spawn_behavior: LevelSpawnBehavior::UseWorldTranslation { load_level_neighbors: false }, + .insert_resource(LdtkSettings { + level_spawn_behavior: LevelSpawnBehavior::UseWorldTranslation { + load_level_neighbors: false, + }, ..default() }) .insert_resource(map_generation_config) - .register_asset_loader(level_loader) - .add_systems( - Update, - ( - load_levels_if_not_present, - move_camera, - keyinput, - ), - ) + .add_systems(Update, (load_levels_if_not_present, move_camera, keyinput)) + .add_plugins(WebPlugin{}) .run(); - } - // should be a step before the game part fn load_levels_if_not_present( ldtk_project: Res>, mut level_set: Query<&mut LevelSet>, ) { - - if ldtk_project.is_empty() { return; } - let ids: Vec<_> = ldtk_project.ids().collect(); + if ldtk_project.is_empty() { + return; + } + let ids: Vec<_> = ldtk_project.ids().collect(); let id = ids.get(0).unwrap(); let ldtk_project = ldtk_project.get(*id).unwrap(); - let level_iids: Vec<_> = ldtk_project.data().iter_raw_levels().map(|l| l.iid.clone()).collect(); - + let level_iids: Vec<_> = ldtk_project + .data() + .iter_raw_levels() + .map(|l| l.iid.clone()) + .collect(); let mut level_set = level_set.iter_mut().last().unwrap(); if level_set.iids.len() > 0 { @@ -71,17 +81,19 @@ fn load_levels_if_not_present( } } - level_iids.iter().for_each(|id| { level_set.iids.insert(LevelIid::new(id)); }); + level_iids.iter().for_each(|id| { + level_set.iids.insert(LevelIid::new(id)); + }); } fn keyinput( input: Res>, level_query: Query>, - mut commands: Commands, asset_server: Res, + mut commands: Commands, + asset_server: Res, mut config: ResMut, mut level_set: Query<&mut LevelSet>, ) { - if input.just_pressed(KeyCode::KeyR) { for level_entity in &level_query { commands.entity(level_entity).despawn_recursive() @@ -92,7 +104,6 @@ fn keyinput( level_set.iids.clear(); } } - } fn get_config() -> MapGenerationConfig { @@ -118,5 +129,3 @@ fn get_config() -> MapGenerationConfig { ..Default::default() } } - - diff --git a/website/game.html b/website/game.html new file mode 100644 index 0000000..a5157af --- /dev/null +++ b/website/game.html @@ -0,0 +1,30 @@ + + + + + + + + + \ No newline at end of file diff --git a/website/index.html b/website/index.html new file mode 100644 index 0000000..a017936 --- /dev/null +++ b/website/index.html @@ -0,0 +1,14 @@ + + + + + + +
+ +
+
+ +
+ + \ No newline at end of file