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;
/// Maximum zoom level
pub const ZOOM_MAX: f32 = 2000.0;
/// Z-axis range for starfield stars
/// This does not affect scale.
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;
/// Size range for starfield stars, in game units.
/// This is scaled for zoom, but NOT for distance.
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;
/// 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
pub const TEXTURE_ROOT: &'static str = "./assets/render";
// We can draw at most this many sprites on the screen.
// TODO: compile-time option or config file
/// We can draw at most this many sprites on the screen.
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;

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;
mod physics;
pub mod util;
@ -7,5 +13,6 @@ pub use physics::Physics;
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
/// A lightweight handle for a specific ship in a physics system
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
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.
#[derive(Debug)]
pub struct ShipGun {
/// The kind of gun this is
pub kind: content::Gun,
/// How many seconds we must wait before this gun can fire
pub cooldown: f32,
/// Index of the gunpoint this gun is attached to
pub point: usize,
}
impl ShipGun {
/// Make a new shipgun
pub fn new(kind: content::Gun, point: usize) -> Self {
Self {
kind: kind,
@ -63,11 +69,15 @@ impl OutfitStatSum {
/// 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.
/// TODO: rename (outfitset?)
/// TODO: ctrl-f outfitset in comments
#[derive(Debug)]
pub struct ShipOutfits {
/// The total sum of the stats this set of outfits provides
/// TODO: this shouldn't be pub
pub stats: OutfitStatSum,
pub total_space: content::OutfitSpace,
//pub total_space: content::OutfitSpace,
available_space: content::OutfitSpace,
outfits: Vec<content::Outfit>,
guns: Vec<ShipGun>,
@ -80,13 +90,14 @@ pub struct ShipOutfits {
}
impl<'a> ShipOutfits {
/// Make a new outfit array
pub fn new(content: &content::Ship) -> Self {
Self {
stats: OutfitStatSum::new(),
outfits: Vec::new(),
guns: Vec::new(),
available_space: content.space.clone(),
total_space: content.space.clone(),
//total_space: content.space.clone(),
enginepoints: content.engines.clone(),
gunpoints: content.guns.clone(),
engine_flare_sprites: vec![],
@ -153,10 +164,12 @@ impl<'a> ShipOutfits {
return true;
}
/// Iterate over all guns in this outfitset
pub fn iter_guns(&mut self) -> impl Iterator<Item = &mut ShipGun> {
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)> {
self.guns
.iter_mut()
@ -164,6 +177,7 @@ impl<'a> ShipOutfits {
.map(|(a, b)| (b, a))
}
/// Update engine flare sprites
pub fn update_engine_flares(&mut self) {
// TODO: better way to pick flare texture
self.engine_flare_sprites.clear();
@ -189,6 +203,7 @@ impl<'a> ShipOutfits {
.collect();
}
/// Get the sprites we should show if this ship is firing its engines
pub fn get_engine_flares(&self) -> Vec<ObjectSubSprite> {
return self.engine_flare_sprites.clone();
}

View File

@ -8,50 +8,81 @@ use crate::util;
use galactica_content as content;
use galactica_render::ObjectSprite;
/// A helper struct that contains parameters for a projectile
/// TODO: decouple data from physics
pub struct ProjectileBuilder {
/// TODO
pub rigid_body: RigidBodyBuilder,
/// TODO
pub collider: ColliderBuilder,
/// TODO
pub sprite_texture: content::TextureHandle,
/// TODO
pub lifetime: f32,
/// TODO
pub size: f32,
/// TODO
pub damage: f32,
/// TODO
pub faction: content::FactionHandle,
}
impl ProjectileBuilder {
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,
}
}
}
/// A single projectile in the world
#[derive(Debug)]
pub struct Projectile {
/// TODO
pub rigid_body: RigidBodyHandle,
/// TODO
pub collider: ColliderHandle,
/// TODO
pub sprite_texture: content::TextureHandle,
/// TODO
pub lifetime: f32,
/// TODOd
pub size: f32,
/// TODO
pub damage: f32,
/// TODO
pub faction: content::FactionHandle,
}
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) {
self.lifetime -= t;
}
/// Is this projectile expired?
pub fn is_expired(&self) -> bool {
return self.lifetime < 0.0;
}
/// Get this projectiles' sprite
pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite {
let pos = util::rigidbody_position(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 {
/// TODO
pub physics_handle: ShipHandle,
/// TODO
pub faction: FactionHandle,
/// TODO
pub hull: f32,
/// TODO
pub controls: ShipControls,
outfits: ShipOutfits,
@ -47,6 +56,7 @@ pub struct Ship {
}
impl Ship {
/// Make a new ship
pub fn new(
c: &content::Ship,
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> {
let mut rng = rand::thread_rng();
let mut out = Vec::new();
@ -154,6 +165,7 @@ impl Ship {
return ShipTickResult { projectiles: p };
}
/// Get this ship's sprite
pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite {
let ship_pos = util::rigidbody_position(r);
let ship_rot = util::rigidbody_rotation(r);

View File

@ -8,7 +8,12 @@ use rapier2d::{
};
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_render::ObjectSprite;
@ -59,13 +64,14 @@ impl Physics {
r,
&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;
}
}
// Public methods
impl Physics {
/// Create a new physics system
pub fn new() -> Self {
let (collision_send, collision_queue) = 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(
&mut self,
ct: &content::Ship,
@ -114,6 +122,7 @@ impl Physics {
return h;
}
/// Step this physics system by `t` seconds
pub fn step(&mut self, t: f32, ct: &content::Content) {
// Run ship updates
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 {
&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> {
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)> {
// TODO: handle dead handles
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)> + '_ {
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> + '_ {
self.ships
.values()
.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> + '_ {
self.projectiles
.values()

View File

@ -1,7 +1,12 @@
//! Conversion utilities
use cgmath::{Point2, Vector2};
use nalgebra;
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> {
Point2 {
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> {
Vector2 {
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> {
let v = r.velocity_at_point(&nalgebra::Point2::new(
r.translation()[0],

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +1,21 @@
#![warn(missing_docs)]
//! Computer-controlled ship behaviors
pub mod behavior;
use galactica_content as content;
use galactica_physics::{Physics, ShipHandle};
/// 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 Physics, content: &content::Content);
/// Get the ship this behavior is attached to
fn get_handle(&self) -> ShipHandle;
}