diff --git a/content/system.toml b/content/system.toml index 0496ea1..9307fdf 100644 --- a/content/system.toml +++ b/content/system.toml @@ -1,4 +1,5 @@ # TODO: big objects in one config +# TODO: satisfy conditions to land [system."12 Autumn Above"] name = "12 Autumn Above" @@ -13,10 +14,9 @@ object.earth.position.angle = 0 object.earth.position.z = 10.0 object.earth.size = 1000 -# TODO: satisfy conditions to land -object.earth.landable = true -object.earth.name = "Earth" -object.earth.desc = """ + +object.earth.landable.name = "Earth" +object.earth.landable.desc = """ The ancestral home world of humanity, Earth has a population twice that of any other inhabited planet. Sprawling cities cover large portions of its surface, many of them overcrowded and dangerous. Some people work to scrape together enough money to leave, while at the same time others, born @@ -28,8 +28,13 @@ one planet has a greater population than a hundred planets elsewhere. As a resul settlements of less than a million are grouped together into planetary districts that elect a single representative between them - a source of much frustration in the frontier worlds. """ -object.earth.image = "ui::landscape::test" +object.earth.landable.image = "ui::landscape::test" +object.earth.landable.outfitter = [ + "plasma engines", + "shield generator", + "blaster", +] object.luna.sprite = "planet::luna" object.luna.position.center = "earth" diff --git a/crates/content/src/lib.rs b/crates/content/src/lib.rs index 7ba8c10..46598b8 100644 --- a/crates/content/src/lib.rs +++ b/crates/content/src/lib.rs @@ -165,8 +165,7 @@ impl Display for ContentIndex { /// Stores temporary data while building context objects #[derive(Debug)] pub(crate) struct ContentBuildContext { - /// Map effect names to handles - pub effect_index: HashMap>, + pub effect_index: HashMap>, } impl ContentBuildContext { diff --git a/crates/content/src/part/effect.rs b/crates/content/src/part/effect.rs index 26f7c33..9ae3934 100644 --- a/crates/content/src/part/effect.rs +++ b/crates/content/src/part/effect.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use std::{collections::HashMap, sync::Arc}; -use crate::{Content, ContentBuildContext, Sprite}; +use crate::{Content, ContentBuildContext, ContentIndex, Sprite}; pub(crate) mod syntax { use std::sync::Arc; @@ -138,12 +138,12 @@ pub(crate) mod syntax { #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum EffectReference { - Label(String), + Label(ContentIndex), Effect(Effect), } impl EffectReference { - pub fn to_handle( + pub fn resolve( self, build_context: &mut ContentBuildContext, content: &mut Content, @@ -246,7 +246,9 @@ impl crate::Build for Effect { let h = effect .add_to(build_context, content, &effect_name) .with_context(|| format!("while evaluating effect `{}`", effect_name))?; - build_context.effect_index.insert(effect_name, h); + build_context + .effect_index + .insert(ContentIndex::new(&effect_name), h); } return Ok(()); diff --git a/crates/content/src/part/outfit.rs b/crates/content/src/part/outfit.rs index 300a560..81edfd5 100644 --- a/crates/content/src/part/outfit.rs +++ b/crates/content/src/part/outfit.rs @@ -81,12 +81,12 @@ pub(crate) mod syntax { }; let impact_effect = match self.projectile.impact_effect { - Some(e) => Some(e.to_handle(build_context, content, "")?), + Some(e) => Some(e.resolve(build_context, content, "")?), None => None, }; let expire_effect = match self.projectile.expire_effect { - Some(e) => Some(e.to_handle(build_context, content, "")?), + Some(e) => Some(e.resolve(build_context, content, "")?), None => None, }; @@ -143,7 +143,7 @@ pub struct Outfit { pub space: OutfitSpace, /// The name of this outfit - pub name: String, + pub display_name: String, /// Thie outfit's index pub index: ContentIndex, @@ -281,7 +281,7 @@ impl crate::Build for Outfit { let mut o = Self { index: ContentIndex::new(&outfit_name), - name: outfit.name, + display_name: outfit.name, thumbnail, gun, engine_thrust: 0.0, diff --git a/crates/content/src/part/ship.rs b/crates/content/src/part/ship.rs index ac6d195..5f8eca0 100644 --- a/crates/content/src/part/ship.rs +++ b/crates/content/src/part/ship.rs @@ -298,7 +298,7 @@ impl crate::Build for Ship { effects.push(CollapseEffectSpawner { effect: e .effect - .to_handle(build_context, ct, "") + .resolve(build_context, ct, "") .with_context(|| format!("while loading ship `{}`", ship_name))?, count: e.count, pos: e.pos.map(|p| { @@ -316,7 +316,7 @@ impl crate::Build for Ship { effects.push(CollapseEffectSpawner { effect: g .effect - .to_handle(build_context, ct, "") + .resolve(build_context, ct, "") .with_context(|| { format!("while loading ship `{}`", ship_name) })?, @@ -360,7 +360,7 @@ impl crate::Build for Ship { effects.push(DamageEffectSpawner { effect: e .effect - .to_handle(build_context, ct, "") + .resolve(build_context, ct, "") .with_context(|| format!("while loading ship `{}`", ship_name))?, frequency: e.frequency, pos: e.pos.map(|p| { diff --git a/crates/content/src/part/system.rs b/crates/content/src/part/system.rs index acb1dc0..78c290f 100644 --- a/crates/content/src/part/system.rs +++ b/crates/content/src/part/system.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use galactica_util::to_radians; use nalgebra::{Point2, Point3}; use std::{ @@ -6,7 +6,7 @@ use std::{ sync::Arc, }; -use crate::{util::Polar, Content, ContentBuildContext, ContentIndex, Sprite}; +use crate::{util::Polar, Content, ContentBuildContext, ContentIndex, Outfit, Sprite}; pub(crate) mod syntax { use serde::Deserialize; @@ -26,15 +26,18 @@ pub(crate) mod syntax { pub struct Object { pub sprite: ContentIndex, pub position: Position, - pub size: f32, - pub radius: Option, pub angle: Option, - pub landable: Option, - pub name: Option, - pub desc: Option, - pub image: Option, + pub landable: Option, + } + + #[derive(Debug, Deserialize)] + pub struct Landable { + pub name: String, + pub desc: String, + pub image: ContentIndex, + pub outfitter: Vec, } #[derive(Debug, Deserialize)] @@ -107,9 +110,6 @@ pub struct System { /// System objects to not interact with the physics engine. #[derive(Debug, Clone)] pub struct SystemObject { - /// This object's name - pub display_name: Option, - /// This object's index pub index: ContentIndex, @@ -130,13 +130,22 @@ pub struct SystemObject { pub angle: f32, /// If true, ships may land on this object - pub landable: bool, + pub landable: Option, +} - /// The description of this object (shown on landed ui) - pub desc: Option, +#[derive(Debug, Clone)] +pub struct LandableSystemObject { + /// This object's name + pub display_name: String, - /// This object's image (shown on landed ui) - pub image: Option>, + /// The description of this object + pub desc: String, + + /// This object's image + pub image: Arc, + + /// The outfits we can buy here + pub outfitter: Vec>, } /// Helper function for resolve_position, never called on its own. @@ -210,71 +219,64 @@ impl crate::Build for System { for (system_name, system) in system { let mut objects = HashMap::new(); - for (index, obj) in &system.object { + for (index, object) in &system.object { let mut cycle_detector = HashSet::new(); cycle_detector.insert(index.clone()); - let sprite = match content.sprites.get(&obj.sprite) { + let sprite = match content.sprites.get(&object.sprite) { None => bail!( "In system `{}`: sprite `{}` doesn't exist", system_name, - obj.sprite + object.sprite ), Some(t) => t.clone(), }; - let image = match &obj.image { - Some(x) => match content.sprites.get(x) { + let landable = 'landable: { + if object.landable.is_none() { + break 'landable None; + } + let l = object.landable.as_ref().unwrap(); + + let image = match content.sprites.get(&l.image) { None => bail!( "In system `{}`: sprite `{}` doesn't exist", system_name, - obj.sprite + object.sprite ), - Some(t) => Some(t.clone()), - }, - None => None, + Some(t) => t.clone(), + }; + + let mut outfitter = Vec::new(); + for o in &l.outfitter { + match content.outfits.get(&o) { + Some(x) => outfitter.push(x.clone()), + None => { + bail!("In system `{}`: outfit `{}` doesn't exist", system_name, o) + } + } + } + + break 'landable Some(LandableSystemObject { + image, + outfitter, + display_name: l.name.clone(), + // TODO: better linebreaks, handle double spaces + // Tabs + desc: l.desc.replace("\n", " ").replace("
", "\n"), + }); }; - if obj.landable.unwrap_or(false) { - if obj.name.is_none() { - return Err(anyhow!("if an object is landable, it must have a name")) - .with_context(|| format!("in object labeled `{}`", index)) - .with_context(|| format!("in system `{}`", system_name)); - } - - if obj.desc.is_none() { - return Err(anyhow!( - "if an object is landable, it must have a description" - )) - .with_context(|| format!("in object labeled `{}`", index)) - .with_context(|| format!("in system `{}`", system_name)); - } - - if obj.image.is_none() { - return Err(anyhow!("if an object is landable, it must have an image")) - .with_context(|| format!("in object labeled `{}`", index)) - .with_context(|| format!("in system `{}`", system_name)); - } - } - objects.insert( index.clone(), Arc::new(SystemObject { index: index.clone(), sprite, - image, - pos: resolve_position(&system.object, &obj, cycle_detector) + pos: resolve_position(&system.object, &object, cycle_detector) .with_context(|| format!("in object {:#?}", index))?, - size: obj.size, - angle: to_radians(obj.angle.unwrap_or(0.0)), - landable: obj.landable.unwrap_or(false), - display_name: obj.name.as_ref().map(|x| x.clone()), - // TODO: better linebreaks, handle double spaces - // Tabs - desc: obj - .desc - .as_ref() - .map(|x| x.replace("\n", " ").replace("
", "\n")), + size: object.size, + angle: to_radians(object.angle.unwrap_or(0.0)), + landable: landable, }), ); } diff --git a/crates/content/src/spriteautomaton.rs b/crates/content/src/spriteautomaton.rs index 06eeafc..977c412 100644 --- a/crates/content/src/spriteautomaton.rs +++ b/crates/content/src/spriteautomaton.rs @@ -234,7 +234,8 @@ impl SpriteAutomaton { // Edge case: we're stopped and got a request to transition. // we should transition right away. - if let Some(e) = self.next_edge_override.take() { + if self.next_edge_override.is_some() { + let e = self.next_edge_override.take().unwrap(); self.take_edge(&e); }