Added system loading from file
parent
50bd3bdb8c
commit
619b8ae797
|
@ -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
|
||||
|
|
|
@ -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<syntax::System>,
|
||||
pub systems: Vec<syntax::system::System>,
|
||||
}
|
||||
|
||||
impl Content {
|
||||
pub fn new(cv: Vec<ContentType>) -> Result<Self> {
|
||||
pub fn new(cv: Vec<(PathBuf, ContentType)>) -> Result<Self> {
|
||||
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(());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Option<Self>> {
|
||||
// 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),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Content> {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
#![allow(dead_code)]
|
||||
pub mod system;
|
||||
pub use system::System;
|
||||
|
|
|
@ -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<Object>,
|
||||
pub name: String,
|
||||
pub objects: Vec<Object>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Object {
|
||||
sprite: String,
|
||||
center: Point2<Pfloat>,
|
||||
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<Point2<f32>> {
|
||||
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<Self> {
|
||||
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(),
|
||||
position: Polar {
|
||||
center,
|
||||
radius: obj.radius,
|
||||
angle: obj.angle,
|
||||
angle: Deg(obj.angle),
|
||||
},
|
||||
size: obj.size,
|
||||
parallax: obj.parallax,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
36
src/main.rs
36
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(());
|
||||
}
|
||||
|
|
|
@ -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<Pfloat>,
|
||||
|
||||
// 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(),
|
||||
};
|
||||
|
||||
for o in &ct.objects {
|
||||
s.bodies.push(Doodad {
|
||||
pos: (0.0, 0.0).into(),
|
||||
sprite: SpriteTexture { name: "star::star" },
|
||||
parallax: 10.0,
|
||||
height: 80.0,
|
||||
pos: o.position.to_cartesian(),
|
||||
sprite: SpriteTexture(o.sprite.to_owned()),
|
||||
size: o.size,
|
||||
parallax: o.parallax,
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue