Moved ship behaviors to world
parent
8ec3ece500
commit
85da817ed6
|
@ -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",
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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 }
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 }
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<f32>,
|
||||
|
||||
/// This particle's velocity, in world coordinates
|
||||
pub velocity: Vector2<f32>,
|
||||
|
||||
/// This particle's angle
|
||||
pub angle: Rad<f32>,
|
||||
|
||||
/// This particle's angular velocity (rad/sec)
|
||||
pub angvel: Rad<f32>,
|
||||
|
||||
/// 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<f32>,
|
||||
parent_angle: Rad<f32>,
|
||||
parent_velocity: Vector2<f32>,
|
||||
target_velocity: Vector2<f32>,
|
||||
) -> 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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<f32>,
|
||||
|
||||
/// This particle's velocity, in world coordinates
|
||||
pub velocity: Vector2<f32>,
|
||||
|
||||
/// This particle's angle
|
||||
pub angle: Rad<f32>,
|
||||
|
||||
/// This particle's angular velocity (rad/sec)
|
||||
pub angvel: Rad<f32>,
|
||||
|
||||
/// 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<f32>,
|
||||
parent_angle: Rad<f32>,
|
||||
parent_velocity: Vector2<f32>,
|
||||
target_velocity: Vector2<f32>,
|
||||
) -> 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)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<ParticleBuilder>,
|
||||
|
||||
/// 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<f32>,
|
||||
}
|
Loading…
Reference in New Issue