master
Mark 2024-01-01 12:01:29 -08:00
parent afa6bd6690
commit b309a074dc
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
12 changed files with 137 additions and 21 deletions

View File

@ -1,14 +1,24 @@
#![warn(missing_docs)]
//! Compile-time parameters
/// Minimum zoom level
pub const ZOOM_MIN: f32 = 200.0; pub const ZOOM_MIN: f32 = 200.0;
/// Maximum zoom level
pub const ZOOM_MAX: f32 = 2000.0; pub const ZOOM_MAX: f32 = 2000.0;
/// Z-axis range for starfield stars /// Z-axis range for starfield stars
/// This does not affect scale. /// This does not affect scale.
pub const STARFIELD_Z_MIN: f32 = 100.0; pub const STARFIELD_Z_MIN: f32 = 100.0;
/// Z-axis range for starfield stars
/// This does not affect scale.
pub const STARFIELD_Z_MAX: f32 = 200.0; pub const STARFIELD_Z_MAX: f32 = 200.0;
/// Size range for starfield stars, in game units. /// Size range for starfield stars, in game units.
/// This is scaled for zoom, but NOT for distance. /// This is scaled for zoom, but NOT for distance.
pub const STARFIELD_SIZE_MIN: f32 = 0.2; pub const STARFIELD_SIZE_MIN: f32 = 0.2;
/// Size range for starfield stars, in game units.
/// This is scaled for zoom, but NOT for distance.
pub const STARFIELD_SIZE_MAX: f32 = 1.8; pub const STARFIELD_SIZE_MAX: f32 = 1.8;
/// Size of a square starfield tile, in game units. /// Size of a square starfield tile, in game units.
@ -34,9 +44,8 @@ pub const CONTENT_ROOT: &'static str = "./content";
/// Root directory of game textures /// Root directory of game textures
pub const TEXTURE_ROOT: &'static str = "./assets/render"; pub const TEXTURE_ROOT: &'static str = "./assets/render";
// We can draw at most this many sprites on the screen. /// We can draw at most this many sprites on the screen.
// TODO: compile-time option or config file
pub const SPRITE_INSTANCE_LIMIT: u64 = 500; pub const SPRITE_INSTANCE_LIMIT: u64 = 500;
// Must be small enough to fit in an i32 /// Must be small enough to fit in an i32
pub const STARFIELD_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24; pub const STARFIELD_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24;

View File

@ -1,3 +1,9 @@
#![warn(missing_docs)]
//! This module keeps track of all objects and interactions in the game:
//! Ships, projectiles, collisions, etc.
//! TODO: Rename & re-organize
pub mod objects; pub mod objects;
mod physics; mod physics;
pub mod util; pub mod util;
@ -7,5 +13,6 @@ pub use physics::Physics;
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle}; use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
/// A lightweight handle for a specific ship in a physics system
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ShipHandle(pub RigidBodyHandle, ColliderHandle); pub struct ShipHandle(pub RigidBodyHandle, ColliderHandle);

View File

@ -5,12 +5,18 @@ use galactica_render::ObjectSubSprite;
/// Represents a gun attached to a specific ship at a certain gunpoint. /// Represents a gun attached to a specific ship at a certain gunpoint.
#[derive(Debug)] #[derive(Debug)]
pub struct ShipGun { pub struct ShipGun {
/// The kind of gun this is
pub kind: content::Gun, pub kind: content::Gun,
/// How many seconds we must wait before this gun can fire
pub cooldown: f32, pub cooldown: f32,
/// Index of the gunpoint this gun is attached to
pub point: usize, pub point: usize,
} }
impl ShipGun { impl ShipGun {
/// Make a new shipgun
pub fn new(kind: content::Gun, point: usize) -> Self { pub fn new(kind: content::Gun, point: usize) -> Self {
Self { Self {
kind: kind, kind: kind,
@ -63,11 +69,15 @@ impl OutfitStatSum {
/// Represents all the outfits attached to a ship. /// Represents all the outfits attached to a ship.
/// Keeps track of the sum of their stats, so it mustn't be re-computed every frame. /// Keeps track of the sum of their stats, so it mustn't be re-computed every frame.
/// TODO: rename (outfitset?)
/// TODO: ctrl-f outfitset in comments
#[derive(Debug)] #[derive(Debug)]
pub struct ShipOutfits { pub struct ShipOutfits {
/// The total sum of the stats this set of outfits provides
/// TODO: this shouldn't be pub
pub stats: OutfitStatSum, pub stats: OutfitStatSum,
pub total_space: content::OutfitSpace,
//pub total_space: content::OutfitSpace,
available_space: content::OutfitSpace, available_space: content::OutfitSpace,
outfits: Vec<content::Outfit>, outfits: Vec<content::Outfit>,
guns: Vec<ShipGun>, guns: Vec<ShipGun>,
@ -80,13 +90,14 @@ pub struct ShipOutfits {
} }
impl<'a> ShipOutfits { impl<'a> ShipOutfits {
/// Make a new outfit array
pub fn new(content: &content::Ship) -> Self { pub fn new(content: &content::Ship) -> Self {
Self { Self {
stats: OutfitStatSum::new(), stats: OutfitStatSum::new(),
outfits: Vec::new(), outfits: Vec::new(),
guns: Vec::new(), guns: Vec::new(),
available_space: content.space.clone(), available_space: content.space.clone(),
total_space: content.space.clone(), //total_space: content.space.clone(),
enginepoints: content.engines.clone(), enginepoints: content.engines.clone(),
gunpoints: content.guns.clone(), gunpoints: content.guns.clone(),
engine_flare_sprites: vec![], engine_flare_sprites: vec![],
@ -153,10 +164,12 @@ impl<'a> ShipOutfits {
return true; return true;
} }
/// Iterate over all guns in this outfitset
pub fn iter_guns(&mut self) -> impl Iterator<Item = &mut ShipGun> { pub fn iter_guns(&mut self) -> impl Iterator<Item = &mut ShipGun> {
self.guns.iter_mut() self.guns.iter_mut()
} }
/// Iterate over all guns and the gunpoints they're attached to
pub fn iter_guns_points(&mut self) -> impl Iterator<Item = (&mut ShipGun, &content::GunPoint)> { pub fn iter_guns_points(&mut self) -> impl Iterator<Item = (&mut ShipGun, &content::GunPoint)> {
self.guns self.guns
.iter_mut() .iter_mut()
@ -164,6 +177,7 @@ impl<'a> ShipOutfits {
.map(|(a, b)| (b, a)) .map(|(a, b)| (b, a))
} }
/// Update engine flare sprites
pub fn update_engine_flares(&mut self) { pub fn update_engine_flares(&mut self) {
// TODO: better way to pick flare texture // TODO: better way to pick flare texture
self.engine_flare_sprites.clear(); self.engine_flare_sprites.clear();
@ -189,6 +203,7 @@ impl<'a> ShipOutfits {
.collect(); .collect();
} }
/// Get the sprites we should show if this ship is firing its engines
pub fn get_engine_flares(&self) -> Vec<ObjectSubSprite> { pub fn get_engine_flares(&self) -> Vec<ObjectSubSprite> {
return self.engine_flare_sprites.clone(); return self.engine_flare_sprites.clone();
} }

View File

@ -8,50 +8,81 @@ use crate::util;
use galactica_content as content; use galactica_content as content;
use galactica_render::ObjectSprite; use galactica_render::ObjectSprite;
/// A helper struct that contains parameters for a projectile
/// TODO: decouple data from physics
pub struct ProjectileBuilder { pub struct ProjectileBuilder {
/// TODO
pub rigid_body: RigidBodyBuilder, pub rigid_body: RigidBodyBuilder,
/// TODO
pub collider: ColliderBuilder, pub collider: ColliderBuilder,
/// TODO
pub sprite_texture: content::TextureHandle, pub sprite_texture: content::TextureHandle,
/// TODO
pub lifetime: f32, pub lifetime: f32,
/// TODO
pub size: f32, pub size: f32,
/// TODO
pub damage: f32, pub damage: f32,
/// TODO
pub faction: content::FactionHandle, pub faction: content::FactionHandle,
} }
impl ProjectileBuilder { /// A single projectile in the world
pub fn build(self, r: RigidBodyHandle, c: ColliderHandle) -> Projectile {
Projectile {
rigid_body: r,
collider: c,
sprite_texture: self.sprite_texture,
lifetime: self.lifetime,
size: self.size,
damage: self.damage,
faction: self.faction,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Projectile { pub struct Projectile {
/// TODO
pub rigid_body: RigidBodyHandle, pub rigid_body: RigidBodyHandle,
/// TODO
pub collider: ColliderHandle, pub collider: ColliderHandle,
/// TODO
pub sprite_texture: content::TextureHandle, pub sprite_texture: content::TextureHandle,
/// TODO
pub lifetime: f32, pub lifetime: f32,
/// TODOd
pub size: f32, pub size: f32,
/// TODO
pub damage: f32, pub damage: f32,
/// TODO
pub faction: content::FactionHandle, pub faction: content::FactionHandle,
} }
impl Projectile { impl Projectile {
/// Make a new projectile
pub fn new(b: ProjectileBuilder, r: RigidBodyHandle, c: ColliderHandle) -> Self {
Projectile {
rigid_body: r,
collider: c,
sprite_texture: b.sprite_texture,
lifetime: b.lifetime,
size: b.size,
damage: b.damage,
faction: b.faction,
}
}
/// Update this projectile's state after `t` seconds
pub fn tick(&mut self, t: f32) { pub fn tick(&mut self, t: f32) {
self.lifetime -= t; self.lifetime -= t;
} }
/// Is this projectile expired?
pub fn is_expired(&self) -> bool { pub fn is_expired(&self) -> bool {
return self.lifetime < 0.0; return self.lifetime < 0.0;
} }
/// Get this projectiles' sprite
pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite { pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite {
let pos = util::rigidbody_position(r); let pos = util::rigidbody_position(r);
let rot = util::rigidbody_rotation(r); let rot = util::rigidbody_rotation(r);

View File

@ -35,10 +35,19 @@ impl ShipControls {
} }
} }
/// A ship instance in the physics system
/// TODO: Decouple ship data from physics
pub struct Ship { pub struct Ship {
/// TODO
pub physics_handle: ShipHandle, pub physics_handle: ShipHandle,
/// TODO
pub faction: FactionHandle, pub faction: FactionHandle,
/// TODO
pub hull: f32, pub hull: f32,
/// TODO
pub controls: ShipControls, pub controls: ShipControls,
outfits: ShipOutfits, outfits: ShipOutfits,
@ -47,6 +56,7 @@ pub struct Ship {
} }
impl Ship { impl Ship {
/// Make a new ship
pub fn new( pub fn new(
c: &content::Ship, c: &content::Ship,
outfits: ShipOutfits, outfits: ShipOutfits,
@ -64,6 +74,7 @@ impl Ship {
} }
} }
/// Fire this ship's guns. Called every frame, if this ship is firing.
pub fn fire_guns(&mut self, r: &RigidBody) -> Vec<ProjectileBuilder> { pub fn fire_guns(&mut self, r: &RigidBody) -> Vec<ProjectileBuilder> {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let mut out = Vec::new(); let mut out = Vec::new();
@ -154,6 +165,7 @@ impl Ship {
return ShipTickResult { projectiles: p }; return ShipTickResult { projectiles: p };
} }
/// Get this ship's sprite
pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite { pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite {
let ship_pos = util::rigidbody_position(r); let ship_pos = util::rigidbody_position(r);
let ship_rot = util::rigidbody_rotation(r); let ship_rot = util::rigidbody_rotation(r);

View File

@ -8,7 +8,12 @@ use rapier2d::{
}; };
use std::{collections::HashMap, f32::consts::PI}; use std::{collections::HashMap, f32::consts::PI};
use crate::{objects, objects::ShipOutfits, wrapper::Wrapper, ShipHandle}; use crate::{
objects,
objects::{Projectile, ShipOutfits},
wrapper::Wrapper,
ShipHandle,
};
use galactica_content as content; use galactica_content as content;
use galactica_render::ObjectSprite; use galactica_render::ObjectSprite;
@ -59,13 +64,14 @@ impl Physics {
r, r,
&mut self.wrapper.rigid_body_set, &mut self.wrapper.rigid_body_set,
); );
self.projectiles.insert(c, pb.build(r, c)); self.projectiles.insert(c, Projectile::new(pb, r, c));
return c; return c;
} }
} }
// Public methods // Public methods
impl Physics { impl Physics {
/// Create a new physics system
pub fn new() -> Self { pub fn new() -> Self {
let (collision_send, collision_queue) = crossbeam::channel::unbounded(); let (collision_send, collision_queue) = crossbeam::channel::unbounded();
let (contact_force_send, _) = crossbeam::channel::unbounded(); let (contact_force_send, _) = crossbeam::channel::unbounded();
@ -79,6 +85,8 @@ impl Physics {
} }
} }
/// Add a ship to this physics system
/// TODO: decouple from Ship::new()
pub fn add_ship( pub fn add_ship(
&mut self, &mut self,
ct: &content::Ship, ct: &content::Ship,
@ -114,6 +122,7 @@ impl Physics {
return h; return h;
} }
/// Step this physics system by `t` seconds
pub fn step(&mut self, t: f32, ct: &content::Content) { pub fn step(&mut self, t: f32, ct: &content::Content) {
// Run ship updates // Run ship updates
let mut res = Vec::new(); let mut res = Vec::new();
@ -183,19 +192,23 @@ impl Physics {
} }
} }
/// Get a rigid body from a handle
pub fn get_rigid_body(&self, r: RigidBodyHandle) -> &RigidBody { pub fn get_rigid_body(&self, r: RigidBodyHandle) -> &RigidBody {
&self.wrapper.rigid_body_set[r] &self.wrapper.rigid_body_set[r]
} }
/// Get a ship from a handle
pub fn get_ship_mut(&mut self, s: &ShipHandle) -> Option<&mut objects::Ship> { pub fn get_ship_mut(&mut self, s: &ShipHandle) -> Option<&mut objects::Ship> {
self.ships.get_mut(&s.1) self.ships.get_mut(&s.1)
} }
/// Get a ship and its rigidbody from a handle
pub fn get_ship_body(&self, s: &ShipHandle) -> Option<(&objects::Ship, &RigidBody)> { pub fn get_ship_body(&self, s: &ShipHandle) -> Option<(&objects::Ship, &RigidBody)> {
// TODO: handle dead handles // TODO: handle dead handles
Some((self.ships.get(&s.1)?, self.wrapper.rigid_body_set.get(s.0)?)) Some((self.ships.get(&s.1)?, self.wrapper.rigid_body_set.get(s.0)?))
} }
/// Iterate over all ships in this physics system
pub fn iter_ship_body(&self) -> impl Iterator<Item = (&objects::Ship, &RigidBody)> + '_ { pub fn iter_ship_body(&self) -> impl Iterator<Item = (&objects::Ship, &RigidBody)> + '_ {
self.ships.values().map(|x| { self.ships.values().map(|x| {
( (
@ -205,12 +218,14 @@ impl Physics {
}) })
} }
/// Iterate over all ship sprites in this physics system
pub fn get_ship_sprites(&self) -> impl Iterator<Item = ObjectSprite> + '_ { pub fn get_ship_sprites(&self) -> impl Iterator<Item = ObjectSprite> + '_ {
self.ships self.ships
.values() .values()
.map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.physics_handle.0])) .map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.physics_handle.0]))
} }
/// Iterate over all projectile sprites in this physics system
pub fn get_projectile_sprites(&self) -> impl Iterator<Item = ObjectSprite> + '_ { pub fn get_projectile_sprites(&self) -> impl Iterator<Item = ObjectSprite> + '_ {
self.projectiles self.projectiles
.values() .values()

View File

@ -1,7 +1,12 @@
//! Conversion utilities
use cgmath::{Point2, Vector2}; use cgmath::{Point2, Vector2};
use nalgebra; use nalgebra;
use rapier2d::dynamics::RigidBody; use rapier2d::dynamics::RigidBody;
// TODO: Migrate to nalgebra fully, remove these converters
/// Convert a rigidbody position to a position in game coordinates
pub fn rigidbody_position(r: &RigidBody) -> cgmath::Point2<f32> { pub fn rigidbody_position(r: &RigidBody) -> cgmath::Point2<f32> {
Point2 { Point2 {
x: r.translation()[0], x: r.translation()[0],
@ -9,6 +14,7 @@ pub fn rigidbody_position(r: &RigidBody) -> cgmath::Point2<f32> {
} }
} }
/// Convert a rigidbody rotation to a rotation in game coordinates
pub fn rigidbody_rotation(r: &RigidBody) -> Vector2<f32> { pub fn rigidbody_rotation(r: &RigidBody) -> Vector2<f32> {
Vector2 { Vector2 {
x: r.rotation().re, x: r.rotation().re,
@ -16,6 +22,7 @@ pub fn rigidbody_rotation(r: &RigidBody) -> Vector2<f32> {
} }
} }
/// Convert a rigidbody velocity to a velocity in game coordinates
pub fn rigidbody_velocity(r: &RigidBody) -> cgmath::Vector2<f32> { pub fn rigidbody_velocity(r: &RigidBody) -> cgmath::Vector2<f32> {
let v = r.velocity_at_point(&nalgebra::Point2::new( let v = r.velocity_at_point(&nalgebra::Point2::new(
r.translation()[0], r.translation()[0],

View File

@ -2,11 +2,14 @@ use crate::ShipBehavior;
use galactica_content as content; use galactica_content as content;
use galactica_physics::{Physics, ShipHandle}; use galactica_physics::{Physics, ShipHandle};
/// Dummy ship behavior.
/// Does nothing.
pub struct Dummy { pub struct Dummy {
_handle: ShipHandle, _handle: ShipHandle,
} }
impl Dummy { impl Dummy {
/// Create a new ship controller
pub fn new(handle: ShipHandle) -> Box<Self> { pub fn new(handle: ShipHandle) -> Box<Self> {
Box::new(Self { _handle: handle }) Box::new(Self { _handle: handle })
} }

View File

@ -1,3 +1,5 @@
//! Various implementations of [`crate::ShipBehavior`]
mod dummy; mod dummy;
mod player; mod player;
mod point; mod point;

View File

@ -2,6 +2,8 @@ use crate::ShipBehavior;
use galactica_content as content; use galactica_content as content;
use galactica_physics::{Physics, ShipHandle}; use galactica_physics::{Physics, ShipHandle};
/// Player ship behavior.
/// Controls a ship using controller input
pub struct Player { pub struct Player {
handle: ShipHandle, handle: ShipHandle,
key_left: bool, key_left: bool,
@ -11,6 +13,7 @@ pub struct Player {
} }
impl Player { impl Player {
/// Make a new ship controller
pub fn new(handle: ShipHandle) -> Box<Self> { pub fn new(handle: ShipHandle) -> Box<Self> {
Box::new(Self { Box::new(Self {
handle, handle,

View File

@ -4,11 +4,14 @@ use crate::ShipBehavior;
use galactica_content as content; use galactica_content as content;
use galactica_physics::{util, Physics, ShipHandle}; use galactica_physics::{util, Physics, ShipHandle};
/// "Point" ship behavior.
/// Point and shoot towards the nearest enemy.
pub struct Point { pub struct Point {
handle: ShipHandle, handle: ShipHandle,
} }
impl Point { impl Point {
/// Create a new ship controller
pub fn new(handle: ShipHandle) -> Box<Self> { pub fn new(handle: ShipHandle) -> Box<Self> {
Box::new(Self { handle }) Box::new(Self { handle })
} }

View File

@ -1,12 +1,21 @@
#![warn(missing_docs)]
//! Computer-controlled ship behaviors
pub mod behavior; pub mod behavior;
use galactica_content as content; use galactica_content as content;
use galactica_physics::{Physics, ShipHandle}; use galactica_physics::{Physics, ShipHandle};
/// Main behavior trait. Any struct that implements this
/// may be used to control a ship.
pub trait ShipBehavior pub trait ShipBehavior
where where
Self: Send, Self: Send,
{ {
/// Update a ship's controls based on world state
fn update_controls(&mut self, physics: &mut Physics, content: &content::Content); fn update_controls(&mut self, physics: &mut Physics, content: &content::Content);
/// Get the ship this behavior is attached to
fn get_handle(&self) -> ShipHandle; fn get_handle(&self) -> ShipHandle;
} }