From 85da817ed6423c11c949d1ebbda10e7932b6b620 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 9 Jan 2024 11:36:39 -0800 Subject: [PATCH] Moved ship behaviors to world --- Cargo.lock | 11 +-- Cargo.toml | 1 - crates/behavior/Cargo.toml | 22 ----- crates/behavior/src/behavior/dummy.rs | 23 ----- crates/behavior/src/behavior/mod.rs | 9 -- crates/behavior/src/behavior/player.rs | 48 --------- crates/behavior/src/lib.rs | 21 ---- crates/galactica/Cargo.toml | 1 - crates/world/src/behavior/mod.rs | 18 ++++ crates/world/src/behavior/null.rs | 19 ++++ .../{behavior => world}/src/behavior/point.rs | 38 +++---- crates/world/src/lib.rs | 98 +------------------ crates/world/src/particlebuilder.rs | 88 +++++++++++++++++ crates/world/src/stepresources.rs | 33 +++++++ 14 files changed, 185 insertions(+), 245 deletions(-) delete mode 100644 crates/behavior/Cargo.toml delete mode 100644 crates/behavior/src/behavior/dummy.rs delete mode 100644 crates/behavior/src/behavior/mod.rs delete mode 100644 crates/behavior/src/behavior/player.rs delete mode 100644 crates/behavior/src/lib.rs create mode 100644 crates/world/src/behavior/mod.rs create mode 100644 crates/world/src/behavior/null.rs rename crates/{behavior => world}/src/behavior/point.rs (75%) create mode 100644 crates/world/src/particlebuilder.rs create mode 100644 crates/world/src/stepresources.rs diff --git a/Cargo.lock b/Cargo.lock index b3414c1..2a1d270 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -579,7 +579,6 @@ version = "0.0.0" dependencies = [ "anyhow", "cgmath", - "galactica-behavior", "galactica-constants", "galactica-content", "galactica-gameobject", @@ -590,15 +589,6 @@ dependencies = [ "winit", ] -[[package]] -name = "galactica-behavior" -version = "0.0.0" -dependencies = [ - "cgmath", - "galactica-content", - "galactica-world", -] - [[package]] name = "galactica-constants" version = "0.0.0" @@ -647,6 +637,7 @@ dependencies = [ "cgmath", "galactica-constants", "galactica-content", + "galactica-gameobject", "galactica-packer", "galactica-world", "image", diff --git a/Cargo.toml b/Cargo.toml index ae9dfb2..758e115 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,6 @@ galactica-constants = { path = "crates/constants" } galactica-content = { path = "crates/content" } galactica-render = { path = "crates/render" } galactica-world = { path = "crates/world" } -galactica-behavior = { path = "crates/behavior" } galactica-gameobject = { path = "crates/gameobject" } galactica-packer = { path = "crates/packer" } galactica = { path = "crates/galactica" } diff --git a/crates/behavior/Cargo.toml b/crates/behavior/Cargo.toml deleted file mode 100644 index bd95e2c..0000000 --- a/crates/behavior/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "galactica-behavior" -description = "AI behaviors for Galactica" -categories = { workspace = true } -keywords = { workspace = true } -version = { workspace = true } -rust-version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -repository = { workspace = true } -license = { workspace = true } -documentation = { workspace = true } -readme = { workspace = true } - -[lints] -workspace = true - -[dependencies] -galactica-content = { workspace = true } -galactica-world = { workspace = true } -cgmath = { workspace = true } diff --git a/crates/behavior/src/behavior/dummy.rs b/crates/behavior/src/behavior/dummy.rs deleted file mode 100644 index c684f09..0000000 --- a/crates/behavior/src/behavior/dummy.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::ShipBehavior; -use galactica_content as content; -use galactica_world::{ShipPhysicsHandle, World}; - -/// Dummy ship behavior. -/// Does nothing. -pub struct Dummy { - _handle: ShipPhysicsHandle, -} - -impl Dummy { - /// Create a new ship controller - pub fn new(handle: ShipPhysicsHandle) -> Self { - Self { _handle: handle } - } -} - -impl ShipBehavior for Dummy { - fn update_controls(&mut self, _physics: &mut World, _content: &content::Content) {} - fn get_handle(&self) -> ShipPhysicsHandle { - return self._handle; - } -} diff --git a/crates/behavior/src/behavior/mod.rs b/crates/behavior/src/behavior/mod.rs deleted file mode 100644 index fc95c58..0000000 --- a/crates/behavior/src/behavior/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Various implementations of [`crate::ShipBehavior`] - -mod dummy; -mod player; -mod point; - -pub use dummy::Dummy; -pub use player::Player; -pub use point::Point; diff --git a/crates/behavior/src/behavior/player.rs b/crates/behavior/src/behavior/player.rs deleted file mode 100644 index 1983096..0000000 --- a/crates/behavior/src/behavior/player.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::ShipBehavior; -use galactica_content as content; -use galactica_world::{ShipPhysicsHandle, World}; - -/// Player ship behavior. -/// Controls a ship using controller input -pub struct Player { - handle: ShipPhysicsHandle, - - /// Turn left - pub key_left: bool, - - /// Turn right - pub key_right: bool, - - /// Fire guns - pub key_guns: bool, - - /// Foward thrust - pub key_thrust: bool, -} - -impl Player { - /// Make a new ship controller - pub fn new(handle: ShipPhysicsHandle) -> Self { - Self { - handle, - key_left: false, - key_right: false, - key_guns: false, - key_thrust: false, - } - } -} - -impl ShipBehavior for Player { - fn update_controls(&mut self, physics: &mut World, _content: &content::Content) { - let s = physics.get_ship_mut(&self.handle).unwrap(); - s.controls.left = self.key_left; - s.controls.right = self.key_right; - s.controls.guns = self.key_guns; - s.controls.thrust = self.key_thrust; - } - - fn get_handle(&self) -> ShipPhysicsHandle { - return self.handle; - } -} diff --git a/crates/behavior/src/lib.rs b/crates/behavior/src/lib.rs deleted file mode 100644 index 394e1cf..0000000 --- a/crates/behavior/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![warn(missing_docs)] - -//! Computer-controlled ship behaviors - -pub mod behavior; - -use galactica_content as content; -use galactica_world::{ShipPhysicsHandle, World}; - -/// Main behavior trait. Any struct that implements this -/// may be used to control a ship. -pub trait ShipBehavior -where - Self: Send, -{ - /// Update a ship's controls based on world state - fn update_controls(&mut self, physics: &mut World, content: &content::Content); - - /// Get the ship this behavior is attached to - fn get_handle(&self) -> ShipPhysicsHandle; -} diff --git a/crates/galactica/Cargo.toml b/crates/galactica/Cargo.toml index 85ffa3d..635d058 100644 --- a/crates/galactica/Cargo.toml +++ b/crates/galactica/Cargo.toml @@ -25,7 +25,6 @@ galactica-content = { workspace = true } galactica-render = { workspace = true } galactica-constants = { workspace = true } galactica-world = { workspace = true } -galactica-behavior = { workspace = true } galactica-gameobject = { workspace = true } winit = { workspace = true } diff --git a/crates/world/src/behavior/mod.rs b/crates/world/src/behavior/mod.rs new file mode 100644 index 0000000..4ded665 --- /dev/null +++ b/crates/world/src/behavior/mod.rs @@ -0,0 +1,18 @@ +//! Various implementations of [`crate::ShipBehavior`] + +mod null; +//mod point; + +pub use null::*; +//pub use point::Point; + +use crate::{objects::ShipControls, StepResources}; + +/// Main behavior trait. Any struct that implements this +/// may be used to control a ship. +pub trait ShipBehavior { + /// Update a ship's controls based on world state. + /// This method does not return anything, it modifies + /// the ship's controls in-place. + fn update_controls(&mut self, res: &StepResources) -> ShipControls; +} diff --git a/crates/world/src/behavior/null.rs b/crates/world/src/behavior/null.rs new file mode 100644 index 0000000..89d4115 --- /dev/null +++ b/crates/world/src/behavior/null.rs @@ -0,0 +1,19 @@ +use super::ShipBehavior; +use crate::{objects::ShipControls, StepResources}; + +/// The Null behaviors is assigned to objects that are not controlled by the computer. +/// Most notably, the player's ship has a Null behavior. +pub struct Null {} + +impl Null { + /// Create a new ship controller + pub fn new() -> Self { + Self {} + } +} + +impl ShipBehavior for Null { + fn update_controls(&mut self, _res: &StepResources) -> ShipControls { + ShipControls::new() + } +} diff --git a/crates/behavior/src/behavior/point.rs b/crates/world/src/behavior/point.rs similarity index 75% rename from crates/behavior/src/behavior/point.rs rename to crates/world/src/behavior/point.rs index 1e9ff96..2b1975d 100644 --- a/crates/behavior/src/behavior/point.rs +++ b/crates/world/src/behavior/point.rs @@ -1,24 +1,30 @@ use cgmath::{Deg, InnerSpace}; +use galactica_gameobject::GameData; -use crate::ShipBehavior; use galactica_content as content; -use galactica_world::{util, ShipPhysicsHandle, World}; + +use crate::World; + +use super::ShipBehavior; /// "Point" ship behavior. /// Point and shoot towards the nearest enemy. -pub struct Point { - handle: ShipPhysicsHandle, -} +pub struct Point {} impl Point { /// Create a new ship controller - pub fn new(handle: ShipPhysicsHandle) -> Self { - Self { handle } + pub fn new() -> Self { + Self {} } } impl ShipBehavior for Point { - fn update_controls(&mut self, physics: &mut World, content: &content::Content) { + fn update_controls( + &mut self, + physics: &mut World, + content: &content::Content, + data: &GameData, + ) { // Turn off all controls let s = physics.get_ship_mut(&self.handle).unwrap(); s.controls.left = false; @@ -27,20 +33,22 @@ impl ShipBehavior for Point { s.controls.thrust = false; let (my_s, my_r) = physics.get_ship_body(self.handle).unwrap(); + let my_data = data.get_ship(my_s.data_handle).unwrap(); let my_position = util::rigidbody_position(my_r); let my_rotation = util::rigidbody_rotation(my_r); let my_angvel = my_r.angvel(); - let my_faction = content.get_faction(my_s.ship.faction); + let my_faction = content.get_faction(my_data.get_faction()); // Iterate all possible targets let mut it = physics .iter_ship_body() - .filter( - |(s, _)| match my_faction.relationships.get(&s.ship.faction).unwrap() { + .filter(|(s, _)| { + let data = data.get_ship(s.data_handle).unwrap(); + match my_faction.relationships.get(&data.get_faction()).unwrap() { content::Relationship::Hostile => true, _ => false, - }, - ) + } + }) .map(|(_, r)| r); // Find the closest target @@ -75,8 +83,4 @@ impl ShipBehavior for Point { s.controls.guns = true; s.controls.thrust = false; } - - fn get_handle(&self) -> ShipPhysicsHandle { - return self.handle; - } } diff --git a/crates/world/src/lib.rs b/crates/world/src/lib.rs index b1e7f96..3fd894a 100644 --- a/crates/world/src/lib.rs +++ b/crates/world/src/lib.rs @@ -3,102 +3,14 @@ //! This module keeps track of the visible world. //! Ships, projectiles, collisions, etc. +pub mod behavior; pub mod objects; +mod particlebuilder; +mod stepresources; pub mod util; mod world; mod wrapper; -use cgmath::{Matrix2, Point2, Rad, Vector2}; -use galactica_content as content; -use rand::Rng; +pub use particlebuilder::*; +pub use stepresources::*; pub use world::World; - -use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle}; - -/// A lightweight handle for a specific ship in our physics system -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct ShipPhysicsHandle(pub RigidBodyHandle, ColliderHandle); - -/// Instructions to create a new particle -pub struct ParticleBuilder { - /// The sprite to use for this particle - pub sprite: content::SpriteHandle, - - /// This object's center, in world coordinates. - pub pos: Point2, - - /// This particle's velocity, in world coordinates - pub velocity: Vector2, - - /// This particle's angle - pub angle: Rad, - - /// This particle's angular velocity (rad/sec) - pub angvel: Rad, - - /// This particle's lifetime, in seconds - pub lifetime: f32, - - /// The size of this particle, - /// given as height in world units. - pub size: f32, - - /// Fade this particle out over this many seconds as it expires - pub fade: f32, -} - -impl ParticleBuilder { - /// Create a ParticleBuilder from an Effect - pub fn from_content( - effect: &content::Effect, - pos: Point2, - parent_angle: Rad, - parent_velocity: Vector2, - target_velocity: Vector2, - ) -> Self { - let mut rng = rand::thread_rng(); - - let velocity = { - let a = - rng.gen_range(-effect.velocity_scale_parent_rng..=effect.velocity_scale_parent_rng); - let b = - rng.gen_range(-effect.velocity_scale_target_rng..=effect.velocity_scale_target_rng); - - let velocity = ((effect.velocity_scale_parent + a) * parent_velocity) - + ((effect.velocity_scale_target + b) * target_velocity); - - Matrix2::from_angle(Rad( - rng.gen_range(-effect.direction_rng..=effect.direction_rng) - )) * velocity - }; - - // Rad has odd behavior when its angle is zero, so we need extra checks here - let angvel = if effect.angvel_rng == 0.0 { - effect.angvel - } else { - Rad(effect.angvel.0 + rng.gen_range(-effect.angvel_rng..=effect.angvel_rng)) - }; - let angle = if effect.angle_rng == 0.0 { - parent_angle + effect.angle - } else { - parent_angle + effect.angle + Rad(rng.gen_range(-effect.angle_rng..=effect.angle_rng)) - }; - - ParticleBuilder { - sprite: effect.sprite, - pos, - velocity, - - angle, - angvel, - - lifetime: 0f32 - .max(effect.lifetime + rng.gen_range(-effect.lifetime_rng..=effect.lifetime_rng)), - - // Make sure size isn't negative. This check should be on EVERY rng! - size: 0f32.max(effect.size + rng.gen_range(-effect.size_rng..=effect.size_rng)), - - fade: 0f32.max(effect.fade + rng.gen_range(-effect.fade_rng..=effect.fade_rng)), - } - } -} diff --git a/crates/world/src/particlebuilder.rs b/crates/world/src/particlebuilder.rs new file mode 100644 index 0000000..41de145 --- /dev/null +++ b/crates/world/src/particlebuilder.rs @@ -0,0 +1,88 @@ +use cgmath::{Matrix2, Point2, Rad, Vector2}; +use galactica_content as content; +use rand::Rng; + +/// Instructions to create a new particle +#[derive(Debug)] +pub struct ParticleBuilder { + /// The sprite to use for this particle + pub sprite: content::SpriteHandle, + + /// This object's center, in world coordinates. + pub pos: Point2, + + /// This particle's velocity, in world coordinates + pub velocity: Vector2, + + /// This particle's angle + pub angle: Rad, + + /// This particle's angular velocity (rad/sec) + pub angvel: Rad, + + /// This particle's lifetime, in seconds + pub lifetime: f32, + + /// The size of this particle, + /// given as height in world units. + pub size: f32, + + /// Fade this particle out over this many seconds as it expires + pub fade: f32, +} + +impl ParticleBuilder { + /// Create a ParticleBuilder from an Effect + pub fn from_content( + effect: &content::Effect, + pos: Point2, + parent_angle: Rad, + parent_velocity: Vector2, + target_velocity: Vector2, + ) -> Self { + let mut rng = rand::thread_rng(); + + let velocity = { + let a = + rng.gen_range(-effect.velocity_scale_parent_rng..=effect.velocity_scale_parent_rng); + let b = + rng.gen_range(-effect.velocity_scale_target_rng..=effect.velocity_scale_target_rng); + + let velocity = ((effect.velocity_scale_parent + a) * parent_velocity) + + ((effect.velocity_scale_target + b) * target_velocity); + + Matrix2::from_angle(Rad( + rng.gen_range(-effect.direction_rng..=effect.direction_rng) + )) * velocity + }; + + // Rad has odd behavior when its angle is zero, so we need extra checks here + let angvel = if effect.angvel_rng == 0.0 { + effect.angvel + } else { + Rad(effect.angvel.0 + rng.gen_range(-effect.angvel_rng..=effect.angvel_rng)) + }; + let angle = if effect.angle_rng == 0.0 { + parent_angle + effect.angle + } else { + parent_angle + effect.angle + Rad(rng.gen_range(-effect.angle_rng..=effect.angle_rng)) + }; + + ParticleBuilder { + sprite: effect.sprite, + pos, + velocity, + + angle, + angvel, + + lifetime: 0f32 + .max(effect.lifetime + rng.gen_range(-effect.lifetime_rng..=effect.lifetime_rng)), + + // Make sure size isn't negative. This check should be on EVERY rng! + size: 0f32.max(effect.size + rng.gen_range(-effect.size_rng..=effect.size_rng)), + + fade: 0f32.max(effect.fade + rng.gen_range(-effect.fade_rng..=effect.fade_rng)), + } + } +} diff --git a/crates/world/src/stepresources.rs b/crates/world/src/stepresources.rs new file mode 100644 index 0000000..8e86be8 --- /dev/null +++ b/crates/world/src/stepresources.rs @@ -0,0 +1,33 @@ +use crate::{objects::ShipControls, ParticleBuilder}; +use cgmath::Point2; +use galactica_content::Content; +use galactica_gameobject::{GameData, GameShipHandle}; + +/// External resources we need to compute time steps +#[derive(Debug)] +pub struct StepResources<'a> { + /// Game content + pub ct: &'a Content, + + /// Game data + pub dt: &'a mut GameData, + + /// Length of time step + pub t: f32, + + /// Particles to create + pub particles: &'a mut Vec, + + /// Player inputs + pub player_controls: ShipControls, + + /// The ship that the player controls + pub player: GameShipHandle, +} + +/// Return values after computing time steps +#[derive(Debug)] +pub struct StepOutput { + /// The player's position in world coordinates + pub player_position: Point2, +}