Compare commits
2 Commits
99f198c9d6
...
2c4db4ebc5
Author | SHA1 | Date |
---|---|---|
Mark | 2c4db4ebc5 | |
Mark | cbe8054f45 |
|
@ -0,0 +1,7 @@
|
|||
[faction."player"]
|
||||
display_name = "Player"
|
||||
relationship.enemy = "hostile"
|
||||
|
||||
[faction."enemy"]
|
||||
display_name = "Enemy"
|
||||
relationship.player = "hostile"
|
|
@ -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],
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
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, PartialEq, Eq)]
|
||||
pub struct FactionHandle {
|
||||
/// The index of this faction in content.factions
|
||||
/// TODO: pub in crate, currently for debug.
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl Hash for FactionHandle {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.index.hash(state)
|
||||
}
|
||||
}
|
|
@ -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, Relationship, 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.
|
||||
/// Represents static game content
|
||||
#[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);
|
||||
|
|
|
@ -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<()> {
|
|
@ -0,0 +1,106 @@
|
|||
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>,
|
||||
}
|
||||
}
|
||||
|
||||
/// How two factions should interact with each other.
|
||||
/// Relationships are directional: the relationship of
|
||||
/// `a` to `b` may not equal the relationship of `b` to `a`.
|
||||
///
|
||||
/// Relationships dictate how a ship of THIS faction
|
||||
/// will interact with a ship of the OTHER faction.
|
||||
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||
pub enum Relationship {
|
||||
/// Attack this faction
|
||||
#[serde(rename = "hostile")]
|
||||
Hostile,
|
||||
|
||||
/// Ignore this faction
|
||||
#[serde(rename = "neutral")]
|
||||
Neutral,
|
||||
|
||||
/// Protect this faction
|
||||
#[serde(rename = "friend")]
|
||||
Friend,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
|
||||
/// Relationships between this faction and other factions
|
||||
/// This is guaranteed to contain an entry for ALL factions.
|
||||
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();
|
||||
|
||||
// Indexing will break if this is false.
|
||||
assert!(ct.factions.len() == 0);
|
||||
|
||||
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 {},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ct.factions.push(Self {
|
||||
name: faction_name.to_owned(),
|
||||
handle: h,
|
||||
relationships,
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
|
@ -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<()> {
|
|
@ -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, Relationship};
|
||||
pub use gun::{Gun, Projectile};
|
||||
pub use ship::{EnginePoint, GunPoint, Ship};
|
||||
pub use system::{Object, System};
|
||||
pub use texture::Texture;
|
|
@ -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
|
|
@ -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<()> {
|
|
@ -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<()> {
|
|
@ -1,4 +1,5 @@
|
|||
use cgmath::Point2;
|
||||
use galactica_content::FactionHandle;
|
||||
use std::time::Instant;
|
||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||
|
||||
|
@ -23,6 +24,7 @@ pub struct Game {
|
|||
|
||||
physics: Physics,
|
||||
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
|
||||
content: Content,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
|
@ -32,30 +34,37 @@ 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::Dummy::new(h2));
|
||||
shipbehaviors.push(shipbehavior::Point::new(h3));
|
||||
|
||||
Game {
|
||||
last_update: Instant::now(),
|
||||
|
@ -72,6 +81,7 @@ impl Game {
|
|||
time_scale: 1.0,
|
||||
physics,
|
||||
shipbehaviors,
|
||||
content: ct,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,11 +109,16 @@ 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 {
|
||||
b.update_controls(&mut self.physics, &self.input, self.player);
|
||||
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.content);
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
self.physics.step(t);
|
||||
self.physics.step(t, &self.content);
|
||||
|
||||
if self.input.v_scroll != 0.0 {
|
||||
self.camera.zoom =
|
||||
|
@ -112,7 +127,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;
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ fn main() -> Result<()> {
|
|||
consts::STARFIELD_TEXTURE_NAME.to_owned(),
|
||||
)?;
|
||||
|
||||
println!("{:?}", content.factions);
|
||||
|
||||
pollster::block_on(run(content))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use cgmath::Point2;
|
||||
use content::{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,11 +107,12 @@ 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;
|
||||
}
|
||||
|
||||
pub fn step(&mut self, t: f32) {
|
||||
pub fn step(&mut self, t: f32, ct: &Content) {
|
||||
// Run ship updates
|
||||
let mut res = Vec::new();
|
||||
let mut to_remove = Vec::new();
|
||||
|
@ -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,11 +152,19 @@ impl Physics {
|
|||
|
||||
if let Some(p) = self.projectiles.get(a) {
|
||||
if let Some(s) = self.ships.get_mut(b) {
|
||||
s.hull -= p.damage;
|
||||
}
|
||||
let p_faction = ct.get_faction(p.faction);
|
||||
let r = p_faction.relationships[&s.faction];
|
||||
match r {
|
||||
content::Relationship::Hostile => {
|
||||
// TODO: implement death and spawning, and enable damage
|
||||
//s.hull -= p.damage;
|
||||
self.remove_projectile(*a);
|
||||
self.remove_projectile(*b);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,16 +185,22 @@ 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
|
||||
Some((self.ships.get(&s.1)?, self.wrapper.rigid_body_set.get(s.0)?))
|
||||
}
|
||||
|
||||
pub fn iter_ship_body(&self) -> impl Iterator<Item = (&objects::Ship, &RigidBody)> + '_ {
|
||||
self.ships.values().map(|x| {
|
||||
(
|
||||
self.ships.get(&s.1).unwrap(),
|
||||
self.wrapper.rigid_body_set.get(s.0).unwrap(),
|
||||
x,
|
||||
self.wrapper.rigid_body_set.get(x.physics_handle.0).unwrap(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_ship_sprites(&self) -> impl Iterator<Item = Sprite> + '_ {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use cgmath::{Deg, InnerSpace};
|
||||
use galactica_content as content;
|
||||
|
||||
use crate::{
|
||||
inputstatus::InputStatus,
|
||||
|
@ -9,7 +10,13 @@ pub trait ShipBehavior
|
|||
where
|
||||
Self: Send,
|
||||
{
|
||||
fn update_controls(&mut self, physics: &mut Physics, input: &InputStatus, player: ShipHandle);
|
||||
fn update_controls(
|
||||
&mut self,
|
||||
physics: &mut Physics,
|
||||
input: &InputStatus,
|
||||
content: &content::Content,
|
||||
);
|
||||
fn get_handle(&self) -> ShipHandle;
|
||||
}
|
||||
|
||||
pub struct Dummy {
|
||||
|
@ -27,9 +34,12 @@ impl ShipBehavior for Dummy {
|
|||
&mut self,
|
||||
_physics: &mut Physics,
|
||||
_input: &InputStatus,
|
||||
_player: ShipHandle,
|
||||
_content: &content::Content,
|
||||
) {
|
||||
}
|
||||
fn get_handle(&self) -> ShipHandle {
|
||||
return self._handle;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Player {
|
||||
|
@ -43,13 +53,22 @@ 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);
|
||||
fn update_controls(
|
||||
&mut self,
|
||||
physics: &mut Physics,
|
||||
input: &InputStatus,
|
||||
_content: &content::Content,
|
||||
) {
|
||||
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 {
|
||||
|
@ -63,34 +82,68 @@ 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);
|
||||
fn update_controls(
|
||||
&mut self,
|
||||
physics: &mut Physics,
|
||||
_input: &InputStatus,
|
||||
content: &content::Content,
|
||||
) {
|
||||
// Turn off all controls
|
||||
let s = physics.get_ship_mut(&self.handle).unwrap();
|
||||
s.controls.left = false;
|
||||
s.controls.right = false;
|
||||
s.controls.guns = false;
|
||||
s.controls.thrust = false;
|
||||
|
||||
let (my_s, my_r) = physics.get_ship_body(&self.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.faction);
|
||||
|
||||
// Iterate all possible targets
|
||||
let mut it = physics
|
||||
.iter_ship_body()
|
||||
.filter(|(s, _)| match my_faction.relationships[&s.faction] {
|
||||
content::Relationship::Hostile => true,
|
||||
_ => false,
|
||||
})
|
||||
.map(|(_, r)| r);
|
||||
|
||||
// Find the closest target
|
||||
let mut closest_enemy_position = match it.next() {
|
||||
Some(c) => util::rigidbody_position(c),
|
||||
None => return, // Do nothing if no targets are available
|
||||
};
|
||||
let mut d = (my_position - closest_enemy_position).magnitude();
|
||||
for r in it {
|
||||
let p = util::rigidbody_position(r);
|
||||
let new_d = (my_position - p).magnitude();
|
||||
if new_d < d {
|
||||
d = new_d;
|
||||
closest_enemy_position = p;
|
||||
}
|
||||
}
|
||||
|
||||
let (_, r) = physics.get_ship_body(&self.handle);
|
||||
let t = util::rigidbody_position(r);
|
||||
let pa = util::rigidbody_rotation(r);
|
||||
let v = r.angvel();
|
||||
|
||||
let d: Deg<f32> = (t - p).angle(pa).into();
|
||||
println!("{:?}", d);
|
||||
|
||||
let s = physics.get_ship_mut(&self.handle);
|
||||
let angle_delta: Deg<f32> = (my_rotation)
|
||||
.angle(closest_enemy_position - my_position)
|
||||
.into();
|
||||
|
||||
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 angle_delta < Deg(0.0) && my_angvel > -0.3 {
|
||||
s.controls.right = true;
|
||||
println!("r")
|
||||
} else if d > Deg(0.0) && v > -0.1 {
|
||||
println!("l");
|
||||
} else if angle_delta > Deg(0.0) && my_angvel < 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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue