2023-12-30 10:58:17 -08:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2024-01-05 18:04:30 -08:00
|
|
|
use anyhow::{bail, Context, Result};
|
2023-12-27 19:51:58 -08:00
|
|
|
use cgmath::Point2;
|
2023-12-30 10:58:17 -08:00
|
|
|
use nalgebra::{point, Point};
|
|
|
|
|
2024-01-05 18:04:30 -08:00
|
|
|
use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitSpace};
|
2023-12-30 21:05:06 -08:00
|
|
|
|
2023-12-30 16:57:03 -08:00
|
|
|
pub(crate) mod syntax {
|
2024-01-05 18:04:30 -08:00
|
|
|
use crate::part::{effect::syntax::EffectReference, outfitspace};
|
2023-12-27 19:51:58 -08:00
|
|
|
use serde::Deserialize;
|
2023-12-30 21:05:06 -08:00
|
|
|
|
2023-12-27 19:51:58 -08:00
|
|
|
// Raw serde syntax structs.
|
|
|
|
// These are never seen by code outside this crate.
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct Ship {
|
2024-01-04 17:17:55 -08:00
|
|
|
pub sprite: String,
|
2023-12-27 19:51:58 -08:00
|
|
|
pub size: f32,
|
|
|
|
pub engines: Vec<Engine>,
|
|
|
|
pub guns: Vec<Gun>,
|
2023-12-29 12:21:56 -08:00
|
|
|
pub hull: f32,
|
2023-12-30 11:07:20 -08:00
|
|
|
pub mass: f32,
|
2024-01-05 13:25:44 -08:00
|
|
|
pub collision: Vec<[f32; 2]>,
|
2023-12-30 17:51:57 -08:00
|
|
|
pub angular_drag: f32,
|
|
|
|
pub linear_drag: f32,
|
2024-01-05 12:09:59 -08:00
|
|
|
pub space: outfitspace::syntax::OutfitSpace,
|
2024-01-05 18:04:30 -08:00
|
|
|
pub collapse: Option<Collapse>,
|
2023-12-30 10:58:17 -08:00
|
|
|
}
|
|
|
|
|
2023-12-27 19:51:58 -08:00
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct Engine {
|
|
|
|
pub x: f32,
|
|
|
|
pub y: f32,
|
|
|
|
pub size: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct Gun {
|
|
|
|
pub x: f32,
|
|
|
|
pub y: f32,
|
|
|
|
}
|
2024-01-05 18:04:30 -08:00
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// plural or not? document!
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct Collapse {
|
|
|
|
pub length: f32,
|
|
|
|
pub effects: Vec<CollapseEffectSpawner>,
|
|
|
|
pub event: Vec<CollapseEvent>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct CollapseEffectSpawner {
|
|
|
|
pub effect: EffectReference,
|
|
|
|
pub count: f32,
|
|
|
|
pub pos: Option<[f32; 2]>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[serde(untagged)]
|
|
|
|
pub enum CollapseEvent {
|
|
|
|
Effect(EffectCollapseEvent),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct EffectCollapseEvent {
|
|
|
|
pub time: f32,
|
|
|
|
pub effects: Vec<CollapseEffectSpawner>,
|
|
|
|
}
|
2023-12-27 19:51:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Processed data structs.
|
|
|
|
// These are exported.
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Represents a ship chassis.
|
2023-12-27 19:51:58 -08:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Ship {
|
2023-12-29 15:46:09 -08:00
|
|
|
/// This ship's name
|
2023-12-27 19:51:58 -08:00
|
|
|
pub name: String,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// This ship's sprite
|
2024-01-04 17:17:55 -08:00
|
|
|
pub sprite: SpriteHandle,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// The size of this ship.
|
|
|
|
/// Measured as unrotated height,
|
|
|
|
/// in terms of game units.
|
2023-12-27 19:51:58 -08:00
|
|
|
pub size: f32,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
2023-12-30 11:07:20 -08:00
|
|
|
/// The mass of this ship
|
|
|
|
pub mass: f32,
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Engine points on this ship.
|
|
|
|
/// This is where engine flares are drawn.
|
2023-12-28 17:04:41 -08:00
|
|
|
pub engines: Vec<EnginePoint>,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// Gun points on this ship.
|
|
|
|
/// A gun outfit can be mounted on each.
|
2023-12-28 17:04:41 -08:00
|
|
|
pub guns: Vec<GunPoint>,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// This ship's hull strength
|
2023-12-29 12:21:56 -08:00
|
|
|
pub hull: f32,
|
2023-12-30 10:58:17 -08:00
|
|
|
|
|
|
|
/// Collision shape for this ship
|
|
|
|
pub collision: Collision,
|
|
|
|
|
|
|
|
/// Remove later
|
|
|
|
pub aspect: f32,
|
2023-12-30 17:51:57 -08:00
|
|
|
|
|
|
|
/// Reduction in angular velocity over time
|
|
|
|
pub angular_drag: f32,
|
|
|
|
|
|
|
|
/// Reduction in velocity over time
|
|
|
|
pub linear_drag: f32,
|
2023-12-30 21:05:06 -08:00
|
|
|
|
|
|
|
/// Outfit space in this ship
|
|
|
|
pub space: OutfitSpace,
|
2024-01-05 18:04:30 -08:00
|
|
|
|
|
|
|
/// Ship collapse sequence
|
|
|
|
pub collapse: ShipCollapse,
|
2023-12-30 10:58:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Collision shape for this ship
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Collision {
|
|
|
|
pub points: Vec<Point<f32, 2>>,
|
|
|
|
pub indices: Vec<[u32; 2]>,
|
2023-12-27 19:51:58 -08:00
|
|
|
}
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// An engine point on a ship.
|
|
|
|
/// This is where flares are drawn.
|
2023-12-27 19:51:58 -08:00
|
|
|
#[derive(Debug, Clone)]
|
2023-12-28 17:04:41 -08:00
|
|
|
pub struct EnginePoint {
|
2023-12-29 15:46:09 -08:00
|
|
|
/// This engine point's position, in game units,
|
|
|
|
/// relative to the ship's center.
|
2023-12-27 19:51:58 -08:00
|
|
|
pub pos: Point2<f32>,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// The size of the flare that should be drawn
|
|
|
|
/// at this point, measured as height in game units.
|
2023-12-27 19:51:58 -08:00
|
|
|
pub size: f32,
|
|
|
|
}
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// A gun point on a ship.
|
2023-12-27 19:51:58 -08:00
|
|
|
#[derive(Debug, Clone)]
|
2023-12-28 17:04:41 -08:00
|
|
|
pub struct GunPoint {
|
2023-12-29 15:46:09 -08:00
|
|
|
/// This gun point's position, in game units,
|
|
|
|
/// relative to the ship's center.
|
2023-12-27 19:51:58 -08:00
|
|
|
pub pos: Point2<f32>,
|
|
|
|
}
|
|
|
|
|
2024-01-05 18:04:30 -08:00
|
|
|
/// Parameters for a ship's collapse sequence
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct ShipCollapse {
|
|
|
|
/// Collapse sequence length, in seconds
|
|
|
|
pub length: f32,
|
|
|
|
|
|
|
|
/// Effects to create during collapse
|
|
|
|
pub effects: Vec<CollapseEffectSpawner>,
|
|
|
|
|
|
|
|
/// Scripted events during ship collapse
|
|
|
|
pub events: Vec<CollapseEvent>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A scripted event during a ship collapse sequence
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct CollapseEffectSpawner {
|
|
|
|
/// The effect to create
|
|
|
|
pub effect: EffectHandle,
|
|
|
|
|
|
|
|
/// How many effects to create
|
|
|
|
pub count: f32,
|
|
|
|
|
|
|
|
/// Where to create these effects.
|
|
|
|
/// Position is random if None.
|
|
|
|
pub pos: Option<Point2<f32>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A scripted event during a ship collapse sequence
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum CollapseEvent {
|
|
|
|
/// A scripted effect during a ship collapse sequence
|
|
|
|
Effect(EffectCollapseEvent),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A scripted effect during a ship collapse sequence
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct EffectCollapseEvent {
|
|
|
|
/// When to trigger this event
|
|
|
|
pub time: f32,
|
|
|
|
|
|
|
|
/// The effect to create
|
|
|
|
pub effects: Vec<CollapseEffectSpawner>,
|
|
|
|
}
|
|
|
|
|
2023-12-30 16:57:03 -08:00
|
|
|
impl crate::Build for Ship {
|
2024-01-04 17:17:55 -08:00
|
|
|
type InputSyntaxType = HashMap<String, syntax::Ship>;
|
2023-12-30 10:58:17 -08:00
|
|
|
|
2024-01-05 12:09:59 -08:00
|
|
|
fn build(
|
|
|
|
ship: Self::InputSyntaxType,
|
2024-01-05 18:04:30 -08:00
|
|
|
build_context: &mut ContentBuildContext,
|
|
|
|
ct: &mut Content,
|
2024-01-05 12:09:59 -08:00
|
|
|
) -> Result<()> {
|
2023-12-27 20:13:39 -08:00
|
|
|
for (ship_name, ship) in ship {
|
2024-01-05 18:04:30 -08:00
|
|
|
let handle = match ct.sprite_index.get(&ship.sprite) {
|
2023-12-30 10:58:17 -08:00
|
|
|
None => bail!(
|
2024-01-04 17:17:55 -08:00
|
|
|
"In ship `{}`: sprite `{}` doesn't exist",
|
2023-12-30 10:58:17 -08:00
|
|
|
ship_name,
|
2024-01-04 17:17:55 -08:00
|
|
|
ship.sprite
|
2023-12-30 10:58:17 -08:00
|
|
|
),
|
|
|
|
Some(t) => *t,
|
|
|
|
};
|
|
|
|
|
2023-12-30 11:07:20 -08:00
|
|
|
let size = ship.size;
|
2024-01-05 18:04:30 -08:00
|
|
|
let aspect = ct.get_sprite(handle).aspect;
|
|
|
|
|
|
|
|
let collapse = if let Some(c) = ship.collapse {
|
|
|
|
let mut effects = Vec::new();
|
|
|
|
for e in c.effects {
|
|
|
|
effects.push(CollapseEffectSpawner {
|
|
|
|
effect: e
|
|
|
|
.effect
|
|
|
|
.to_handle(build_context, ct)
|
|
|
|
.with_context(|| format!("while loading ship `{}`", ship_name))?,
|
|
|
|
count: e.count,
|
|
|
|
pos: e.pos.map(|p| Point2 {
|
|
|
|
x: p[0] * (size / 2.0) * aspect,
|
|
|
|
y: p[1] * size / 2.0,
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut events = Vec::new();
|
|
|
|
for e in c.event {
|
|
|
|
match e {
|
|
|
|
syntax::CollapseEvent::Effect(e) => {
|
|
|
|
let mut effects = Vec::new();
|
|
|
|
for g in e.effects {
|
|
|
|
effects.push(CollapseEffectSpawner {
|
|
|
|
effect: g.effect.to_handle(build_context, ct).with_context(
|
|
|
|
|| format!("while loading ship `{}`", ship_name),
|
|
|
|
)?,
|
|
|
|
count: g.count,
|
|
|
|
pos: g.pos.map(|p| Point2 {
|
|
|
|
x: p[0] * (size / 2.0) * aspect,
|
|
|
|
y: p[1] * size / 2.0,
|
|
|
|
}),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
events.push(CollapseEvent::Effect(EffectCollapseEvent {
|
|
|
|
time: e.time,
|
|
|
|
effects,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ShipCollapse {
|
|
|
|
length: c.length,
|
|
|
|
effects,
|
|
|
|
events,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Default collapse sequence
|
|
|
|
ShipCollapse {
|
|
|
|
length: 0.0,
|
|
|
|
effects: vec![],
|
|
|
|
events: vec![],
|
|
|
|
}
|
|
|
|
};
|
2023-12-30 11:07:20 -08:00
|
|
|
|
2024-01-05 18:04:30 -08:00
|
|
|
ct.ships.push(Self {
|
2023-12-30 10:58:17 -08:00
|
|
|
aspect,
|
2024-01-05 18:04:30 -08:00
|
|
|
collapse,
|
2023-12-30 11:07:20 -08:00
|
|
|
name: ship_name,
|
2024-01-04 17:17:55 -08:00
|
|
|
sprite: handle,
|
2023-12-30 11:07:20 -08:00
|
|
|
mass: ship.mass,
|
2023-12-30 21:05:06 -08:00
|
|
|
space: OutfitSpace::from(ship.space),
|
2023-12-30 17:51:57 -08:00
|
|
|
angular_drag: ship.angular_drag,
|
|
|
|
linear_drag: ship.linear_drag,
|
2023-12-28 20:19:33 -08:00
|
|
|
size,
|
2023-12-29 12:21:56 -08:00
|
|
|
hull: ship.hull,
|
2024-01-05 13:25:44 -08:00
|
|
|
|
2023-12-27 19:51:58 -08:00
|
|
|
engines: ship
|
|
|
|
.engines
|
|
|
|
.iter()
|
2023-12-28 17:04:41 -08:00
|
|
|
.map(|e| EnginePoint {
|
2023-12-28 20:19:33 -08:00
|
|
|
pos: Point2 {
|
2023-12-30 10:58:17 -08:00
|
|
|
x: e.x * size * aspect / 2.0,
|
|
|
|
y: e.y * size / 2.0,
|
2023-12-28 20:19:33 -08:00
|
|
|
},
|
2023-12-27 19:51:58 -08:00
|
|
|
size: e.size,
|
|
|
|
})
|
|
|
|
.collect(),
|
2024-01-05 13:25:44 -08:00
|
|
|
|
2023-12-27 19:51:58 -08:00
|
|
|
guns: ship
|
|
|
|
.guns
|
|
|
|
.iter()
|
2023-12-28 17:04:41 -08:00
|
|
|
.map(|e| GunPoint {
|
2023-12-28 20:19:33 -08:00
|
|
|
pos: Point2 {
|
2023-12-30 10:58:17 -08:00
|
|
|
x: e.x * size * aspect / 2.0,
|
|
|
|
y: e.y * size / 2.0,
|
2023-12-28 20:19:33 -08:00
|
|
|
},
|
2023-12-27 19:51:58 -08:00
|
|
|
})
|
|
|
|
.collect(),
|
2024-01-05 13:25:44 -08:00
|
|
|
|
2023-12-30 10:58:17 -08:00
|
|
|
collision: Collision {
|
2024-01-05 13:25:44 -08:00
|
|
|
indices: (0..ship.collision.len())
|
|
|
|
.map(|x| {
|
|
|
|
// Auto-generate mesh lines:
|
|
|
|
// [ [0, 1], [1, 2], ..., [n, 0] ]
|
|
|
|
let next = if x == ship.collision.len() - 1 {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
x + 1
|
|
|
|
};
|
|
|
|
[x as u32, next as u32]
|
|
|
|
})
|
|
|
|
.collect(),
|
2023-12-30 10:58:17 -08:00
|
|
|
points: ship
|
|
|
|
.collision
|
|
|
|
.iter()
|
|
|
|
.map(|x| point![x[0] * (size / 2.0) * aspect, x[1] * size / 2.0])
|
|
|
|
.collect(),
|
|
|
|
},
|
2023-12-27 19:51:58 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-12-30 10:58:17 -08:00
|
|
|
return Ok(());
|
2023-12-27 19:51:58 -08:00
|
|
|
}
|
|
|
|
}
|