Fixed physics angles

Added factions
master
Mark 2023-12-30 16:57:03 -08:00
parent 99f198c9d6
commit cbe8054f45
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
18 changed files with 339 additions and 154 deletions

7
content/factions.toml Normal file
View File

@ -0,0 +1,7 @@
[faction."player"]
display_name = "Player"
relationship.enemy = "hostile"
[faction."enemy"]
display_name = "Enemy"
relationship.player = "hostile"

View File

@ -1,13 +1,12 @@
[ship."Gypsum"]
sprite_texture = "ship::gypsum"
size = 100
mass = 10
mass = 1
hull = 200
engines = [{ x = 0.0, y = -1.05, size = 50.0 }]
guns = [{ x = 0.0, y = 1 }, { x = 0.1, y = 0.80 }, { x = -0.1, y = 0.80 }]
collision.points = [
#[rustfmt:skip],
[0.53921, 1.0000],

View File

@ -0,0 +1,32 @@
use std::{cmp::Eq, hash::Hash};
/// Represents a specific texture defined in the content dir.
#[derive(Debug, Clone, Copy)]
pub struct TextureHandle {
/// The index of this texture in content.textures
pub(crate) index: usize,
/// The aspect ratio of this texture (width / height)
pub aspect: f32,
}
impl Hash for TextureHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.index.hash(state)
}
}
impl Eq for TextureHandle {}
impl PartialEq for TextureHandle {
fn eq(&self, other: &Self) -> bool {
self.index.eq(&other.index)
}
}
/// A lightweight representation of a faction
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct FactionHandle {
/// The index of this faction in content.factions
/// TODO: pub in crate, currently for debug.
pub index: usize,
}

View File

@ -3,35 +3,29 @@
//! This subcrate is responsible for loading, parsing, validating game content,
//! which is usually stored in `./content`.
mod engine;
mod gun;
mod ship;
mod system;
mod texture;
mod handle;
mod part;
mod util;
pub use engine::Engine;
pub use gun::{Gun, Projectile};
pub use ship::{EnginePoint, GunPoint, Ship};
pub use system::{Object, System};
pub use texture::Texture;
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result};
use std::{
cmp::Eq,
collections::HashMap,
fs::File,
hash::Hash,
io::Read,
path::{Path, PathBuf},
};
use toml;
use walkdir::WalkDir;
pub use handle::{FactionHandle, TextureHandle};
pub use part::{Engine, EnginePoint, Faction, Gun, GunPoint, Ship, System, Texture};
mod syntax {
use super::{bail, HashMap, Result};
use super::{engine, gun, ship, system, texture};
use anyhow::{bail, Result};
use serde::Deserialize;
use std::collections::HashMap;
use crate::part::{engine, faction, gun, ship, system, texture};
#[derive(Debug, Deserialize)]
pub struct Root {
@ -40,6 +34,7 @@ mod syntax {
pub system: Option<HashMap<String, system::syntax::System>>,
pub engine: Option<HashMap<String, engine::syntax::Engine>>,
pub texture: Option<HashMap<String, texture::syntax::Texture>>,
pub faction: Option<HashMap<String, faction::syntax::Faction>>,
}
impl Root {
@ -50,6 +45,7 @@ mod syntax {
system: None,
engine: None,
texture: None,
faction: None,
}
}
@ -131,6 +127,21 @@ mod syntax {
}
}
if let Some(a) = other.faction {
if self.faction.is_none() {
self.faction = Some(a);
} else {
let sg = self.faction.as_mut().unwrap();
for (k, v) in a {
if sg.contains_key(&k) {
bail!("Repeated faction name {k}");
} else {
sg.insert(k, v);
}
}
}
}
return Ok(());
}
}
@ -145,53 +156,33 @@ trait Build {
Self: Sized;
}
/// Represents a specific texture defined in the content dir.
#[derive(Debug, Clone, Copy)]
pub struct TextureHandle {
/// The index of this texture in content.textures
pub index: usize,
/// The aspect ratio of this texture (width / height)
pub aspect: f32,
}
impl Hash for TextureHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.index.hash(state)
}
}
impl Eq for TextureHandle {}
impl PartialEq for TextureHandle {
fn eq(&self, other: &Self) -> bool {
self.index.eq(&other.index)
}
}
/// Represents generic game content, not connected to any game objects.
#[derive(Debug)]
pub struct Content {
/// Star systems
pub systems: Vec<system::System>,
pub systems: Vec<part::system::System>,
/// Ship bodies
pub ships: Vec<ship::Ship>,
pub ships: Vec<part::ship::Ship>,
/// Gun outfits
pub guns: Vec<gun::Gun>,
pub guns: Vec<part::gun::Gun>,
/// Engine outfits
pub engines: Vec<engine::Engine>,
pub engines: Vec<part::engine::Engine>,
/// Textures
pub textures: Vec<texture::Texture>,
pub textures: Vec<part::texture::Texture>,
/// Factions
pub factions: Vec<part::faction::Faction>,
/// Map strings to texture handles
/// This is never used outside this crate.
texture_index: HashMap<String, TextureHandle>,
texture_index: HashMap<String, handle::TextureHandle>,
/// The texture to use for starfield stars
starfield_handle: Option<TextureHandle>,
starfield_handle: Option<handle::TextureHandle>,
/// Root directory for textures
texture_root: PathBuf,
@ -217,12 +208,17 @@ impl Content {
}
/// Get a texture from a handle
pub fn get_texture(&self, h: TextureHandle) -> &texture::Texture {
pub fn get_texture(&self, h: TextureHandle) -> &Texture {
// In theory, this could fail if h has a bad index, but that shouldn't ever happen.
// The only TextureHandles that exist should be created by this crate.
return &self.textures[h.index];
}
/// Get a faction from a handle
pub fn get_faction(&self, h: FactionHandle) -> &Faction {
return &self.factions[h.index];
}
/// Load content from a directory.
pub fn load_dir(
path: PathBuf,
@ -262,6 +258,7 @@ impl Content {
guns: Vec::new(),
engines: Vec::new(),
textures: Vec::new(),
factions: Vec::new(),
texture_index: HashMap::new(),
starfield_handle: None,
texture_root,
@ -270,19 +267,22 @@ impl Content {
// Order here matters, usually
if root.texture.is_some() {
texture::Texture::build(root.texture.take().unwrap(), &mut content)?;
part::texture::Texture::build(root.texture.take().unwrap(), &mut content)?;
}
if root.ship.is_some() {
ship::Ship::build(root.ship.take().unwrap(), &mut content)?;
part::ship::Ship::build(root.ship.take().unwrap(), &mut content)?;
}
if root.gun.is_some() {
gun::Gun::build(root.gun.take().unwrap(), &mut content)?;
part::gun::Gun::build(root.gun.take().unwrap(), &mut content)?;
}
if root.engine.is_some() {
engine::Engine::build(root.engine.take().unwrap(), &mut content)?;
part::engine::Engine::build(root.engine.take().unwrap(), &mut content)?;
}
if root.system.is_some() {
system::System::build(root.system.take().unwrap(), &mut content)?;
part::system::System::build(root.system.take().unwrap(), &mut content)?;
}
if root.faction.is_some() {
part::faction::Faction::build(root.faction.take().unwrap(), &mut content)?;
}
return Ok(content);

View File

@ -2,9 +2,9 @@ use std::collections::HashMap;
use anyhow::{bail, Result};
use crate::{Content, TextureHandle};
use crate::{handle::TextureHandle, Content};
pub(super) mod syntax {
pub(crate) mod syntax {
use serde::Deserialize;
// Raw serde syntax structs.
// These are never seen by code outside this crate.
@ -36,7 +36,7 @@ pub struct Engine {
pub flare_sprite_texture: TextureHandle,
}
impl super::Build for Engine {
impl crate::Build for Engine {
type InputSyntax = HashMap<String, syntax::Engine>;
fn build(engine: Self::InputSyntax, ct: &mut Content) -> Result<()> {

View File

@ -0,0 +1,98 @@
use anyhow::Result;
use serde::Deserialize;
use std::collections::HashMap;
use crate::{handle::FactionHandle, Content};
pub(crate) mod syntax {
use std::collections::HashMap;
use serde::Deserialize;
// Raw serde syntax structs.
// These are never seen by code outside this crate.
#[derive(Debug, Deserialize)]
pub struct Faction {
pub display_name: String,
pub relationship: HashMap<String, super::Relationship>,
}
}
#[derive(Debug, Deserialize, Clone, Copy)]
pub enum Relationship {
#[serde(rename = "hostile")]
Hostile,
#[serde(rename = "neutral")]
Neutral,
#[serde(rename = "enemy")]
Enemy,
}
/// Represents a game faction
#[derive(Debug, Clone)]
pub struct Faction {
/// The name of this faction
pub name: String,
/// This faction's handle
pub handle: FactionHandle,
pub relationships: HashMap<FactionHandle, Relationship>,
}
impl crate::Build for Faction {
type InputSyntax = HashMap<String, syntax::Faction>;
fn build(factions: Self::InputSyntax, ct: &mut Content) -> Result<()> {
// Keeps track of position in faction array.
// This lets us build FactionHandles before finishing all factions.
let faction_names: Vec<String> = factions.keys().map(|x| x.to_owned()).collect();
for f_idx in 0..faction_names.len() {
let faction_name = &faction_names[f_idx];
let faction = &factions[faction_name];
// Handle for this faction
let h = FactionHandle { index: f_idx };
// Compute relationships
let mut relationships = HashMap::new();
for i in 0..faction_names.len() {
let f_other = &faction_names[i];
let h_other = FactionHandle { index: i };
if let Some(r) = faction.relationship.get(f_other) {
relationships.insert(h_other, *r);
} else {
// Default relationship, if not specified
// Look at reverse direction...
let other = factions[f_other].relationship.get(faction_name);
relationships.insert(
h_other,
// ... and pick a relationship based on that.
match other {
Some(Relationship::Hostile) => Relationship::Hostile {},
_ => Relationship::Neutral {},
},
);
}
}
let relationships = faction
.relationship
.iter()
.map(|x| (h, x.1.clone()))
.collect();
ct.factions.push(Self {
name: faction_name.to_owned(),
handle: h,
relationships,
});
}
return Ok(());
}
}

View File

@ -3,9 +3,9 @@ use std::collections::HashMap;
use anyhow::{bail, Result};
use cgmath::Deg;
use crate::{Content, TextureHandle};
use crate::{handle::TextureHandle, Content};
pub(super) mod syntax {
pub(crate) mod syntax {
use serde::Deserialize;
// Raw serde syntax structs.
// These are never seen by code outside this crate.
@ -82,7 +82,7 @@ pub struct Projectile {
pub damage: f32,
}
impl super::Build for Gun {
impl crate::Build for Gun {
type InputSyntax = HashMap<String, syntax::Gun>;
fn build(gun: Self::InputSyntax, ct: &mut Content) -> Result<()> {

View File

@ -0,0 +1,15 @@
//! Content parts
pub mod engine;
pub mod faction;
pub mod gun;
pub mod ship;
pub mod system;
pub mod texture;
pub use engine::Engine;
pub use faction::Faction;
pub use gun::{Gun, Projectile};
pub use ship::{EnginePoint, GunPoint, Ship};
pub use system::{Object, System};
pub use texture::Texture;

View File

@ -4,9 +4,9 @@ use anyhow::{bail, Result};
use cgmath::Point2;
use nalgebra::{point, Point};
use crate::{Content, TextureHandle};
use crate::{handle::TextureHandle, Content};
pub(super) mod syntax {
pub(crate) mod syntax {
use serde::Deserialize;
// Raw serde syntax structs.
// These are never seen by code outside this crate.
@ -108,7 +108,7 @@ pub struct GunPoint {
pub pos: Point2<f32>,
}
impl super::Build for Ship {
impl crate::Build for Ship {
type InputSyntax = HashMap<String, syntax::Ship>;
fn build(ship: Self::InputSyntax, ct: &mut Content) -> Result<()> {
@ -154,7 +154,7 @@ impl super::Build for Ship {
})
.collect(),
collision: Collision {
indices: ship.collision.indices.clone(),
indices: ship.collision.indices,
points: ship
.collision
.points

View File

@ -2,9 +2,9 @@ use anyhow::{bail, Context, Result};
use cgmath::{Deg, Point3};
use std::collections::{HashMap, HashSet};
use crate::{util::Polar, Content, TextureHandle};
use crate::{handle::TextureHandle, util::Polar, Content};
pub(super) mod syntax {
pub(crate) mod syntax {
use super::HashMap;
use serde::Deserialize;
// Raw serde syntax structs.
@ -174,7 +174,7 @@ fn resolve_position(
}
}
impl super::Build for System {
impl crate::Build for System {
type InputSyntax = HashMap<String, syntax::System>;
fn build(system: Self::InputSyntax, ct: &mut Content) -> Result<()> {

View File

@ -3,9 +3,9 @@ use std::{collections::HashMap, path::PathBuf};
use anyhow::{bail, Context, Result};
use image::io::Reader;
use crate::{Content, TextureHandle};
use crate::{handle::TextureHandle, Content};
pub(super) mod syntax {
pub(crate) mod syntax {
use std::path::PathBuf;
use serde::Deserialize;
@ -31,7 +31,7 @@ pub struct Texture {
pub path: PathBuf,
}
impl super::Build for Texture {
impl crate::Build for Texture {
type InputSyntax = HashMap<String, syntax::Texture>;
fn build(texture: Self::InputSyntax, ct: &mut Content) -> Result<()> {

View File

@ -1,4 +1,5 @@
use cgmath::Point2;
use galactica_content::FactionHandle;
use std::time::Instant;
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
@ -32,29 +33,36 @@ impl Game {
let h1 = physics.add_ship(
&ct.ships[0],
vec![
outfits::ShipOutfit::Gun(outfits::ShipGun::new(ct.guns[0].clone(), 1)),
outfits::ShipOutfit::Gun(outfits::ShipGun::new(ct.guns[0].clone(), 2)),
outfits::ShipOutfit::Gun(outfits::ShipGun::new(&ct.guns[0], 1)),
outfits::ShipOutfit::Gun(outfits::ShipGun::new(&ct.guns[0], 2)),
outfits::ShipOutfit::Engine(ct.engines[0].clone()),
],
Point2 { x: 0.0, y: 0.0 },
FactionHandle { index: 0 },
);
let h2 = physics.add_ship(&ct.ships[0], vec![], Point2 { x: 300.0, y: 300.0 });
let _h3 = physics.add_ship(
let h2 = physics.add_ship(
&ct.ships[0],
vec![],
Point2 { x: 300.0, y: 300.0 },
FactionHandle { index: 0 },
);
let h3 = physics.add_ship(
&ct.ships[0],
vec![outfits::ShipOutfit::Gun(outfits::ShipGun::new(
ct.guns[0].clone(),
&ct.guns[0],
0,
))],
Point2 {
x: -300.0,
y: 300.0,
},
FactionHandle { index: 1 },
);
let mut shipbehaviors: Vec<Box<dyn ShipBehavior>> = Vec::new();
shipbehaviors.push(shipbehavior::Player::new(h1));
//shipbehaviors.push(shipbehavior::Point::new(h3));
shipbehaviors.push(shipbehavior::Point::new(h3));
shipbehaviors.push(shipbehavior::Dummy::new(h2));
Game {
@ -99,9 +107,14 @@ impl Game {
pub fn update(&mut self) {
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
for b in &mut self.shipbehaviors {
self.shipbehaviors.retain_mut(|b| {
if self.physics.get_ship_mut(&b.get_handle()).is_none() {
false
} else {
b.update_controls(&mut self.physics, &self.input, self.player);
true
}
});
self.physics.step(t);
@ -112,7 +125,11 @@ impl Game {
}
// TODO: Camera physics
let r = self.physics.get_ship_mut(&self.player).physics_handle;
let r = self
.physics
.get_ship_mut(&self.player)
.unwrap()
.physics_handle;
let r = self.physics.get_rigid_body(r.0); // TODO: r.0 shouldn't be public
let ship_pos = util::rigidbody_position(r);
self.camera.pos = ship_pos;

View File

@ -1,9 +1,6 @@
use cgmath::{Deg, Point3};
use crate::{
content::{self, EnginePoint, GunPoint},
render::SubSprite,
};
use crate::{content, render::SubSprite};
/// Represents a gun attached to a specific ship at a certain gunpoint.
pub struct ShipGun {
@ -13,9 +10,9 @@ pub struct ShipGun {
}
impl ShipGun {
pub fn new(kind: content::Gun, point: usize) -> Self {
pub fn new(kind: &content::Gun, point: usize) -> Self {
Self {
kind,
kind: kind.clone(),
point,
cooldown: 0.0,
}
@ -77,7 +74,7 @@ impl<'a> ShipOutfits {
.map(|x| x.unwrap())
}
pub fn iter_guns_points(&mut self) -> impl Iterator<Item = (&mut ShipGun, &GunPoint)> {
pub fn iter_guns_points(&mut self) -> impl Iterator<Item = (&mut ShipGun, &content::GunPoint)> {
self.outfits
.iter_mut()
.map(|x| x.gun())
@ -95,7 +92,7 @@ impl<'a> ShipOutfits {
.map(|x| x.unwrap())
}
pub fn iter_enginepoints(&self) -> impl Iterator<Item = &EnginePoint> {
pub fn iter_enginepoints(&self) -> impl Iterator<Item = &content::EnginePoint> {
self.enginepoints.iter()
}

View File

@ -1,5 +1,5 @@
use cgmath::Point3;
use galactica_content::TextureHandle;
use cgmath::{Deg, InnerSpace, Point3, Vector2};
use galactica_content::{FactionHandle, TextureHandle};
use rapier2d::{
dynamics::{RigidBody, RigidBodyBuilder, RigidBodyHandle},
geometry::{ColliderBuilder, ColliderHandle},
@ -14,6 +14,7 @@ pub struct ProjectileBuilder {
pub lifetime: f32,
pub size: f32,
pub damage: f32,
pub faction: FactionHandle,
}
impl ProjectileBuilder {
@ -25,6 +26,7 @@ impl ProjectileBuilder {
lifetime: self.lifetime,
size: self.size,
damage: self.damage,
faction: self.faction,
}
}
}
@ -37,6 +39,7 @@ pub struct Projectile {
pub lifetime: f32,
pub size: f32,
pub damage: f32,
pub faction: FactionHandle,
}
impl Projectile {
@ -50,7 +53,11 @@ impl Projectile {
pub fn get_sprite(&self, r: &RigidBody) -> Sprite {
let pos = util::rigidbody_position(r);
let ang = util::rigidbody_angle(r);
let rot = util::rigidbody_rotation(r);
// Sprites point north at 0 degrees
let ang: Deg<f32> = rot.angle(Vector2 { x: 1.0, y: 0.0 }).into();
Sprite {
texture: self.sprite_texture,
pos: Point3 {
@ -59,7 +66,7 @@ impl Projectile {
z: 1.0,
},
size: self.size,
angle: ang,
angle: -ang,
children: None,
}
}

View File

@ -1,5 +1,5 @@
use cgmath::{Deg, EuclideanSpace, Matrix2, Rad, Vector2};
use content::TextureHandle;
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Rad, Vector2};
use content::{FactionHandle, TextureHandle};
use nalgebra::vector;
use rand::Rng;
use rapier2d::{
@ -40,12 +40,13 @@ impl ShipControls {
pub struct Ship {
pub physics_handle: ShipHandle,
outfits: outfits::ShipOutfits,
sprite_texture: TextureHandle,
size: f32,
pub faction: FactionHandle,
pub hull: f32,
pub controls: ShipControls,
outfits: outfits::ShipOutfits,
sprite_texture: TextureHandle,
size: f32,
}
impl Ship {
@ -53,6 +54,7 @@ impl Ship {
c: &content::Ship,
outfits: Vec<outfits::ShipOutfit>,
physics_handle: ShipHandle,
faction: FactionHandle,
) -> Self {
let mut o = outfits::ShipOutfits::new(c);
for x in outfits.into_iter() {
@ -66,6 +68,7 @@ impl Ship {
size: c.size,
hull: c.hull,
controls: ShipControls::new(),
faction,
}
}
@ -81,24 +84,28 @@ impl Ship {
g.cooldown = g.kind.rate + rng.gen_range(-g.kind.rate_rng..=g.kind.rate_rng);
let ship_pos = util::rigidbody_position(r);
let ship_ang: Deg<f32> = util::rigidbody_angle(r);
let ship_ang_rad: Rad<f32> = ship_ang.into();
let ship_rot = util::rigidbody_rotation(r);
let ship_vel = util::rigidbody_velocity(r);
let ship_ang = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 });
let pos = ship_pos + (Matrix2::from_angle(ship_ang) * p.pos.to_vec());
let pos = ship_pos + (Matrix2::from_angle(-ship_ang) * p.pos.to_vec());
let spread: Rad<f32> =
Deg(rng.gen_range(-(g.kind.spread.0 / 2.0)..=g.kind.spread.0 / 2.0)).into();
let vel = ship_vel
+ (Matrix2::from_angle(
ship_ang + Deg(rng.gen_range(-(g.kind.spread.0 / 2.0)..=g.kind.spread.0 / 2.0)),
) * Vector2 {
+ (Matrix2::from_angle(-ship_ang + spread)
* Vector2 {
x: 0.0,
y: g.kind.projectile.speed
+ rng.gen_range(-g.kind.projectile.speed_rng..=g.kind.projectile.speed_rng),
+ rng.gen_range(
-g.kind.projectile.speed_rng..=g.kind.projectile.speed_rng,
),
});
let p_r = RigidBodyBuilder::kinematic_velocity_based()
.translation(vector![pos.x, pos.y])
.rotation(-ship_ang_rad.0)
.rotation(-ship_ang.0)
.linvel(vector![vel.x, vel.y]);
let p_c = ColliderBuilder::ball(5.0)
@ -116,6 +123,7 @@ impl Ship {
size: g.kind.projectile.size
+ rng.gen_range(-g.kind.projectile.size_rng..=g.kind.projectile.size_rng),
damage: g.kind.projectile.damage, // TODO: kind as param to builder
faction: self.faction,
})
}
return out;
@ -123,8 +131,8 @@ impl Ship {
/// Apply the effects of all active controls
pub fn apply_controls(&mut self, r: &mut RigidBody, t: f32) -> ShipTickResult {
let ship_ang = util::rigidbody_angle(r);
let engine_force = Matrix2::from_angle(ship_ang) * Vector2 { x: 0.0, y: 1.0 } * t;
let ship_rot = util::rigidbody_rotation(r);
let engine_force = ship_rot * t;
if self.controls.thrust {
for e in self.outfits.iter_engines() {
@ -155,12 +163,15 @@ impl Ship {
pub fn get_sprite(&self, r: &RigidBody) -> Sprite {
let ship_pos = util::rigidbody_position(r);
let ship_ang = util::rigidbody_angle(r);
let ship_rot = util::rigidbody_rotation(r);
// Sprites point north at 0 degrees
let ship_ang: Deg<f32> = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }).into();
Sprite {
pos: (ship_pos.x, ship_pos.y, 1.0).into(),
texture: self.sprite_texture.clone(), // TODO: sprite texture should be easy to clone
angle: ship_ang,
texture: self.sprite_texture,
angle: -ship_ang,
size: self.size,
children: if self.controls.thrust {

View File

@ -1,4 +1,5 @@
use cgmath::Point2;
use content::FactionHandle;
use crossbeam::channel::Receiver;
use nalgebra::vector;
use rapier2d::{
@ -6,7 +7,7 @@ use rapier2d::{
geometry::{ColliderBuilder, ColliderHandle, CollisionEvent},
pipeline::ChannelEventCollector,
};
use std::collections::HashMap;
use std::{collections::HashMap, f32::consts::PI};
use super::{wrapper::Wrapper, ShipHandle};
use crate::{content, game::outfits, objects, render::Sprite};
@ -83,11 +84,15 @@ impl Physics {
ct: &content::Ship,
outfits: Vec<outfits::ShipOutfit>,
position: Point2<f32>,
faction: FactionHandle,
) -> ShipHandle {
let cl = ColliderBuilder::convex_decomposition(
&ct.collision.points[..],
&ct.collision.indices[..],
)
// Rotate collider to match sprite
// (Collider starts pointing east, sprite starts pointing north.)
.rotation(PI / -2.0)
.mass(ct.mass);
let rb = RigidBodyBuilder::dynamic()
@ -102,7 +107,8 @@ impl Physics {
);
let h = ShipHandle(r, c);
self.ships.insert(c, objects::Ship::new(ct, outfits, h));
self.ships
.insert(c, objects::Ship::new(ct, outfits, h, faction));
return h;
}
@ -136,8 +142,8 @@ impl Physics {
let a = &event.collider1();
let b = &event.collider2();
// If a projectile is part of the collision, make sure
// a is a projectile.
// If projectiles are a part of this collision, make sure
// `a` is one of them.
let (a, b) = if self.projectiles.contains_key(b) {
(b, a)
} else {
@ -146,13 +152,16 @@ impl Physics {
if let Some(p) = self.projectiles.get(a) {
if let Some(s) = self.ships.get_mut(b) {
// TODO: better rules here
if s.faction != p.faction {
s.hull -= p.damage;
}
self.remove_projectile(*a);
self.remove_projectile(*b);
}
}
}
}
}
// Delete projectiles
let mut to_remove = Vec::new();
@ -171,16 +180,13 @@ impl Physics {
&self.wrapper.rigid_body_set[r]
}
pub fn get_ship_mut(&mut self, s: &ShipHandle) -> &mut objects::Ship {
self.ships.get_mut(&s.1).unwrap()
pub fn get_ship_mut(&mut self, s: &ShipHandle) -> Option<&mut objects::Ship> {
self.ships.get_mut(&s.1)
}
pub fn get_ship_body(&self, s: &ShipHandle) -> (&objects::Ship, &RigidBody) {
pub fn get_ship_body(&self, s: &ShipHandle) -> Option<(&objects::Ship, &RigidBody)> {
// TODO: handle dead handles
(
self.ships.get(&s.1).unwrap(),
self.wrapper.rigid_body_set.get(s.0).unwrap(),
)
Some((self.ships.get(&s.1)?, self.wrapper.rigid_body_set.get(s.0)?))
}
pub fn get_ship_sprites(&self) -> impl Iterator<Item = Sprite> + '_ {

View File

@ -1,4 +1,4 @@
use cgmath::{Deg, InnerSpace, Point2, Vector2};
use cgmath::{Point2, Vector2};
use nalgebra;
use rapier2d::dynamics::RigidBody;
@ -9,19 +9,10 @@ pub fn rigidbody_position(r: &RigidBody) -> cgmath::Point2<f32> {
}
}
pub fn rigidbody_angle(r: &RigidBody) -> Deg<f32> {
Vector2 {
x: r.rotation().im,
y: r.rotation().re,
}
.angle(Vector2 { x: 0.0, y: 1.0 })
.into()
}
pub fn rigidbody_rotation(r: &RigidBody) -> Vector2<f32> {
Vector2 {
x: r.rotation().im,
y: r.rotation().re,
x: r.rotation().re,
y: r.rotation().im,
}
}

View File

@ -10,6 +10,7 @@ where
Self: Send,
{
fn update_controls(&mut self, physics: &mut Physics, input: &InputStatus, player: ShipHandle);
fn get_handle(&self) -> ShipHandle;
}
pub struct Dummy {
@ -30,6 +31,9 @@ impl ShipBehavior for Dummy {
_player: ShipHandle,
) {
}
fn get_handle(&self) -> ShipHandle {
return self._handle;
}
}
pub struct Player {
@ -44,12 +48,16 @@ impl Player {
impl ShipBehavior for Player {
fn update_controls(&mut self, physics: &mut Physics, input: &InputStatus, _player: ShipHandle) {
let s = physics.get_ship_mut(&self.handle);
let s = physics.get_ship_mut(&self.handle).unwrap();
s.controls.left = input.key_left;
s.controls.right = input.key_right;
s.controls.guns = input.key_guns;
s.controls.thrust = input.key_thrust;
}
fn get_handle(&self) -> ShipHandle {
return self.handle;
}
}
pub struct Point {
@ -64,33 +72,30 @@ impl Point {
impl ShipBehavior for Point {
fn update_controls(&mut self, physics: &mut Physics, _input: &InputStatus, player: ShipHandle) {
let (_, r) = physics.get_ship_body(&player);
let (_, r) = physics.get_ship_body(&player).unwrap();
let p = util::rigidbody_position(r);
let (_, r) = physics.get_ship_body(&self.handle);
let (_, r) = physics.get_ship_body(&self.handle).unwrap();
let t = util::rigidbody_position(r);
let pa = util::rigidbody_rotation(r);
let v = r.angvel();
let d: Deg<f32> = (pa).angle(p - t).into();
let d: Deg<f32> = (t - p).angle(pa).into();
println!("{:?}", d);
let s = physics.get_ship_mut(&self.handle);
let s = physics.get_ship_mut(&self.handle).unwrap();
s.controls.left = false;
s.controls.right = false;
if d < Deg(0.0) && v < 0.1 {
s.controls.left = false;
if d < Deg(0.0) && v > -0.3 {
s.controls.right = true;
println!("r")
} else if d > Deg(0.0) && v > -0.1 {
println!("l");
} else if d > Deg(0.0) && v < 0.3 {
s.controls.left = true;
s.controls.right = false;
}
s.controls.guns = true;
s.controls.thrust = false;
}
fn get_handle(&self) -> ShipHandle {
return self.handle;
}
}