From 4815544f4e63f72060daa13a19a9a572d95b99e8 Mon Sep 17 00:00:00 2001 From: Thierry Berger Date: Wed, 6 Dec 2023 18:08:36 +0100 Subject: [PATCH 01/10] wip integration --- Cargo.lock | 1 + Cargo.toml | 11 ++- src/buildings.rs | 199 +++++++++++++++++++++++++++++++++++++++++++++++ src/inventory.rs | 96 +++++++++++++++++++++++ src/lib.rs | 3 + src/random.rs | 32 ++++++++ src/turret.rs | 50 +++++++++++- 7 files changed, 384 insertions(+), 8 deletions(-) create mode 100644 src/buildings.rs create mode 100644 src/inventory.rs create mode 100644 src/random.rs diff --git a/Cargo.lock b/Cargo.lock index 480ce20..d754b4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -885,6 +885,7 @@ dependencies = [ "hexx", "image", "rand", + "rand_chacha", "winit", ] diff --git a/Cargo.toml b/Cargo.toml index 259cdee..cc11064 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "bevy_game" # ToDo +name = "bevy_game" # ToDo version = "0.1.0" publish = false -authors = ["Niklas Eicker "] # ToDo: you are the author ;) +authors = ["Niklas Eicker "] # ToDo: you are the author ;) edition = "2021" exclude = ["dist", "build", "assets", "credits"] @@ -22,14 +22,12 @@ inherits = "release" lto = "thin" [features] -dev = [ - "bevy/dynamic_linking", -] +dev = ["bevy/dynamic_linking"] # All of Bevy's default features exept for the audio related ones, since they clash with bevy_kira_audio # and android_shared_stdcxx, since that is covered in `mobile` [dependencies] -bevy = { version="0.12", default-features = false, features = [ +bevy = { version = "0.12", default-features = false, features = [ "animation", "bevy_asset", "bevy_gilrs", @@ -56,6 +54,7 @@ bevy = { version="0.12", default-features = false, features = [ bevy_kira_audio = { version = "0.18" } bevy_asset_loader = { version = "0.18" } rand = { version = "0.8.3" } +rand_chacha = "0.3" # keep the following in sync with Bevy's dependencies winit = { version = "0.28", default-features = false } diff --git a/src/buildings.rs b/src/buildings.rs new file mode 100644 index 0000000..cc7c7e8 --- /dev/null +++ b/src/buildings.rs @@ -0,0 +1,199 @@ +use crate::inventory::{self, Inventory}; +use bevy::ecs::system::EntityCommand; +use bevy::math::vec3; +use bevy::prelude::*; +use bevy::render::mesh::Indices; +use bevy::render::render_resource::PrimitiveTopology; +use bevy::sprite::MaterialMesh2dBundle; +use bevy::sprite::Mesh2dHandle; +use bevy::utils::HashMap; +use rand::seq::SliceRandom; + +pub struct Plugin; + +impl bevy::prelude::Plugin for Plugin { + fn build(&self, app: &mut App) { + app.add_plugins(inventory::InventoryPlugin::::default()); + app.add_systems(Startup, (create_assets, spawn_layout).chain()); + } +} + +#[derive(Resource)] +pub struct VisualAssets { + pub mesh_def: HashMap, + pub size_def: HashMap, + pub color_def: HashMap>, +} + +pub(crate) fn create_assets( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + commands.insert_resource(VisualAssets { + mesh_def: [ + ( + MeshType::Triangle, + meshes + .add( + Mesh::new(PrimitiveTopology::TriangleList) + .with_inserted_attribute( + Mesh::ATTRIBUTE_POSITION, + vec![[-0.5, -0.5, 0.0], [0.0, 0.5, 0.0], [0.5, -0.5, 0.0]], + ) + .with_indices(Some(Indices::U32(vec![0, 1, 2]))), + ) + .into(), + ), + ( + MeshType::Circle, + meshes.add(Mesh::from(shape::Circle::default())).into(), + ), + ( + MeshType::Quad, + meshes.add(Mesh::from(shape::Quad::default())).into(), + ), + ] + .into(), + size_def: [ + (SizeType::Big, 1f32), + (SizeType::Medium, 0.75f32), + (SizeType::Small, 0.5f32), + ] + .into(), + color_def: [ + ( + ColorType::Black, + materials.add(ColorMaterial::from(Color::BLACK)), + ), + ( + ColorType::White, + materials.add(ColorMaterial::from(Color::WHITE)), + ), + ( + ColorType::Pink, + materials.add(ColorMaterial::from(Color::PINK)), + ), + ( + ColorType::Blue, + materials.add(ColorMaterial::from(Color::BLUE)), + ), + ] + .into(), + }); +} + +const ITEM_VISUAL_SIZE: f32 = 64f32; + +pub(crate) fn spawn_layout(mut commands: Commands) { + let mut rng = crate::random::RandomDeterministic::new_from_seed(0); + let inventory = vec![ + commands.spawn(get_random_item(&mut rng)).id(), + commands.spawn(get_random_item(&mut rng)).id(), + commands.spawn(get_random_item(&mut rng)).id(), + commands.spawn(get_random_item(&mut rng)).id(), + commands.spawn(get_random_item(&mut rng)).id(), + commands.spawn(get_random_item(&mut rng)).id(), + ] + .into(); + commands.spawn(( + Inventory:: { + items: inventory, + ..default() + }, + rng, + inventory::InventoryVisualDef { + positions: vec![ + vec3(-350f32, 0f32, 0f32), + vec3(-350f32, ITEM_VISUAL_SIZE + 10f32, 0f32), + vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 2f32, 0f32), + vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 3f32, 0f32), + vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 4f32, 0f32), + vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 5f32, 0f32), + ], + }, + )); +} + +#[derive(Component, Clone, Copy, Hash, Eq, PartialEq)] +pub struct ItemType(MeshType, SizeType, ColorType); + +#[derive(Clone, Copy, Hash, Eq, PartialEq)] +pub enum MeshType { + Triangle, + Circle, + Quad, +} +#[derive(Clone, Copy, Hash, Eq, PartialEq)] +pub enum SizeType { + Small, + Medium, + Big, +} +#[derive(Clone, Copy, Hash, Eq, PartialEq)] +pub enum ColorType { + Black, + White, + Pink, + Blue, +} + +impl inventory::CommandVisualBuilder for ItemType { + type C = CreateItemDefVisual; + fn command_to_create_visual(&self) -> Self::C { + CreateItemDefVisual { item_type: *self } + } +} + +pub struct CreateItemDefVisual { + pub item_type: ItemType, +} + +impl EntityCommand for CreateItemDefVisual { + fn apply(self, id: Entity, world: &mut World) { + let assets = world.get_resource::().unwrap(); + let visual = (MaterialMesh2dBundle { + mesh: assets.mesh_def[&self.item_type.0].clone(), + transform: Transform::default().with_scale(Vec3::splat( + ITEM_VISUAL_SIZE * assets.size_def[&self.item_type.1], + )), + material: assets.color_def[&self.item_type.2].clone(), + ..default() + }); + world.entity_mut(id).insert(visual); + } +} + +pub fn get_random_item(rng: &mut crate::random::RandomDeterministic) -> ItemType { + let choices_mesh = [ + (MeshType::Triangle, 2), + (MeshType::Circle, 2), + (MeshType::Quad, 2), + ]; + let choices_size = [ + (SizeType::Big, 1), + (SizeType::Medium, 2), + (SizeType::Small, 1), + ]; + let choices_color = [ + (ColorType::Black, 5), + (ColorType::White, 5), + (ColorType::Pink, 1), + (ColorType::Blue, 1), + ]; + let item_type = ItemType( + choices_mesh + .choose_weighted(&mut rng.random, |i| i.1) + .unwrap() + .0, + choices_size + .choose_weighted(&mut rng.random, |i| i.1) + .unwrap() + .0, + choices_color + .choose_weighted(&mut rng.random, |i| i.1) + .unwrap() + .0, + ); + item_type +} diff --git a/src/inventory.rs b/src/inventory.rs new file mode 100644 index 0000000..99a7601 --- /dev/null +++ b/src/inventory.rs @@ -0,0 +1,96 @@ +use bevy::ecs::system::EntityCommand; +use bevy::prelude::*; +use std::collections::VecDeque; +use std::marker::PhantomData; + +pub struct InventoryPlugin { + _item_type: PhantomData, +} + +impl Default for InventoryPlugin { + fn default() -> Self { + Self { + _item_type: Default::default(), + } + } +} + +impl Plugin for InventoryPlugin { + fn build(&self, app: &mut App) { + app.add_systems( + PostUpdate, + ( + item_create_visual::, + apply_deferred, + item_reposition::, + ) + .chain(), + ); + } +} + +pub trait CommandVisualBuilder { + type C: EntityCommand; + fn command_to_create_visual(&self) -> Self::C; +} + +#[derive(Component)] +pub struct MarkerItemVisual; + +#[derive(Component)] +pub struct Inventory { + /// entities contained here have a MarkerItem component, it handles logic + /// their rendering is created via item_create_visual + pub items: VecDeque, + pub _item_type: PhantomData, +} + +impl Default for Inventory { + fn default() -> Self { + Self { + items: Default::default(), + _item_type: Default::default(), + } + } +} + +#[derive(Component)] +pub struct InventoryVisualDef { + pub positions: Vec, +} + +fn item_create_visual( + mut commands: Commands, + inventory: Query<(&Inventory, &InventoryVisualDef), Changed>>, + items_without_visual: Query<(Entity, &IT), Without>, +) { + for (inventory, visual_def) in inventory.iter() { + for item in inventory.items.iter().take(visual_def.positions.len()) { + let Ok(item) = items_without_visual.get(*item) else { + continue; + }; + let mut c = commands.entity(item.0); + c.add(item.1.command_to_create_visual()) + .insert(MarkerItemVisual); + } + } +} +fn item_reposition( + inventory: Query<(&Inventory, &InventoryVisualDef), Changed>>, + items_with_visual: Query<(Entity, &IT), With>, + mut q_transform: Query<&mut Transform>, +) { + for (inventory, visual_def) in inventory.iter() { + for (i, item) in inventory + .items + .iter() + .take(visual_def.positions.len()) + .enumerate() + { + let Ok(item) = items_with_visual.get(*item) else { + continue; + }; + q_transform.get_mut(item.0).unwrap().translation = visual_def.positions[i]; + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 8cf80b3..bd5ecf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,16 @@ mod actions; mod audio; mod board; +mod buildings; mod bullet; mod crystal; mod enemy; mod grid; +mod inventory; mod loading; mod menu; mod primitives; +mod random; mod turret; use crate::actions::ActionsPlugin; diff --git a/src/random.rs b/src/random.rs new file mode 100644 index 0000000..da74d2e --- /dev/null +++ b/src/random.rs @@ -0,0 +1,32 @@ +use bevy::prelude::*; +use rand::prelude::*; +use rand_chacha::ChaCha20Rng; +use std::marker::PhantomData; + +#[derive(Component)] +pub struct RandomDeterministic { + pub random: ChaCha20Rng, + seed: u64, +} + +impl Default for RandomDeterministic { + fn default() -> Self { + let seed = 0; //thread_rng().gen::(); + return Self::new_from_seed(seed); + } +} + +impl RandomDeterministic { + pub fn new_from_seed(seed: u64) -> RandomDeterministic { + Self { + random: ChaCha20Rng::seed_from_u64(seed), + seed, + } + } + pub fn reset(&mut self) { + *self = Self::new_from_seed(self.seed); + } + pub fn get_seed(&self) -> u64 { + self.seed + } +} diff --git a/src/turret.rs b/src/turret.rs index 7fa59c7..c92dddb 100644 --- a/src/turret.rs +++ b/src/turret.rs @@ -1,24 +1,33 @@ use std::{f32::consts::FRAC_PI_2, time::Duration}; use crate::{ + buildings::{self, ItemType}, bullet::SpawnBullet, enemy::Enemy, - grid::HexGrid, + grid::{HexCell, HexGrid}, + inventory::{self, Inventory}, primitives::{ target::{SourceWithTargetAccessor, Target}, view::{ auto_remove_target_when_out_of_range, scan_for_targets_in_range, EnterViewEvent, View, }, }, + random::RandomDeterministic, GameState, }; -use bevy::{ecs::system::EntityCommand, math::Vec3, prelude::*, sprite::SpriteBundle}; +use bevy::{ + ecs::system::{CommandQueue, EntityCommand, SystemState}, + math::Vec3, + prelude::*, + sprite::SpriteBundle, +}; use bevy_easings::{Ease, EaseFunction}; pub struct TurretPlugin; impl Plugin for TurretPlugin { fn build(&self, app: &mut App) { + app.add_plugins(buildings::Plugin); app.add_systems( Update, ( @@ -60,6 +69,43 @@ pub struct SpawnTurret { impl EntityCommand for SpawnTurret { fn apply(self, id: Entity, world: &mut World) { + let mut state: SystemState<( + Query<( + &mut RandomDeterministic, + &mut crate::inventory::Inventory, + )>, + Query<&buildings::ItemType>, + )> = SystemState::new(world); + + let mut new_item = || { + let (mut q_inventory, q_items) = state.get_mut(world); + + let (mut rng, mut inventory) = q_inventory.single_mut(); + + let Some(first_item) = inventory.items.front() else { + return None; + }; + let Ok(item_to_build) = q_items.get(*first_item) else { + return None; + }; + // TODO: check if we can build item_to_build (cooldown, space available, currency, ...) + // TODO: send an event if not possible. + // TODO: pay "price" ? + inventory.items.pop_front(); + drop(inventory); + let new_item = buildings::get_random_item(&mut rng); + let mut queue = CommandQueue::default(); + let new_item = world.spawn(new_item).id(); + Some(new_item) + }; + + let Some(new_item) = new_item() else { + return; + }; + let (mut q_inventory, q_items) = state.get_mut(world); + let (rng, mut inventory) = q_inventory.single_mut(); + + inventory.items.push_back(new_item); let texture = world.resource_scope(|_, asset_server: Mut| { asset_server.load("textures/DifferentTurrets/Turret01.png") }); From 293c0b17330490b5ed8630c27a82046f04c7d952 Mon Sep 17 00:00:00 2001 From: Thierry Berger Date: Wed, 6 Dec 2023 18:17:14 +0100 Subject: [PATCH 02/10] minor cleanup --- src/buildings.rs | 4 ++-- src/random.rs | 4 ++-- src/turret.rs | 32 +++++++++++++++++--------------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/buildings.rs b/src/buildings.rs index cc7c7e8..841ab1f 100644 --- a/src/buildings.rs +++ b/src/buildings.rs @@ -152,14 +152,14 @@ pub struct CreateItemDefVisual { impl EntityCommand for CreateItemDefVisual { fn apply(self, id: Entity, world: &mut World) { let assets = world.get_resource::().unwrap(); - let visual = (MaterialMesh2dBundle { + let visual = MaterialMesh2dBundle { mesh: assets.mesh_def[&self.item_type.0].clone(), transform: Transform::default().with_scale(Vec3::splat( ITEM_VISUAL_SIZE * assets.size_def[&self.item_type.1], )), material: assets.color_def[&self.item_type.2].clone(), ..default() - }); + }; world.entity_mut(id).insert(visual); } } diff --git a/src/random.rs b/src/random.rs index da74d2e..fbb37e9 100644 --- a/src/random.rs +++ b/src/random.rs @@ -1,7 +1,7 @@ use bevy::prelude::*; use rand::prelude::*; use rand_chacha::ChaCha20Rng; -use std::marker::PhantomData; + #[derive(Component)] pub struct RandomDeterministic { @@ -12,7 +12,7 @@ pub struct RandomDeterministic { impl Default for RandomDeterministic { fn default() -> Self { let seed = 0; //thread_rng().gen::(); - return Self::new_from_seed(seed); + Self::new_from_seed(seed) } } diff --git a/src/turret.rs b/src/turret.rs index c92dddb..85be249 100644 --- a/src/turret.rs +++ b/src/turret.rs @@ -1,11 +1,10 @@ use std::{f32::consts::FRAC_PI_2, time::Duration}; use crate::{ - buildings::{self, ItemType}, + buildings::{self}, bullet::SpawnBullet, enemy::Enemy, - grid::{HexCell, HexGrid}, - inventory::{self, Inventory}, + grid::{HexGrid}, primitives::{ target::{SourceWithTargetAccessor, Target}, view::{ @@ -16,7 +15,7 @@ use crate::{ GameState, }; use bevy::{ - ecs::system::{CommandQueue, EntityCommand, SystemState}, + ecs::system::{EntityCommand, SystemState}, math::Vec3, prelude::*, sprite::SpriteBundle, @@ -80,32 +79,35 @@ impl EntityCommand for SpawnTurret { let mut new_item = || { let (mut q_inventory, q_items) = state.get_mut(world); - let (mut rng, mut inventory) = q_inventory.single_mut(); + let (_rng, mut inventory) = q_inventory.single_mut(); - let Some(first_item) = inventory.items.front() else { + let Some(first_item) = inventory.items.front().cloned() else { return None; }; - let Ok(item_to_build) = q_items.get(*first_item) else { + let Ok(_item_to_build) = q_items.get(first_item) else { return None; }; // TODO: check if we can build item_to_build (cooldown, space available, currency, ...) // TODO: send an event if not possible. // TODO: pay "price" ? inventory.items.pop_front(); - drop(inventory); + + /* let new_item = buildings::get_random_item(&mut rng); - let mut queue = CommandQueue::default(); - let new_item = world.spawn(new_item).id(); - Some(new_item) + let new_item = world.spawn(new_item).id();*/ + Some((first_item, first_item)) }; - let Some(new_item) = new_item() else { + let Some((item_built, _new_item)) = new_item() else { return; }; - let (mut q_inventory, q_items) = state.get_mut(world); - let (rng, mut inventory) = q_inventory.single_mut(); + // TODO: reuse that entity to merge it with turret entity ? + world.despawn(item_built); + let (mut q_inventory, _q_items) = state.get_mut(world); + let (_rng, _inventory) = q_inventory.single_mut(); + + //inventory.items.push_back(new_item); - inventory.items.push_back(new_item); let texture = world.resource_scope(|_, asset_server: Mut| { asset_server.load("textures/DifferentTurrets/Turret01.png") }); From 1a971e4edcbc0efe9a86382a19dbcc285ef51eba Mon Sep 17 00:00:00 2001 From: Thomas Wilgenbus Date: Thu, 7 Dec 2023 14:12:55 +0100 Subject: [PATCH 03/10] Update src/turret.rs Co-authored-by: Thierry Berger --- src/turret.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/turret.rs b/src/turret.rs index 85be249..0a4f902 100644 --- a/src/turret.rs +++ b/src/turret.rs @@ -106,7 +106,7 @@ impl EntityCommand for SpawnTurret { let (mut q_inventory, _q_items) = state.get_mut(world); let (_rng, _inventory) = q_inventory.single_mut(); - //inventory.items.push_back(new_item); + inventory.items.push_back(new_item); let texture = world.resource_scope(|_, asset_server: Mut| { asset_server.load("textures/DifferentTurrets/Turret01.png") From 0fbf406586c243781fec0d3f4b4d13079c2238ec Mon Sep 17 00:00:00 2001 From: Thomas Wilgenbus Date: Thu, 14 Dec 2023 13:39:54 +0100 Subject: [PATCH 04/10] Refactoring --- src/buildings.rs | 146 ++++++++++++++++++++++++----------------------- src/inventory.rs | 101 ++++++++++++++++++++------------ src/turret.rs | 33 ++++++----- 3 files changed, 158 insertions(+), 122 deletions(-) diff --git a/src/buildings.rs b/src/buildings.rs index 841ab1f..aa8e283 100644 --- a/src/buildings.rs +++ b/src/buildings.rs @@ -1,4 +1,6 @@ -use crate::inventory::{self, Inventory}; +use crate::inventory::SpawnInventory; +use crate::inventory::{self}; +use crate::random::RandomDeterministic; use bevy::ecs::system::EntityCommand; use bevy::math::vec3; use bevy::prelude::*; @@ -13,16 +15,16 @@ pub struct Plugin; impl bevy::prelude::Plugin for Plugin { fn build(&self, app: &mut App) { - app.add_plugins(inventory::InventoryPlugin::::default()); + app.add_plugins(inventory::InventoryPlugin::::default()); app.add_systems(Startup, (create_assets, spawn_layout).chain()); } } #[derive(Resource)] pub struct VisualAssets { - pub mesh_def: HashMap, - pub size_def: HashMap, - pub color_def: HashMap>, + pub mesh_def: HashMap, + pub size_def: HashMap, + pub color_def: HashMap>, } pub(crate) fn create_assets( @@ -33,7 +35,7 @@ pub(crate) fn create_assets( commands.insert_resource(VisualAssets { mesh_def: [ ( - MeshType::Triangle, + BuildingMesh::Triangle, meshes .add( Mesh::new(PrimitiveTopology::TriangleList) @@ -46,36 +48,36 @@ pub(crate) fn create_assets( .into(), ), ( - MeshType::Circle, + BuildingMesh::Circle, meshes.add(Mesh::from(shape::Circle::default())).into(), ), ( - MeshType::Quad, + BuildingMesh::Quad, meshes.add(Mesh::from(shape::Quad::default())).into(), ), ] .into(), size_def: [ - (SizeType::Big, 1f32), - (SizeType::Medium, 0.75f32), - (SizeType::Small, 0.5f32), + (BuildingSize::Big, 1f32), + (BuildingSize::Medium, 0.75f32), + (BuildingSize::Small, 0.5f32), ] .into(), color_def: [ ( - ColorType::Black, + BuildingColor::Black, materials.add(ColorMaterial::from(Color::BLACK)), ), ( - ColorType::White, + BuildingColor::White, materials.add(ColorMaterial::from(Color::WHITE)), ), ( - ColorType::Pink, + BuildingColor::Pink, materials.add(ColorMaterial::from(Color::PINK)), ), ( - ColorType::Blue, + BuildingColor::Blue, materials.add(ColorMaterial::from(Color::BLUE)), ), ] @@ -88,112 +90,114 @@ const ITEM_VISUAL_SIZE: f32 = 64f32; pub(crate) fn spawn_layout(mut commands: Commands) { let mut rng = crate::random::RandomDeterministic::new_from_seed(0); let inventory = vec![ - commands.spawn(get_random_item(&mut rng)).id(), - commands.spawn(get_random_item(&mut rng)).id(), - commands.spawn(get_random_item(&mut rng)).id(), - commands.spawn(get_random_item(&mut rng)).id(), - commands.spawn(get_random_item(&mut rng)).id(), - commands.spawn(get_random_item(&mut rng)).id(), - ] - .into(); - commands.spawn(( - Inventory:: { - items: inventory, - ..default() - }, - rng, - inventory::InventoryVisualDef { - positions: vec![ - vec3(-350f32, 0f32, 0f32), - vec3(-350f32, ITEM_VISUAL_SIZE + 10f32, 0f32), - vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 2f32, 0f32), - vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 3f32, 0f32), - vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 4f32, 0f32), - vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 5f32, 0f32), - ], - }, - )); + commands.spawn(get_random_building(&mut rng)).id(), + commands.spawn(get_random_building(&mut rng)).id(), + commands.spawn(get_random_building(&mut rng)).id(), + commands.spawn(get_random_building(&mut rng)).id(), + commands.spawn(get_random_building(&mut rng)).id(), + commands.spawn(get_random_building(&mut rng)).id(), + ]; + commands + .spawn_empty() + .add(SpawnInventory::::new( + inventory, + inventory::InventoryConfiguration { + positions: vec![ + vec3(-350f32, 0f32, 0f32), + vec3(-350f32, ITEM_VISUAL_SIZE + 10f32, 0f32), + vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 2f32, 0f32), + vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 3f32, 0f32), + vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 4f32, 0f32), + vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 5f32, 0f32), + ], + }, + )) + .insert(RandomDeterministic::new_from_seed(0)); } #[derive(Component, Clone, Copy, Hash, Eq, PartialEq)] -pub struct ItemType(MeshType, SizeType, ColorType); +pub struct Building { + mesh: BuildingMesh, + size: BuildingSize, + color: BuildingColor, +} #[derive(Clone, Copy, Hash, Eq, PartialEq)] -pub enum MeshType { +pub enum BuildingMesh { Triangle, Circle, Quad, } #[derive(Clone, Copy, Hash, Eq, PartialEq)] -pub enum SizeType { +pub enum BuildingSize { Small, Medium, Big, } #[derive(Clone, Copy, Hash, Eq, PartialEq)] -pub enum ColorType { +pub enum BuildingColor { Black, White, Pink, Blue, } -impl inventory::CommandVisualBuilder for ItemType { - type C = CreateItemDefVisual; - fn command_to_create_visual(&self) -> Self::C { - CreateItemDefVisual { item_type: *self } +impl inventory::ItemSpriteBuilder for Building { + type C = BuildingItemSpriteBuilder; + fn build_sprite(&self) -> Self::C { + BuildingItemSpriteBuilder { building: *self } } } -pub struct CreateItemDefVisual { - pub item_type: ItemType, +pub struct BuildingItemSpriteBuilder { + pub building: Building, } -impl EntityCommand for CreateItemDefVisual { +impl EntityCommand for BuildingItemSpriteBuilder { fn apply(self, id: Entity, world: &mut World) { let assets = world.get_resource::().unwrap(); let visual = MaterialMesh2dBundle { - mesh: assets.mesh_def[&self.item_type.0].clone(), + mesh: assets.mesh_def[&self.building.mesh].clone(), transform: Transform::default().with_scale(Vec3::splat( - ITEM_VISUAL_SIZE * assets.size_def[&self.item_type.1], + ITEM_VISUAL_SIZE * assets.size_def[&self.building.size], )), - material: assets.color_def[&self.item_type.2].clone(), + material: assets.color_def[&self.building.color].clone(), ..default() }; world.entity_mut(id).insert(visual); } } -pub fn get_random_item(rng: &mut crate::random::RandomDeterministic) -> ItemType { +pub fn get_random_building(rng: &mut crate::random::RandomDeterministic) -> Building { let choices_mesh = [ - (MeshType::Triangle, 2), - (MeshType::Circle, 2), - (MeshType::Quad, 2), + (BuildingMesh::Triangle, 2), + (BuildingMesh::Circle, 2), + (BuildingMesh::Quad, 2), ]; let choices_size = [ - (SizeType::Big, 1), - (SizeType::Medium, 2), - (SizeType::Small, 1), + (BuildingSize::Big, 1), + (BuildingSize::Medium, 2), + (BuildingSize::Small, 1), ]; let choices_color = [ - (ColorType::Black, 5), - (ColorType::White, 5), - (ColorType::Pink, 1), - (ColorType::Blue, 1), + (BuildingColor::Black, 5), + (BuildingColor::White, 5), + (BuildingColor::Pink, 1), + (BuildingColor::Blue, 1), ]; - let item_type = ItemType( - choices_mesh + let building = Building { + mesh: choices_mesh .choose_weighted(&mut rng.random, |i| i.1) .unwrap() .0, - choices_size + size: choices_size .choose_weighted(&mut rng.random, |i| i.1) .unwrap() .0, - choices_color + color: choices_color .choose_weighted(&mut rng.random, |i| i.1) .unwrap() .0, - ); - item_type + }; + building } diff --git a/src/inventory.rs b/src/inventory.rs index 99a7601..1cba1a4 100644 --- a/src/inventory.rs +++ b/src/inventory.rs @@ -3,11 +3,12 @@ use bevy::prelude::*; use std::collections::VecDeque; use std::marker::PhantomData; -pub struct InventoryPlugin { +/// This plugin handles the creation of Items in the inventory +pub struct InventoryPlugin { _item_type: PhantomData, } -impl Default for InventoryPlugin { +impl Default for InventoryPlugin { fn default() -> Self { Self { _item_type: Default::default(), @@ -15,82 +16,108 @@ impl Default for InventoryPlugin { } } -impl Plugin for InventoryPlugin { +impl Plugin for InventoryPlugin { fn build(&self, app: &mut App) { app.add_systems( PostUpdate, ( - item_create_visual::, + item_create_sprite::, apply_deferred, - item_reposition::, + redraw_inventory_on_change::, ) .chain(), ); } } -pub trait CommandVisualBuilder { +pub trait ItemSpriteBuilder { type C: EntityCommand; - fn command_to_create_visual(&self) -> Self::C; + fn build_sprite(&self) -> Self::C; } #[derive(Component)] -pub struct MarkerItemVisual; +struct MarkerItemSpriteBuilt; #[derive(Component)] -pub struct Inventory { +pub struct Inventory { /// entities contained here have a MarkerItem component, it handles logic /// their rendering is created via item_create_visual pub items: VecDeque, - pub _item_type: PhantomData, + positions: Vec, + + _item_type: PhantomData, } -impl Default for Inventory { - fn default() -> Self { +pub struct SpawnInventory { + items: Vec, + configuration: InventoryConfiguration, + + _item_type: PhantomData, +} + +impl SpawnInventory +where + IT: Component + ItemSpriteBuilder, +{ + pub fn new(items: Vec, configuration: InventoryConfiguration) -> Self { Self { - items: Default::default(), + items, + configuration, _item_type: Default::default(), } } } -#[derive(Component)] -pub struct InventoryVisualDef { +/// Configuration for the inventory +/// positions: Vec - positions of the items in the inventory +/// TODO: should be relative to the inventory entity/transform +pub struct InventoryConfiguration { pub positions: Vec, } -fn item_create_visual( +impl EntityCommand for SpawnInventory +where + IT: Component + ItemSpriteBuilder, +{ + fn apply(self, id: Entity, world: &mut World) { + world.entity_mut(id).insert((Inventory:: { + items: self.items.into_iter().collect(), + positions: self.configuration.positions, + _item_type: self._item_type, + },)); + } +} + +fn item_create_sprite( mut commands: Commands, - inventory: Query<(&Inventory, &InventoryVisualDef), Changed>>, - items_without_visual: Query<(Entity, &IT), Without>, + inventory: Query<&Inventory, Changed>>, + items_without_visual: Query<(Entity, &IT), Without>, ) { - for (inventory, visual_def) in inventory.iter() { - for item in inventory.items.iter().take(visual_def.positions.len()) { - let Ok(item) = items_without_visual.get(*item) else { - continue; - }; - let mut c = commands.entity(item.0); - c.add(item.1.command_to_create_visual()) - .insert(MarkerItemVisual); + for inventory in inventory.iter() { + for item in inventory.items.iter().take(inventory.positions.len()) { + if let Ok((entity, item)) = items_without_visual.get(*item) { + let mut c = commands.entity(entity); + c.add(item.build_sprite()).insert(MarkerItemSpriteBuilt); + } } } } -fn item_reposition( - inventory: Query<(&Inventory, &InventoryVisualDef), Changed>>, - items_with_visual: Query<(Entity, &IT), With>, - mut q_transform: Query<&mut Transform>, + +fn redraw_inventory_on_change( + inventory: Query<&Inventory, Changed>>, + mut items_with_visual: Query<&mut Transform, (With, With)>, ) { - for (inventory, visual_def) in inventory.iter() { - for (i, item) in inventory + for inventory in inventory.iter() { + for (i, &item) in inventory .items .iter() - .take(visual_def.positions.len()) + .take(inventory.positions.len()) .enumerate() { - let Ok(item) = items_with_visual.get(*item) else { - continue; - }; - q_transform.get_mut(item.0).unwrap().translation = visual_def.positions[i]; + if let Ok(mut transform) = items_with_visual.get_mut(item) { + //TODO: should be relative to the inventory entity/transform + transform.translation = inventory.positions[i]; + } } } } diff --git a/src/turret.rs b/src/turret.rs index 0a4f902..ef567e3 100644 --- a/src/turret.rs +++ b/src/turret.rs @@ -4,7 +4,7 @@ use crate::{ buildings::{self}, bullet::SpawnBullet, enemy::Enemy, - grid::{HexGrid}, + grid::HexGrid, primitives::{ target::{SourceWithTargetAccessor, Target}, view::{ @@ -66,20 +66,26 @@ pub struct SpawnTurret { pub at_hex: Entity, } +type InventoryState<'w, 'q> = ( + Query< + 'w, + 'q, + ( + &'w mut RandomDeterministic, + &'w mut crate::inventory::Inventory, + ), + >, + Query<'w, 'q, &'w buildings::Building>, +); + impl EntityCommand for SpawnTurret { fn apply(self, id: Entity, world: &mut World) { - let mut state: SystemState<( - Query<( - &mut RandomDeterministic, - &mut crate::inventory::Inventory, - )>, - Query<&buildings::ItemType>, - )> = SystemState::new(world); + let mut state: SystemState = SystemState::new(world); let mut new_item = || { let (mut q_inventory, q_items) = state.get_mut(world); - let (_rng, mut inventory) = q_inventory.single_mut(); + let (mut rng, mut inventory) = q_inventory.single_mut(); let Some(first_item) = inventory.items.front().cloned() else { return None; @@ -92,19 +98,18 @@ impl EntityCommand for SpawnTurret { // TODO: pay "price" ? inventory.items.pop_front(); - /* - let new_item = buildings::get_random_item(&mut rng); - let new_item = world.spawn(new_item).id();*/ + let new_item = buildings::get_random_building(&mut rng); + let _new_item = world.spawn(new_item).id(); Some((first_item, first_item)) }; - let Some((item_built, _new_item)) = new_item() else { + let Some((item_built, new_item)) = new_item() else { return; }; // TODO: reuse that entity to merge it with turret entity ? world.despawn(item_built); let (mut q_inventory, _q_items) = state.get_mut(world); - let (_rng, _inventory) = q_inventory.single_mut(); + let (_rng, mut inventory) = q_inventory.single_mut(); inventory.items.push_back(new_item); From 4bd61de41147dd7023c75cf5445cae8887bf6fbc Mon Sep 17 00:00:00 2001 From: Thomas Wilgenbus Date: Thu, 14 Dec 2023 13:49:58 +0100 Subject: [PATCH 05/10] Remove unused closure wrapper --- src/turret.rs | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/turret.rs b/src/turret.rs index ef567e3..4e31d7c 100644 --- a/src/turret.rs +++ b/src/turret.rs @@ -82,32 +82,26 @@ impl EntityCommand for SpawnTurret { fn apply(self, id: Entity, world: &mut World) { let mut state: SystemState = SystemState::new(world); - let mut new_item = || { - let (mut q_inventory, q_items) = state.get_mut(world); + let (mut q_inventory, q_items) = state.get_mut(world); - let (mut rng, mut inventory) = q_inventory.single_mut(); + let (mut rng, mut inventory) = q_inventory.single_mut(); - let Some(first_item) = inventory.items.front().cloned() else { - return None; - }; - let Ok(_item_to_build) = q_items.get(first_item) else { - return None; - }; - // TODO: check if we can build item_to_build (cooldown, space available, currency, ...) - // TODO: send an event if not possible. - // TODO: pay "price" ? - inventory.items.pop_front(); - - let new_item = buildings::get_random_building(&mut rng); - let _new_item = world.spawn(new_item).id(); - Some((first_item, first_item)) + let Some(first_item) = inventory.items.front().cloned() else { + return; }; - - let Some((item_built, new_item)) = new_item() else { + let Ok(_item_to_build) = q_items.get(first_item) else { return; }; + // TODO: check if we can build item_to_build (cooldown, space available, currency, ...) + // TODO: send an event if not possible. + // TODO: pay "price" ? + inventory.items.pop_front(); + + let new_item = buildings::get_random_building(&mut rng); + let new_item = world.spawn(new_item).id(); + // TODO: reuse that entity to merge it with turret entity ? - world.despawn(item_built); + world.despawn(first_item); let (mut q_inventory, _q_items) = state.get_mut(world); let (_rng, mut inventory) = q_inventory.single_mut(); From a2e5394a7df642f3d1a10a5dd7d5aff140699d4d Mon Sep 17 00:00:00 2001 From: Thomas Wilgenbus Date: Thu, 14 Dec 2023 15:33:22 +0100 Subject: [PATCH 06/10] Add BuildingSpawner resource --- src/buildings.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++--- src/turret.rs | 52 +++++---------------------------------- 2 files changed, 66 insertions(+), 49 deletions(-) diff --git a/src/buildings.rs b/src/buildings.rs index aa8e283..3f8e4d8 100644 --- a/src/buildings.rs +++ b/src/buildings.rs @@ -1,7 +1,7 @@ use crate::inventory::SpawnInventory; use crate::inventory::{self}; use crate::random::RandomDeterministic; -use bevy::ecs::system::EntityCommand; +use bevy::ecs::system::{EntityCommand, SystemParam, SystemState}; use bevy::math::vec3; use bevy::prelude::*; use bevy::render::mesh::Indices; @@ -15,8 +15,65 @@ pub struct Plugin; impl bevy::prelude::Plugin for Plugin { fn build(&self, app: &mut App) { - app.add_plugins(inventory::InventoryPlugin::::default()); - app.add_systems(Startup, (create_assets, spawn_layout).chain()); + app.add_plugins(inventory::InventoryPlugin::::default()) + .init_resource::() + .add_systems(Startup, (create_assets, spawn_layout).chain()); + } +} + +#[derive(Resource)] +pub struct BuildingSpawner { + pub(crate) state: SystemState>, +} + +#[derive(SystemParam)] +pub(crate) struct GetNextBuildingParams<'w, 's> { + command: Commands<'w, 's>, + q_inventory: Query< + 'w, + 's, + ( + &'static mut RandomDeterministic, + &'static mut crate::inventory::Inventory, + ), + >, + q_buildings: Query<'w, 's, &'static Building>, +} + +impl FromWorld for BuildingSpawner { + fn from_world(world: &mut World) -> Self { + BuildingSpawner { + state: SystemState::new(world), + } + } +} + +impl BuildingSpawner { + pub fn get_next_building(&mut self, world: &mut World) -> Option { + let mut params = self.state.get_mut(world); + let (mut rng, mut inventory) = params.q_inventory.single_mut(); + + let Some(first_item) = inventory.items.front().cloned() else { + return None; + }; + let Ok(_item_to_build) = params.q_buildings.get(first_item) else { + return None; + }; + // TODO: check if we can build item_to_build (cooldown, space available, currency, ...) + // TODO: send an event if not possible. + // TODO: pay "price" ? + inventory.items.pop_front(); + + let new_building = get_random_building(&mut rng); + let new_item = params.command.spawn(new_building).id(); + + inventory.items.push_back(new_item); + + // TODO: reuse that entity to merge it with turret entity ? + world.despawn(first_item); + + self.state.apply(world); + Some(new_building) } } diff --git a/src/turret.rs b/src/turret.rs index 4e31d7c..df2307a 100644 --- a/src/turret.rs +++ b/src/turret.rs @@ -1,7 +1,7 @@ use std::{f32::consts::FRAC_PI_2, time::Duration}; use crate::{ - buildings::{self}, + buildings::{self, BuildingSpawner}, bullet::SpawnBullet, enemy::Enemy, grid::HexGrid, @@ -11,15 +11,9 @@ use crate::{ auto_remove_target_when_out_of_range, scan_for_targets_in_range, EnterViewEvent, View, }, }, - random::RandomDeterministic, GameState, }; -use bevy::{ - ecs::system::{EntityCommand, SystemState}, - math::Vec3, - prelude::*, - sprite::SpriteBundle, -}; +use bevy::{ecs::system::EntityCommand, math::Vec3, prelude::*, sprite::SpriteBundle}; use bevy_easings::{Ease, EaseFunction}; pub struct TurretPlugin; @@ -66,46 +60,12 @@ pub struct SpawnTurret { pub at_hex: Entity, } -type InventoryState<'w, 'q> = ( - Query< - 'w, - 'q, - ( - &'w mut RandomDeterministic, - &'w mut crate::inventory::Inventory, - ), - >, - Query<'w, 'q, &'w buildings::Building>, -); - impl EntityCommand for SpawnTurret { fn apply(self, id: Entity, world: &mut World) { - let mut state: SystemState = SystemState::new(world); - - let (mut q_inventory, q_items) = state.get_mut(world); - - let (mut rng, mut inventory) = q_inventory.single_mut(); - - let Some(first_item) = inventory.items.front().cloned() else { - return; - }; - let Ok(_item_to_build) = q_items.get(first_item) else { - return; - }; - // TODO: check if we can build item_to_build (cooldown, space available, currency, ...) - // TODO: send an event if not possible. - // TODO: pay "price" ? - inventory.items.pop_front(); - - let new_item = buildings::get_random_building(&mut rng); - let new_item = world.spawn(new_item).id(); - - // TODO: reuse that entity to merge it with turret entity ? - world.despawn(first_item); - let (mut q_inventory, _q_items) = state.get_mut(world); - let (_rng, mut inventory) = q_inventory.single_mut(); - - inventory.items.push_back(new_item); + // attach building to the turret + let _building = world.resource_scope(|world, mut building_spawner: Mut| { + building_spawner.get_next_building(world) + }); let texture = world.resource_scope(|_, asset_server: Mut| { asset_server.load("textures/DifferentTurrets/Turret01.png") From c59b5b2eedd22052767f100d3fa6a5e0a5a92d6d Mon Sep 17 00:00:00 2001 From: Thomas Wilgenbus Date: Thu, 14 Dec 2023 16:48:12 +0100 Subject: [PATCH 07/10] Add anchor point relative to bottom-left window size --- src/buildings.rs | 23 +++++++++++++++-------- src/lib.rs | 3 +++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/buildings.rs b/src/buildings.rs index 3f8e4d8..9ea83ed 100644 --- a/src/buildings.rs +++ b/src/buildings.rs @@ -1,8 +1,9 @@ use crate::inventory::SpawnInventory; use crate::inventory::{self}; use crate::random::RandomDeterministic; +use crate::window::WindowSize; use bevy::ecs::system::{EntityCommand, SystemParam, SystemState}; -use bevy::math::vec3; + use bevy::prelude::*; use bevy::render::mesh::Indices; use bevy::render::render_resource::PrimitiveTopology; @@ -143,8 +144,9 @@ pub(crate) fn create_assets( } const ITEM_VISUAL_SIZE: f32 = 64f32; +const PADDING: f32 = 10f32; -pub(crate) fn spawn_layout(mut commands: Commands) { +pub(crate) fn spawn_layout(mut commands: Commands, window_size: ResMut) { let mut rng = crate::random::RandomDeterministic::new_from_seed(0); let inventory = vec![ commands.spawn(get_random_building(&mut rng)).id(), @@ -154,18 +156,23 @@ pub(crate) fn spawn_layout(mut commands: Commands) { commands.spawn(get_random_building(&mut rng)).id(), commands.spawn(get_random_building(&mut rng)).id(), ]; + let anchor_point = Vec2::new( + -window_size.size.x / 2f32 + ITEM_VISUAL_SIZE / 2f32 + PADDING, + -window_size.size.y / 2f32 + (ITEM_VISUAL_SIZE + PADDING) * 5.5f32 + PADDING, + ) + .extend(0f32); commands .spawn_empty() .add(SpawnInventory::::new( inventory, inventory::InventoryConfiguration { positions: vec![ - vec3(-350f32, 0f32, 0f32), - vec3(-350f32, ITEM_VISUAL_SIZE + 10f32, 0f32), - vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 2f32, 0f32), - vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 3f32, 0f32), - vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 4f32, 0f32), - vec3(-350f32, (ITEM_VISUAL_SIZE + 10f32) * 5f32, 0f32), + anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 5f32, 0f32), + anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 4f32, 0f32), + anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 3f32, 0f32), + anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 2f32, 0f32), + anchor_point - Vec3::new(0f32, ITEM_VISUAL_SIZE + PADDING, 0f32), + anchor_point, ], }, )) diff --git a/src/lib.rs b/src/lib.rs index bd5ecf7..7794e62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ mod menu; mod primitives; mod random; mod turret; +mod window; use crate::actions::ActionsPlugin; use crate::audio::InternalAudioPlugin; @@ -28,6 +29,7 @@ use bullet::BulletPlugin; use crystal::CrystalPlugin; use grid::GridPlugin; use primitives::PrimitivesPlugin; +use window::GameWindowPlugin; #[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)] pub enum GameState { @@ -55,6 +57,7 @@ impl Plugin for GamePlugin { DefaultPickingPlugins, CrystalPlugin, PrimitivesPlugin, + GameWindowPlugin, )); #[cfg(debug_assertions)] From 140c83a8d44698f448907b7d0393cb65f34f6910 Mon Sep 17 00:00:00 2001 From: Thomas Wilgenbus Date: Thu, 14 Dec 2023 17:47:55 +0100 Subject: [PATCH 08/10] Allow anchor point to recalculate on window resize --- src/buildings.rs | 58 ++++++++++++++++++++++++++++++++---------------- src/inventory.rs | 2 +- src/turret.rs | 11 ++++----- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/buildings.rs b/src/buildings.rs index 9ea83ed..007704b 100644 --- a/src/buildings.rs +++ b/src/buildings.rs @@ -1,5 +1,5 @@ -use crate::inventory::SpawnInventory; use crate::inventory::{self}; +use crate::inventory::{Inventory, SpawnInventory}; use crate::random::RandomDeterministic; use crate::window::WindowSize; use bevy::ecs::system::{EntityCommand, SystemParam, SystemState}; @@ -17,13 +17,14 @@ pub struct Plugin; impl bevy::prelude::Plugin for Plugin { fn build(&self, app: &mut App) { app.add_plugins(inventory::InventoryPlugin::::default()) - .init_resource::() - .add_systems(Startup, (create_assets, spawn_layout).chain()); + .init_resource::() + .add_systems(Startup, (create_assets, spawn_layout).chain()) + .add_systems(Update, update_anchor_position); } } #[derive(Resource)] -pub struct BuildingSpawner { +pub struct BuildingInventory { pub(crate) state: SystemState>, } @@ -41,16 +42,16 @@ pub(crate) struct GetNextBuildingParams<'w, 's> { q_buildings: Query<'w, 's, &'static Building>, } -impl FromWorld for BuildingSpawner { +impl FromWorld for BuildingInventory { fn from_world(world: &mut World) -> Self { - BuildingSpawner { + BuildingInventory { state: SystemState::new(world), } } } -impl BuildingSpawner { - pub fn get_next_building(&mut self, world: &mut World) -> Option { +impl BuildingInventory { + pub fn next(&mut self, world: &mut World) -> Option { let mut params = self.state.get_mut(world); let (mut rng, mut inventory) = params.q_inventory.single_mut(); @@ -156,29 +157,48 @@ pub(crate) fn spawn_layout(mut commands: Commands, window_size: ResMut::new( inventory, inventory::InventoryConfiguration { - positions: vec![ - anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 5f32, 0f32), - anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 4f32, 0f32), - anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 3f32, 0f32), - anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 2f32, 0f32), - anchor_point - Vec3::new(0f32, ITEM_VISUAL_SIZE + PADDING, 0f32), - anchor_point, - ], + positions: positions_from_anchor_point(anchor_point), }, )) .insert(RandomDeterministic::new_from_seed(0)); } +fn positions_from_anchor_point(anchor_point: Vec3) -> Vec { + vec![ + anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 5f32, 0f32), + anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 4f32, 0f32), + anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 3f32, 0f32), + anchor_point - Vec3::new(0f32, (ITEM_VISUAL_SIZE + PADDING) * 2f32, 0f32), + anchor_point - Vec3::new(0f32, ITEM_VISUAL_SIZE + PADDING, 0f32), + anchor_point, + ] +} + +pub(crate) fn update_anchor_position( + window_size: ResMut, + mut q_inventory: Query<&mut Inventory>, +) { + let anchor_point: Vec3 = Vec3::new( + -window_size.size.x / 2f32 + ITEM_VISUAL_SIZE / 2f32 + PADDING, + -window_size.size.y / 2f32 + (ITEM_VISUAL_SIZE + PADDING) * 5.5f32 + PADDING, + 0f32, + ); + q_inventory.for_each_mut(|mut inventory| { + inventory.positions = positions_from_anchor_point(anchor_point); + }); +} + #[derive(Component, Clone, Copy, Hash, Eq, PartialEq)] pub struct Building { mesh: BuildingMesh, diff --git a/src/inventory.rs b/src/inventory.rs index 1cba1a4..070d090 100644 --- a/src/inventory.rs +++ b/src/inventory.rs @@ -43,7 +43,7 @@ pub struct Inventory { /// entities contained here have a MarkerItem component, it handles logic /// their rendering is created via item_create_visual pub items: VecDeque, - positions: Vec, + pub positions: Vec, _item_type: PhantomData, } diff --git a/src/turret.rs b/src/turret.rs index df2307a..56e6468 100644 --- a/src/turret.rs +++ b/src/turret.rs @@ -1,7 +1,7 @@ use std::{f32::consts::FRAC_PI_2, time::Duration}; use crate::{ - buildings::{self, BuildingSpawner}, + buildings::{self, BuildingInventory}, bullet::SpawnBullet, enemy::Enemy, grid::HexGrid, @@ -62,10 +62,11 @@ pub struct SpawnTurret { impl EntityCommand for SpawnTurret { fn apply(self, id: Entity, world: &mut World) { - // attach building to the turret - let _building = world.resource_scope(|world, mut building_spawner: Mut| { - building_spawner.get_next_building(world) - }); + // TODO: attach building to the turret + let _building = + world.resource_scope(|world, mut building_inventory: Mut| { + building_inventory.next(world) + }); let texture = world.resource_scope(|_, asset_server: Mut| { asset_server.load("textures/DifferentTurrets/Turret01.png") From 66c7213444519759b6530ff9980cbce99162d317 Mon Sep 17 00:00:00 2001 From: Thomas Wilgenbus Date: Thu, 14 Dec 2023 18:41:42 +0100 Subject: [PATCH 09/10] Fix states handling --- src/buildings.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/buildings.rs b/src/buildings.rs index 007704b..9e1c408 100644 --- a/src/buildings.rs +++ b/src/buildings.rs @@ -2,6 +2,7 @@ use crate::inventory::{self}; use crate::inventory::{Inventory, SpawnInventory}; use crate::random::RandomDeterministic; use crate::window::WindowSize; +use crate::GameState; use bevy::ecs::system::{EntityCommand, SystemParam, SystemState}; use bevy::prelude::*; @@ -18,8 +19,16 @@ impl bevy::prelude::Plugin for Plugin { fn build(&self, app: &mut App) { app.add_plugins(inventory::InventoryPlugin::::default()) .init_resource::() - .add_systems(Startup, (create_assets, spawn_layout).chain()) - .add_systems(Update, update_anchor_position); + .add_systems( + OnEnter(GameState::Playing), + (create_assets, spawn_layout).chain(), + ) + .add_systems( + Update, + update_anchor_position + .run_if(resource_changed::()) + .run_if(in_state(GameState::Playing)), + ); } } From 791d8fffe87746ad7a13c48e67088cd8eb450979 Mon Sep 17 00:00:00 2001 From: Thomas Wilgenbus Date: Fri, 15 Dec 2023 13:05:18 +0100 Subject: [PATCH 10/10] Lint --- src/actions/game_control.rs | 1 + src/actions/mod.rs | 2 -- src/audio.rs | 2 +- src/primitives/view.rs | 2 +- src/random.rs | 6 +++--- src/turret.rs | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/actions/game_control.rs b/src/actions/game_control.rs index a10d87d..85a9c06 100644 --- a/src/actions/game_control.rs +++ b/src/actions/game_control.rs @@ -1,5 +1,6 @@ use bevy::prelude::{Input, KeyCode, Res}; +#[allow(dead_code)] pub enum GameControl { Up, Down, diff --git a/src/actions/mod.rs b/src/actions/mod.rs index 5e58ae7..f5a7dd8 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -3,8 +3,6 @@ use bevy::prelude::*; pub mod cursor; mod game_control; -pub const FOLLOW_EPSILON: f32 = 5.; - pub struct ActionsPlugin; // This plugin listens for keyboard input and converts the input into Actions diff --git a/src/audio.rs b/src/audio.rs index 8a58e17..e2590ea 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -33,7 +33,7 @@ fn start_audio(mut commands: Commands, audio_assets: Res, audio: Re commands.insert_resource(FlyingAudio(handle)); } -fn control_flying_sound( +fn _control_flying_sound( actions: Res, audio: Res, mut audio_instances: ResMut>, diff --git a/src/primitives/view.rs b/src/primitives/view.rs index 51a5be9..6286cd9 100644 --- a/src/primitives/view.rs +++ b/src/primitives/view.rs @@ -140,7 +140,7 @@ pub fn auto_remove_target_when_out_of_range( } } -pub fn debug_range(mut gizmos: Gizmos, views: Query<(&View, &Transform)>) { +pub fn _debug_range(mut gizmos: Gizmos, views: Query<(&View, &Transform)>) { for (view, transform) in &views { gizmos.circle_2d(transform.translation.xy(), view.range, Color::LIME_GREEN); } diff --git a/src/random.rs b/src/random.rs index fbb37e9..f30e78d 100644 --- a/src/random.rs +++ b/src/random.rs @@ -2,7 +2,7 @@ use bevy::prelude::*; use rand::prelude::*; use rand_chacha::ChaCha20Rng; - +#[allow(dead_code)] #[derive(Component)] pub struct RandomDeterministic { pub random: ChaCha20Rng, @@ -23,10 +23,10 @@ impl RandomDeterministic { seed, } } - pub fn reset(&mut self) { + pub fn _reset(&mut self) { *self = Self::new_from_seed(self.seed); } - pub fn get_seed(&self) -> u64 { + pub fn _get_seed(&self) -> u64 { self.seed } } diff --git a/src/turret.rs b/src/turret.rs index 56e6468..52ac9c9 100644 --- a/src/turret.rs +++ b/src/turret.rs @@ -29,7 +29,7 @@ impl Plugin for TurretPlugin { process_enemy_enter_range, process_enemy_exit_range, animate_targeting, - //auto_fire, + auto_fire, ) .run_if(in_state(GameState::Playing)), );