From 619b8ae797c48a42e73bc06b5474542ef2a4ba37 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 25 Dec 2023 09:01:12 -0800 Subject: [PATCH] Added system loading from file --- content/system.toml | 10 +++++-- src/content/content.rs | 17 ++++++----- src/content/contenttype.rs | 19 ++++++++---- src/content/mod.rs | 35 ++++++++++++++++++++++ src/content/syntax/mod.rs | 1 - src/content/syntax/system.rs | 58 +++++++++++++++++++++++++----------- src/main.rs | 36 ++++++++-------------- src/system.rs | 43 ++++++++++---------------- 8 files changed, 134 insertions(+), 85 deletions(-) diff --git a/content/system.toml b/content/system.toml index b9b595a..904f49f 100644 --- a/content/system.toml +++ b/content/system.toml @@ -3,11 +3,15 @@ name = "12 Autumn above" [object.star] -sprite = "star::a0" +sprite = "star::star" center = [0.0, 0.0] +size = 50 +parallax = 1.0 [object.earth] sprite = "planet::earth" center = "star" -radius = 200 -angle = 14 +radius = 1000 +angle = 0 +size = 100 +parallax = 1.0 diff --git a/src/content/content.rs b/src/content/content.rs index 9221e2b..186cc2e 100644 --- a/src/content/content.rs +++ b/src/content/content.rs @@ -1,24 +1,25 @@ -use anyhow::Result; +use anyhow::{Context, Result}; +use std::path::PathBuf; use super::{syntax, ContentType}; #[derive(Debug)] pub struct Content { - systems: Vec, + pub systems: Vec, } impl Content { - pub fn new(cv: Vec) -> Result { + pub fn new(cv: Vec<(PathBuf, ContentType)>) -> Result { let mut content = Self { systems: Vec::new(), }; - // TODO: locate bad files - // These methods check intra-file consistency - for c in cv { + for (p, c) in cv { match c { - ContentType::System(v) => content.add_system(v)?, + ContentType::System(v) => content + .add_system(v) + .with_context(|| format!("Could not parse {}", p.display()))?, }; } @@ -26,7 +27,7 @@ impl Content { } fn add_system(&mut self, toml: syntax::system::toml::SystemRoot) -> Result<()> { - self.systems.push(syntax::System::parse(toml)?); + self.systems.push(syntax::system::System::parse(toml)?); return Ok(()); } } diff --git a/src/content/contenttype.rs b/src/content/contenttype.rs index d73f037..50a85b4 100644 --- a/src/content/contenttype.rs +++ b/src/content/contenttype.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use std::{fs::File, io::Read, path::Path}; use super::syntax; @@ -12,12 +12,21 @@ pub enum ContentType { impl ContentType { pub fn try_parse(file_string: &str) -> Result> { // TODO: More advanced parsing, read the whole top comment - let (first, _) = file_string.split_once("\n").unwrap(); + let first = match file_string.split_once("\n") { + None => bail!("This file is empty."), + Some((first, _)) => first, + }; let type_spec = first[1..].trim(); // Remove hash - return Ok(match type_spec { - "content type: system" => Some(Self::System(toml::from_str(&file_string)?)), - _ => None, + let type_spec = if type_spec.starts_with("content type: ") { + type_spec[14..].to_owned() + } else { + bail!("No content type specified") + }; + + return Ok(match &type_spec[..] { + "system" => Some(Self::System(toml::from_str(&file_string)?)), + _ => bail!("Invalid content type `{}`", type_spec), }); } diff --git a/src/content/mod.rs b/src/content/mod.rs index 9cc6345..80d5ed3 100644 --- a/src/content/mod.rs +++ b/src/content/mod.rs @@ -4,3 +4,38 @@ mod syntax; pub use content::Content; pub use contenttype::ContentType; +pub use syntax::system; + +use anyhow::{Context, Result}; +use walkdir::WalkDir; + +pub fn load_content_dir(path: &str) -> Result { + let mut raw_content = Vec::new(); + for e in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) { + if e.metadata().unwrap().is_file() { + // TODO: better warnings + match e.path().extension() { + Some(t) => { + if t.to_str() != Some("toml") { + println!("[WARNING] {e:#?} is not a toml file, skipping."); + continue; + } + } + None => { + println!("[WARNING] {e:#?} is not a toml file, skipping."); + continue; + } + } + + let c = crate::content::ContentType::from_path(e.path()) + .with_context(|| format!("Could not load {:#?}", e.path()))?; + + match c { + Some(c) => raw_content.push((e.path().to_path_buf(), c)), + None => continue, + } + } + } + + return crate::content::Content::new(raw_content); +} diff --git a/src/content/syntax/mod.rs b/src/content/syntax/mod.rs index 57d8314..7a70559 100644 --- a/src/content/syntax/mod.rs +++ b/src/content/syntax/mod.rs @@ -1,3 +1,2 @@ #![allow(dead_code)] pub mod system; -pub use system::System; diff --git a/src/content/syntax/system.rs b/src/content/syntax/system.rs index 8ccacb9..3747469 100644 --- a/src/content/syntax/system.rs +++ b/src/content/syntax/system.rs @@ -1,8 +1,8 @@ use anyhow::{bail, Result}; -use cgmath::Point2; +use cgmath::{Deg, Point2}; use std::collections::HashMap; -use crate::physics::Pfloat; +use crate::physics::{Pfloat, Polar}; /// Toml file syntax pub(in crate::content) mod toml { @@ -26,6 +26,9 @@ pub(in crate::content) mod toml { pub sprite: String, pub center: Center, + pub size: Pfloat, + pub parallax: Pfloat, + #[serde(default)] pub radius: Pfloat, #[serde(default)] @@ -38,20 +41,29 @@ pub(in crate::content) mod toml { Label(String), Coords([Pfloat; 2]), } + + impl ToString for Center { + fn to_string(&self) -> String { + match self { + Self::Label(s) => s.to_owned(), + Self::Coords(v) => format!("{:?}", v), + } + } + } } #[derive(Debug)] pub struct System { - name: String, - objects: Vec, + pub name: String, + pub objects: Vec, } #[derive(Debug)] -struct Object { - sprite: String, - center: Point2, - radius: Pfloat, - angle: Pfloat, +pub struct Object { + pub sprite: String, + pub position: Polar, + pub size: Pfloat, + pub parallax: Pfloat, } fn resolve_center( @@ -59,8 +71,15 @@ fn resolve_center( obj: &toml::Object, ) -> Option> { match &obj.center { - toml::Center::Label(s) => resolve_center(&objects, objects.get(s)?), - toml::Center::Coords(v) => Some(Point2::from(*v)), + toml::Center::Label(s) => Some( + Polar { + center: resolve_center(&objects, objects.get(s)?)?, + radius: obj.radius, + angle: Deg(obj.angle), + } + .to_cartesian(), + ), + toml::Center::Coords(v) => Some((*v).into()), } } @@ -68,22 +87,27 @@ impl System { pub fn parse(value: toml::SystemRoot) -> Result { let mut objects = Vec::new(); - for (_, obj) in &value.object { + for (label, obj) in &value.object { let center = match resolve_center(&value.object, obj) { Some(v) => v, None => { bail!( - "Failed to parse content file: could not resolve center label `{:?}`", - obj.center + "could not resolve center label `{}` in `object.{}`", + obj.center.to_string(), + label ); } }; objects.push(Object { sprite: obj.sprite.clone(), - center, - radius: obj.radius, - angle: obj.angle, + position: Polar { + center, + radius: obj.radius, + angle: Deg(obj.angle), + }, + size: obj.size, + parallax: obj.parallax, }); } diff --git a/src/main.rs b/src/main.rs index 0d8a053..0db8080 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,8 +8,8 @@ mod system; use anyhow::Result; use cgmath::{Deg, Point2}; +use content::Content; use std::time::Instant; -use walkdir::WalkDir; use winit::{ event::{ ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase, @@ -69,10 +69,10 @@ struct Sprite { /// The size of this sprite, /// given as height in world units. - height: Pfloat, + size: Pfloat, /// Scale factor. - /// if this is 1, sprite height is exactly self.height. + /// if this is 1, sprite height is exactly self.size. scale: Pfloat, /// This sprite's rotation @@ -95,7 +95,7 @@ struct Game { } impl Game { - fn new() -> Self { + fn new(ct: Content) -> Self { Game { last_update: Instant::now(), input: InputStatus::new(), @@ -104,7 +104,7 @@ impl Game { pos: (0.0, 0.0).into(), zoom: 500.0, }, - system: System::new(), + system: System::new(&ct.systems[0]), } } @@ -128,11 +128,11 @@ impl Game { } if self.input.key_right { - self.player.body.rot(Deg { 0: 35.0 } * t); + self.player.body.rot(Deg(35.0) * t); } if self.input.key_left { - self.player.body.rot(Deg { 0: -35.0 } * t); + self.player.body.rot(Deg(-35.0) * t); } if self.input.v_scroll != 0.0 { @@ -140,6 +140,7 @@ impl Game { self.input.v_scroll = 0.0; } + println!("{:?}", self.player.body.pos); self.player.body.tick(t); self.camera.pos = self.player.body.pos; @@ -163,12 +164,11 @@ impl Game { } } -pub async fn run() -> Result<()> { +async fn run(mut game: Game) -> Result<()> { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); let mut gpu = GPUState::new(window).await?; - let mut game = Game::new(); gpu.update_starfield_buffer(&game); @@ -231,20 +231,10 @@ pub async fn run() -> Result<()> { } fn main() -> Result<()> { - let mut raw_content = Vec::new(); - for e in WalkDir::new("content").into_iter().filter_map(|e| e.ok()) { - if e.metadata().unwrap().is_file() { - let c = crate::content::ContentType::from_path(e.path())?; - match c { - Some(c) => raw_content.push(c), - None => continue, - } - } - } + let content = content::load_content_dir("./content")?; + println!("{:?}", content); + let game = Game::new(content); - let a = crate::content::Content::new(raw_content)?; - println!("{:?}", a); - - //pollster::block_on(run())?; + pollster::block_on(run(game))?; return Ok(()); } diff --git a/src/system.rs b/src/system.rs index adf80f1..73e72ee 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,10 +1,8 @@ use crate::{ - physics::{Pfloat, Polar}, - render::SpriteTexture, - Doodad, Sprite, Spriteable, STARFIELD_COUNT, STARFIELD_PARALLAX_MAX, STARFIELD_PARALLAX_MIN, - STARFIELD_SIZE, + content, physics::Pfloat, render::SpriteTexture, Doodad, Sprite, Spriteable, STARFIELD_COUNT, + STARFIELD_PARALLAX_MAX, STARFIELD_PARALLAX_MIN, STARFIELD_SIZE, }; -use cgmath::{Deg, Point2, Vector2}; +use cgmath::{Point2, Vector2}; use rand::{self, Rng}; pub struct StarfieldStar { @@ -12,8 +10,9 @@ pub struct StarfieldStar { /// These are relative to the center of a starfield tile. pub pos: Point2, + // TODO: z-coordinate? pub parallax: Pfloat, - pub height: Pfloat, + pub size: Pfloat, /// Color/brightness variation. /// See shader. @@ -26,7 +25,7 @@ pub struct System { } impl System { - pub fn new() -> Self { + pub fn new(ct: &content::system::System) -> Self { let mut rng = rand::thread_rng(); let sz = STARFIELD_SIZE as f32 / 2.0; let mut s = System { @@ -38,7 +37,7 @@ impl System { y: rng.gen_range(-sz..=sz), }, parallax: rng.gen_range(STARFIELD_PARALLAX_MIN..STARFIELD_PARALLAX_MAX), - height: rng.gen_range(0.2..0.8), // TODO: configurable + size: rng.gen_range(0.2..0.8), // TODO: configurable tint: Vector2 { x: rng.gen_range(0.0..=1.0), y: rng.gen_range(0.0..=1.0), @@ -47,26 +46,14 @@ impl System { .collect(), }; - s.bodies.push(Doodad { - pos: (0.0, 0.0).into(), - sprite: SpriteTexture { name: "star::star" }, - parallax: 10.0, - height: 80.0, - }); - - s.bodies.push(Doodad { - pos: Polar { - center: (0.0, 0.0).into(), - radius: 5000.0, - angle: Deg { 0: 31.0 }, - } - .to_cartesian(), - sprite: SpriteTexture { - name: "planet::earth", - }, - parallax: 5.0, - height: 120.0, - }); + for o in &ct.objects { + s.bodies.push(Doodad { + pos: o.position.to_cartesian(), + sprite: SpriteTexture(o.sprite.to_owned()), + size: o.size, + parallax: o.parallax, + }); + } return s; }