Compare commits

..

No commits in common. "b5cc0b555e750a027e2ace0b5b875393b6b79466" and "85c42652161604931330f56724e8267124554738" have entirely different histories.

25 changed files with 618 additions and 413 deletions

87
Cargo.lock generated
View File

@ -40,8 +40,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"const-random",
"getrandom",
"once_cell", "once_cell",
"version_check", "version_check",
"zerocopy", "zerocopy",
@ -411,26 +409,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642" checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642"
[[package]]
name = "const-random"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a"
dependencies = [
"const-random-macro",
]
[[package]]
name = "const-random-macro"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
dependencies = [
"getrandom",
"once_cell",
"tiny-keccak",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.4" version = "0.9.4"
@ -817,7 +795,6 @@ dependencies = [
"log", "log",
"nalgebra", "nalgebra",
"rapier2d", "rapier2d",
"rhai",
"serde", "serde",
"toml", "toml",
"walkdir", "walkdir",
@ -865,7 +842,6 @@ dependencies = [
"log", "log",
"nalgebra", "nalgebra",
"rand", "rand",
"rhai",
"wgpu", "wgpu",
"winit", "winit",
] ]
@ -1958,36 +1934,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b"
[[package]]
name = "rhai"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0de8df6dd1f4886ad635d7e0fc7afc086e4f6daeff129d157287b78738516b"
dependencies = [
"ahash",
"bitflags 2.4.1",
"instant",
"num-traits",
"once_cell",
"rhai_codegen",
"serde",
"serde_json",
"smallvec",
"smartstring",
"thin-vec",
]
[[package]]
name = "rhai_codegen"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89789ba6e8fd0889ae70b39c09000148431c9a1d618eb9d388373f391a55c988"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.42",
]
[[package]] [[package]]
name = "robust" name = "robust"
version = "1.1.0" version = "1.1.0"
@ -2191,21 +2137,6 @@ name = "smallvec"
version = "1.11.2" version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
dependencies = [
"serde",
]
[[package]]
name = "smartstring"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
dependencies = [
"autocfg",
"serde",
"static_assertions",
"version_check",
]
[[package]] [[package]]
name = "smithay-client-toolkit" name = "smithay-client-toolkit"
@ -2331,15 +2262,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "thin-vec"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.51" version = "1.0.51"
@ -2381,15 +2303,6 @@ dependencies = [
"weezl", "weezl",
] ]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]] [[package]]
name = "tiny-skia" name = "tiny-skia"
version = "0.8.4" version = "0.8.4"

View File

@ -74,9 +74,3 @@ lazy_static = "1.4.0"
clap = { version = "4.4.18", features = ["derive"] } clap = { version = "4.4.18", features = ["derive"] }
log = "0.4.20" log = "0.4.20"
log4rs = { version = "1.2.0", features = ["console_appender"] } log4rs = { version = "1.2.0", features = ["console_appender"] }
rhai = { version = "1.17.0", features = [
"f32_float",
"metadata",
"sync",
"no_custom_syntax",
] }

View File

@ -4,8 +4,6 @@
- outfitter - outfitter
## Small jobs ## Small jobs
- Clean up scripting & errors
- Clean up sprite content (and content in general)
- Fix window resizing - Fix window resizing
- Debug: show mouse position - Debug: show mouse position
- 🌟 Better planet desc formatting - 🌟 Better planet desc formatting

View File

@ -36,7 +36,3 @@ starfield.texture = "starfield.png"
# Zoom is measured as "window height in game units." # Zoom is measured as "window height in game units."
zoom_min = 200.0 zoom_min = 200.0
zoom_max = 2000.0 zoom_max = 2000.0
# TODO: move to user config file
ui_scale = 2
ui_landed_scene = "landed.rhai"

View File

@ -1,67 +0,0 @@
fn init(state) {
let frame = SpriteBuilder(
"frame",
"ui::planet",
Rect(
0.0, 0.0, 400.0, 297.866,
SpriteAnchor::Center,
SpriteAnchor::Center
)
);
let landscape = SpriteBuilder(
"landscape",
state.planet_landscape,
Rect(
-180.0, 142.0, 274.0, 135.0,
SpriteAnchor::NorthWest,
SpriteAnchor::Center
)
);
landscape.set_mask("ui::landscapemask");
let button = SpriteBuilder(
"button",
"ui::planet::button",
Rect(
99.0, 128.0, 73.898, 18.708,
SpriteAnchor::NorthWest,
SpriteAnchor::Center
)
);
let title = TextBoxBuilder(
"title",
10.0, 10.0, TextBoxFont::Serif, TextBoxJustify::Center,
Rect(
-70.79, 138.0, 59.867, 10.0,
SpriteAnchor::NorthWest,
SpriteAnchor::Center
)
);
title.set_text(state.planet_name);
return [
button,
landscape,
frame,
title,
];
}
fn hover(element, hover_state) {
if element.has_name("button") {
if hover_state {
element.take_edge("on:top", 0.1);
} else {
element.take_edge("off:top", 0.1);
}
}
}
//fn click(scene, name) {
// if name = "button" {
// return SceneAction::Outfitter();
// }
//}

View File

@ -1,6 +1,5 @@
[ship."Gypsum"] [ship."Gypsum"]
sprite = "ship::gypsum" sprite = "ship::gypsum"
thumb = "icon::gypsum"
size = 100 size = 100
mass = 1 mass = 1
hull = 200 hull = 200

80
content/ui.toml Normal file
View File

@ -0,0 +1,80 @@
[ui.status]
# TODO: unified color value
# TODO: bar type: linear/radial
# TODO: bar as ui util struct
# TODO: mouse collider
# TODO: modular UI (how?)
# shield_bar.pos = [-19, -19]
# shield_bar.diameter = 182
# shield_bar.stroke = 5
# shield_bar.color = [0.3, 0.6, 0.8, 1.0]
#
# hull_bar.pos = [-27.0, -27.0]
# hull_bar.diameter = 166.0
# hull_bar.stroke = 5
# hull_bar.color = [0.8, 0.7, 0.5, 1.0]
# frame.sprite = "ui::status"
# frame.pos = [-10.0, -10.0]
# frame.dim = [200.0, 200.0]
[ui.landed]
frame.sprite = "ui::planet"
frame.rect.pos = [0.0, 0.0]
frame.rect.dim = [800.0, 800.0]
frame.rect.anchor_self = "center"
frame.rect.anchor_parent = "center"
landscape.mask = "ui::landscapemask"
landscape.rect.pos = [-350.0, 282.8]
landscape.rect.dim = [537.5, 270.31]
landscape.rect.anchor_self = "northwest"
landscape.rect.anchor_parent = "center"
button.sprite = "ui::planet::button"
button.rect.pos = [178.12, 254.6]
button.rect.dim = [175.16, 38.437]
button.rect.anchor_self = "northwest"
button.rect.anchor_parent = "center"
button.on_mouse_enter.edge = "on:top"
button.on_mouse_enter.duration = 0.1
button.on_mouse_leave.edge = "off:top"
button.on_mouse_leave.duration = 0.1
planet_name.rect.pos = [-143.89, 273]
planet_name.rect.dim = [121.98, 18.094]
planet_name.rect.anchor_self = "northwest"
planet_name.rect.anchor_parent = "center"
planet_name.font_size = 19
planet_name.line_height = 19
planet_name.align = "center"
planet_desc.rect.pos = [-358.43, -32]
planet_desc.rect.dim = [673.91, 153.75]
planet_desc.rect.anchor_self = "northwest"
planet_desc.rect.anchor_parent = "center"
planet_desc.font_size = 16
planet_desc.line_height = 18
planet_desc.align = "left"
[ui.outfitter]
se_box.sprite = "ui::outfitterbox"
se_box.rect.pos = [-10.0, -10.0]
se_box.rect.dim = [512.0, 337.0] # todo: auto aspect
se_box.rect.anchor_self = "southwest"
se_box.rect.anchor_parent = "southwest"
exit_button.sprite = "ui::button"
exit_button.rect.pos = [279.07, 135.38]
exit_button.rect.dim = [173.44, 45.01]
exit_button.rect.anchor_self = "northwest"
exit_button.rect.anchor_parent = "southwest"
exit_button.on_mouse_enter.edge = "on:top"
exit_button.on_mouse_enter.duration = 0.1
exit_button.on_mouse_leave.edge = "off:top"
exit_button.on_mouse_leave.duration = 0.1

View File

@ -29,4 +29,3 @@ image = { workspace = true }
rapier2d = { workspace = true } rapier2d = { workspace = true }
lazy_static = { workspace = true } lazy_static = { workspace = true }
log = { workspace = true } log = { workspace = true }
rhai = { workspace = true }

View File

@ -12,7 +12,6 @@ use anyhow::{bail, Context, Result};
use galactica_packer::{SpriteAtlas, SpriteAtlasImage}; use galactica_packer::{SpriteAtlas, SpriteAtlasImage};
use log::warn; use log::warn;
use std::{ use std::{
//cell::OnceCell,
collections::HashMap, collections::HashMap,
fs::File, fs::File,
io::Read, io::Read,
@ -34,6 +33,7 @@ mod syntax {
use crate::{ use crate::{
config, config,
part::{effect, faction, outfit, ship, sprite, system}, part::{effect, faction, outfit, ship, sprite, system},
ui,
}; };
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -45,6 +45,7 @@ mod syntax {
pub faction: Option<HashMap<String, faction::syntax::Faction>>, pub faction: Option<HashMap<String, faction::syntax::Faction>>,
pub effect: Option<HashMap<String, effect::syntax::Effect>>, pub effect: Option<HashMap<String, effect::syntax::Effect>>,
pub config: Option<config::syntax::Config>, pub config: Option<config::syntax::Config>,
pub ui: Option<ui::syntax::Ui>,
} }
fn merge_hashmap<K, V>( fn merge_hashmap<K, V>(
@ -82,6 +83,7 @@ mod syntax {
faction: None, faction: None,
effect: None, effect: None,
config: None, config: None,
ui: None,
} }
} }
@ -105,6 +107,15 @@ mod syntax {
} else { } else {
self.config = other.config; self.config = other.config;
} }
if self.ui.is_some() {
if other.ui.is_some() {
bail!("invalid content dir, multiple ui tables")
}
} else {
self.ui = other.ui;
}
return Ok(()); return Ok(());
} }
} }
@ -128,18 +139,22 @@ trait Build {
pub(crate) struct ContentBuildContext { pub(crate) struct ContentBuildContext {
/// Map effect names to handles /// Map effect names to handles
pub effect_index: HashMap<String, EffectHandle>, pub effect_index: HashMap<String, EffectHandle>,
/// Maps sprite handles to a map of section name -> section index
pub sprite_section_index: HashMap<SpriteHandle, HashMap<String, AnimSectionHandle>>,
} }
impl ContentBuildContext { impl ContentBuildContext {
fn new() -> Self { fn new() -> Self {
Self { Self {
effect_index: HashMap::new(), effect_index: HashMap::new(),
sprite_section_index: HashMap::new(),
} }
} }
} }
/// Represents static game content /// Represents static game content
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Content { pub struct Content {
/// Sprites /// Sprites
pub sprites: Vec<Sprite>, pub sprites: Vec<Sprite>,
@ -157,6 +172,7 @@ pub struct Content {
factions: Vec<Faction>, factions: Vec<Faction>,
effects: Vec<Effect>, effects: Vec<Effect>,
config: Config, config: Config,
ui: Option<Ui>,
} }
// Loading methods // Loading methods
@ -169,17 +185,10 @@ impl Content {
} }
/// Load content from a directory. /// Load content from a directory.
pub fn load_dir( pub fn load_dir(path: PathBuf, asset_root: PathBuf, atlas_index: PathBuf) -> Result<Self> {
content_root: PathBuf,
asset_root: PathBuf,
atlas_index: PathBuf,
) -> Result<Self> {
let mut root = syntax::Root::new(); let mut root = syntax::Root::new();
for e in WalkDir::new(&content_root) for e in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
.into_iter()
.filter_map(|e| e.ok())
{
if e.metadata().unwrap().is_file() { if e.metadata().unwrap().is_file() {
// TODO: better warnings // TODO: better warnings
match e.path().extension() { match e.path().extension() {
@ -216,7 +225,7 @@ impl Content {
let mut content = Self { let mut content = Self {
config: { config: {
if let Some(c) = root.config { if let Some(c) = root.config {
c.build(&asset_root, &content_root, &atlas) c.build(&asset_root, &atlas)
.with_context(|| "while parsing config table")? .with_context(|| "while parsing config table")?
} else { } else {
bail!("failed loading content: no config table specified") bail!("failed loading content: no config table specified")
@ -232,6 +241,7 @@ impl Content {
factions: Vec::new(), factions: Vec::new(),
effects: Vec::new(), effects: Vec::new(),
sprite_index: HashMap::new(), sprite_index: HashMap::new(),
ui: None,
}; };
// TODO: enforce sprite and image limits // TODO: enforce sprite and image limits
@ -253,6 +263,10 @@ impl Content {
)?; )?;
} }
if root.ui.is_some() {
part::ui::Ui::build(root.ui.take().unwrap(), &mut build_context, &mut content)?;
}
// Order below this line does not matter // Order below this line does not matter
if root.ship.is_some() { if root.ship.is_some() {
part::ship::Ship::build(root.ship.take().unwrap(), &mut build_context, &mut content)?; part::ship::Ship::build(root.ship.take().unwrap(), &mut build_context, &mut content)?;
@ -291,8 +305,11 @@ impl Content {
} }
/// Get a handle from a sprite name /// Get a handle from a sprite name
pub fn get_sprite_handle(&self, name: &str) -> Option<SpriteHandle> { pub fn get_sprite_handle(&self, name: &str) -> SpriteHandle {
self.sprite_index.get(name).map(|x| *x) return match self.sprite_index.get(name) {
Some(s) => *s,
None => unreachable!("get_sprite_handle was called with a bad name!"),
};
} }
/// Get a sprite from a handle /// Get a sprite from a handle
@ -356,23 +373,9 @@ impl Content {
pub fn get_config(&self) -> &Config { pub fn get_config(&self) -> &Config {
return &self.config; return &self.config;
} }
}
/* /// Get ui configuration
TODO: don't pass content around? pub fn get_ui(&self) -> &Ui {
static mut CONTENT: OnceCell<Content> = OnceCell::new(); return self.ui.as_ref().unwrap();
/// Initialize content::CONTENT with the given paths
pub fn init(content_dir: PathBuf, asset_dir: PathBuf, atlas_index: PathBuf) -> Result<()> {
let content = Content::load_dir(content_dir, asset_dir, atlas_index)?;
unsafe {
match CONTENT.set(content) {
Ok(()) => {}
Err(_) => {
bail!("cannot initialize content, already set.")
}
};
} }
return Ok(());
} }
*/

View File

@ -1,11 +1,8 @@
use std::{num::NonZeroU32, path::PathBuf}; use std::{num::NonZeroU32, path::PathBuf};
use rhai::AST;
pub(crate) mod syntax { pub(crate) mod syntax {
use anyhow::{bail, Context, Result}; use anyhow::{bail, Result};
use galactica_packer::SpriteAtlas; use galactica_packer::SpriteAtlas;
use rhai::Engine;
use serde::Deserialize; use serde::Deserialize;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -19,18 +16,11 @@ pub(crate) mod syntax {
pub starfield: Starfield, pub starfield: Starfield,
pub zoom_min: f32, pub zoom_min: f32,
pub zoom_max: f32, pub zoom_max: f32,
pub ui_scale: f32,
pub ui_landed_scene: PathBuf,
} }
impl Config { impl Config {
// TODO: clean up build trait // TODO: clean up build trait
pub fn build( pub fn build(self, asset_root: &Path, atlas: &SpriteAtlas) -> Result<super::Config> {
self,
asset_root: &Path,
content_root: &Path,
atlas: &SpriteAtlas,
) -> Result<super::Config> {
for i in &self.fonts.files { for i in &self.fonts.files {
if !asset_root.join(i).exists() { if !asset_root.join(i).exists() {
bail!("font file `{}` doesn't exist", i.display()); bail!("font file `{}` doesn't exist", i.display());
@ -56,12 +46,6 @@ pub(crate) mod syntax {
} }
}; };
let engine = Engine::new();
let ui_landed_scene = engine
.compile_file(content_root.join(self.ui_landed_scene))
.with_context(|| format!("while loading `landed` scene"))
.with_context(|| format!("while loading config"))?;
return Ok(super::Config { return Ok(super::Config {
sprite_root: asset_root.join(self.sprite_root), sprite_root: asset_root.join(self.sprite_root),
font_files: self font_files: self
@ -87,8 +71,6 @@ pub(crate) mod syntax {
starfield_instance_limit, starfield_instance_limit,
zoom_max: self.zoom_max, zoom_max: self.zoom_max,
zoom_min: self.zoom_min, zoom_min: self.zoom_min,
ui_scale: self.ui_scale,
ui_landed_scene,
}); });
} }
} }
@ -171,10 +153,4 @@ pub struct Config {
/// Maximum zoom,in game units /// Maximum zoom,in game units
pub zoom_max: f32, pub zoom_max: f32,
/// Ui scale factor
pub ui_scale: f32,
/// Ui landed scene
pub ui_landed_scene: AST,
} }

View File

@ -8,6 +8,7 @@ pub(crate) mod outfitspace;
pub(crate) mod ship; pub(crate) mod ship;
pub(crate) mod sprite; pub(crate) mod sprite;
pub(crate) mod system; pub(crate) mod system;
pub(crate) mod ui;
pub use config::Config; pub use config::Config;
pub use effect::*; pub use effect::*;
@ -20,3 +21,4 @@ pub use ship::{
}; };
pub use sprite::*; pub use sprite::*;
pub use system::{System, SystemObject}; pub use system::{System, SystemObject};
pub use ui::*;

View File

@ -3,8 +3,8 @@ use serde::Deserialize;
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{
handle::SpriteHandle, resolve_edge_as_edge, Content, ContentBuildContext, EffectHandle, handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitHandle, OutfitSpace,
OutfitHandle, OutfitSpace, SectionEdge, SectionEdge,
}; };
pub(crate) mod syntax { pub(crate) mod syntax {
@ -299,10 +299,9 @@ impl crate::Build for Outfit {
None None
} else { } else {
let x = x.unwrap(); let x = x.unwrap();
let mut e = resolve_edge_as_edge(&x.val, 0.0, |x| { let mut e = x
sprite.get_section_handle_by_name(x) .resolve_as_edge(sprite_handle, build_context, 0.0)
}) .with_context(|| format!("in outfit `{}`", outfit_name))?;
.with_context(|| format!("in outfit `{}`", outfit_name))?;
match e { match e {
// Inherit duration from transition sequence // Inherit duration from transition sequence
SectionEdge::Top { SectionEdge::Top {
@ -335,10 +334,9 @@ impl crate::Build for Outfit {
None None
} else { } else {
let x = x.unwrap(); let x = x.unwrap();
let mut e = resolve_edge_as_edge(&x.val, 0.0, |x| { let mut e = x
sprite.get_section_handle_by_name(x) .resolve_as_edge(sprite_handle, build_context, 0.0)
}) .with_context(|| format!("in outfit `{}`", outfit_name))?;
.with_context(|| format!("in outfit `{}`", outfit_name))?;
match e { match e {
// Inherit duration from transition sequence // Inherit duration from transition sequence
SectionEdge::Top { SectionEdge::Top {

View File

@ -16,7 +16,6 @@ pub(crate) mod syntax {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Ship { pub struct Ship {
pub sprite: String, pub sprite: String,
pub thumb: String,
pub size: f32, pub size: f32,
pub engines: Vec<Engine>, pub engines: Vec<Engine>,
pub guns: Vec<Gun>, pub guns: Vec<Gun>,
@ -97,9 +96,6 @@ pub struct Ship {
/// This ship's sprite /// This ship's sprite
pub sprite: SpriteHandle, pub sprite: SpriteHandle,
/// This ship's thumbnail
pub thumb: SpriteHandle,
/// The size of this ship. /// The size of this ship.
/// Measured as unrotated height, /// Measured as unrotated height,
/// in terms of game units. /// in terms of game units.
@ -267,7 +263,7 @@ impl crate::Build for Ship {
ct: &mut Content, ct: &mut Content,
) -> Result<()> { ) -> Result<()> {
for (ship_name, ship) in ship { for (ship_name, ship) in ship {
let sprite = match ct.sprite_index.get(&ship.sprite) { let handle = match ct.sprite_index.get(&ship.sprite) {
None => bail!( None => bail!(
"In ship `{}`: sprite `{}` doesn't exist", "In ship `{}`: sprite `{}` doesn't exist",
ship_name, ship_name,
@ -276,17 +272,8 @@ impl crate::Build for Ship {
Some(t) => *t, Some(t) => *t,
}; };
let thumb = match ct.sprite_index.get(&ship.thumb) {
None => bail!(
"In ship `{}`: thumbnail sprite `{}` doesn't exist",
ship_name,
ship.thumb
),
Some(t) => *t,
};
let size = ship.size; let size = ship.size;
let aspect = ct.get_sprite(sprite).aspect; let aspect = ct.get_sprite(handle).aspect;
let collapse = { let collapse = {
if let Some(c) = ship.collapse { if let Some(c) = ship.collapse {
@ -428,12 +415,11 @@ impl crate::Build for Ship {
}; };
ct.ships.push(Self { ct.ships.push(Self {
sprite,
thumb,
aspect, aspect,
collapse, collapse,
damage, damage,
name: ship_name, name: ship_name,
sprite: handle,
mass: ship.mass, mass: ship.mass,
space: OutfitSpace::from(ship.space), space: OutfitSpace::from(ship.space),
angular_drag: ship.angular_drag, angular_drag: ship.angular_drag,

View File

@ -5,8 +5,8 @@ use std::collections::HashMap;
use crate::{handle::SpriteHandle, Content, ContentBuildContext}; use crate::{handle::SpriteHandle, Content, ContentBuildContext};
pub(crate) mod syntax { pub(crate) mod syntax {
use crate::{AnimSectionHandle, Content}; use crate::{Content, ContentBuildContext, SpriteHandle};
use anyhow::{bail, Ok, Result}; use anyhow::{anyhow, bail, Context, Ok, Result};
use serde::Deserialize; use serde::Deserialize;
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
@ -63,26 +63,24 @@ pub(crate) mod syntax {
} }
impl SpriteSection { impl SpriteSection {
pub fn add_to<F>( pub fn add_to(
&self, &self,
ct: &mut Content, this_sprite: SpriteHandle,
get_handle: F, build_context: &mut ContentBuildContext,
) -> Result<((u32, u32), super::SpriteSection)> content: &mut Content,
where ) -> Result<((u32, u32), super::SpriteSection)> {
F: Fn(&str) -> Option<AnimSectionHandle>,
{
// Make sure all frames have the same size and add them // Make sure all frames have the same size and add them
// to the frame vector // to the frame vector
let mut dim = None; let mut dim = None;
let mut frames: Vec<u32> = Vec::new(); let mut frames: Vec<u32> = Vec::new();
for f in &self.frames { for f in &self.frames {
let idx = match ct.sprite_atlas.get_idx_by_path(f) { let idx = match content.sprite_atlas.get_idx_by_path(f) {
Some(s) => s, Some(s) => s,
None => { None => {
bail!("error: file `{}` isn't in the sprite atlas", f.display()); bail!("error: file `{}` isn't in the sprite atlas", f.display());
} }
}; };
let img = &ct.sprite_atlas.get_by_idx(idx); let img = &content.sprite_atlas.get_by_idx(idx);
match dim { match dim {
None => dim = Some(img.true_size), None => dim = Some(img.true_size),
@ -112,12 +110,12 @@ pub(crate) mod syntax {
} }
let edge_top = match &self.top { let edge_top = match &self.top {
Some(x) => super::resolve_edge_as_edge(&x.val, frame_duration, &get_handle)?, Some(x) => x.resolve_as_edge(this_sprite, build_context, frame_duration)?,
None => super::SectionEdge::Stop, None => super::SectionEdge::Stop,
}; };
let edge_bot = match &self.bot { let edge_bot = match &self.bot {
Some(x) => super::resolve_edge_as_edge(&x.val, frame_duration, &get_handle)?, Some(x) => x.resolve_as_edge(this_sprite, build_context, frame_duration)?,
None => super::SectionEdge::Stop, None => super::SectionEdge::Stop,
}; };
@ -139,6 +137,77 @@ pub(crate) mod syntax {
pub struct SectionEdge { pub struct SectionEdge {
pub val: String, pub val: String,
} }
impl SectionEdge {
pub fn resolve_as_start(
&self,
sprite: SpriteHandle,
build_context: &ContentBuildContext,
) -> Result<super::StartEdge> {
let e = self
.resolve_as_edge(sprite, build_context, 0.0)
.with_context(|| format!("while resolving start edge"))?;
match e {
super::SectionEdge::Bot { section, .. } => Ok(super::StartEdge::Bot { section }),
super::SectionEdge::Top { section, .. } => Ok(super::StartEdge::Top { section }),
_ => {
bail!("bad section start specification `{}`", self.val);
}
}
}
pub fn resolve_as_edge(
&self,
sprite: SpriteHandle,
build_context: &ContentBuildContext,
duration: f32,
) -> Result<super::SectionEdge> {
let all_sections = build_context.sprite_section_index.get(&sprite).unwrap();
if self.val == "hidden" {
return Ok(super::SectionEdge::Top {
section: crate::AnimSectionHandle::Hidden,
duration,
});
}
if self.val == "stop" {
return Ok(super::SectionEdge::Stop);
}
if self.val == "reverse" {
return Ok(super::SectionEdge::Reverse { duration });
}
if self.val == "repeat" {
return Ok(super::SectionEdge::Repeat { duration });
}
let (s, p) = match self.val.split_once(":") {
Some(x) => x,
None => {
bail!("bad section edge specification `{}`", self.val);
}
};
let section = match all_sections.get(s) {
Some(s) => *s,
None => {
return Err(anyhow!("bad section edge specification `{}`", self.val))
.with_context(|| format!("section `{}` doesn't exist", s));
}
};
match p {
"top" => Ok(super::SectionEdge::Top { section, duration }),
"bot" => Ok(super::SectionEdge::Bot { section, duration }),
_ => {
return Err(anyhow!("bad section edge specification `{}`", self.val))
.with_context(|| format!("invalid target `{}`", p));
}
}
}
}
} }
/// A handle for an animation section inside a sprite /// A handle for an animation section inside a sprite
@ -243,9 +312,6 @@ pub struct Sprite {
/// This sprite's animation sections /// This sprite's animation sections
sections: Vec<SpriteSection>, sections: Vec<SpriteSection>,
/// Allows us to get sprite sections by name
sections_by_name: HashMap<String, AnimSectionHandle>,
/// Aspect ratio of this sprite (width / height) /// Aspect ratio of this sprite (width / height)
pub aspect: f32, pub aspect: f32,
} }
@ -268,24 +334,6 @@ impl Sprite {
} }
} }
/// Get an animation section by name.
/// Returns None for invalid names
pub fn get_section_by_name(&self, name: &str) -> Option<&SpriteSection> {
match self.sections_by_name.get(name) {
None => return None,
Some(h) => Some(self.get_section(*h)),
}
}
/// Get an animation section's handle by name.
/// Returns None for invalid names
pub fn get_section_handle_by_name(&self, name: &str) -> Option<AnimSectionHandle> {
match self.sections_by_name.get(name) {
None => return None,
Some(h) => Some(*h),
}
}
/// Get the index of the texture of this sprite's first frame /// Get the index of the texture of this sprite's first frame
pub fn get_first_frame(&self) -> u32 { pub fn get_first_frame(&self) -> u32 {
match self.start_at { match self.start_at {
@ -326,83 +374,18 @@ pub struct SpriteSection {
pub edge_bot: SectionEdge, pub edge_bot: SectionEdge,
} }
/// Resolve an edge specification string as a StartEdge
pub fn resolve_edge_as_start<F>(s: &str, get_handle: F) -> Result<super::StartEdge>
where
F: Fn(&str) -> Option<AnimSectionHandle>,
{
let e = resolve_edge_as_edge(s, 0.0, get_handle)
.with_context(|| format!("while resolving start edge"))?;
match e {
super::SectionEdge::Bot { section, .. } => Ok(super::StartEdge::Bot { section }),
super::SectionEdge::Top { section, .. } => Ok(super::StartEdge::Top { section }),
_ => {
bail!("bad section start specification `{}`", s);
}
}
}
/// Resolve an edge specifiation string as a SectionEdge
pub fn resolve_edge_as_edge<F>(s: &str, duration: f32, get_handle: F) -> Result<super::SectionEdge>
where
F: Fn(&str) -> Option<AnimSectionHandle>,
{
if s == "hidden" {
return Ok(super::SectionEdge::Top {
section: crate::AnimSectionHandle::Hidden,
duration,
});
}
if s == "stop" {
return Ok(super::SectionEdge::Stop);
}
if s == "reverse" {
return Ok(super::SectionEdge::Reverse { duration });
}
if s == "repeat" {
return Ok(super::SectionEdge::Repeat { duration });
}
let (s, p) = match s.split_once(":") {
Some(x) => x,
None => {
bail!("bad section edge specification `{}`", s);
}
};
let section = match get_handle(s) {
Some(s) => s,
None => {
return Err(anyhow!("bad section edge specification `{}`", s))
.with_context(|| format!("section `{}` doesn't exist", s));
}
};
match p {
"top" => Ok(super::SectionEdge::Top { section, duration }),
"bot" => Ok(super::SectionEdge::Bot { section, duration }),
_ => {
return Err(anyhow!("bad section edge specification `{}`", s))
.with_context(|| format!("invalid target `{}`", p));
}
}
}
impl crate::Build for Sprite { impl crate::Build for Sprite {
type InputSyntaxType = HashMap<String, syntax::Sprite>; type InputSyntaxType = HashMap<String, syntax::Sprite>;
fn build( fn build(
sprites: Self::InputSyntaxType, sprites: Self::InputSyntaxType,
_build_context: &mut ContentBuildContext, build_context: &mut ContentBuildContext,
ct: &mut Content, content: &mut Content,
) -> Result<()> { ) -> Result<()> {
for (sprite_name, t) in sprites { for (sprite_name, t) in sprites {
match t { match t {
syntax::Sprite::Static(t) => { syntax::Sprite::Static(t) => {
let idx = match ct.sprite_atlas.get_idx_by_path(&t.file) { let idx = match content.sprite_atlas.get_idx_by_path(&t.file) {
Some(s) => s, Some(s) => s,
None => { None => {
return Err( return Err(
@ -416,16 +399,19 @@ impl crate::Build for Sprite {
}); });
} }
}; };
let img = &ct.sprite_atlas.get_by_idx(idx); let img = &content.sprite_atlas.get_by_idx(idx);
let aspect = img.w / img.h; let aspect = img.w / img.h;
let h = SpriteHandle { let h = SpriteHandle {
index: ct.sprites.len(), index: content.sprites.len(),
}; };
ct.sprite_index.insert(sprite_name.clone(), h); content.sprite_index.insert(sprite_name.clone(), h);
let mut smap = HashMap::new();
smap.insert("anim".to_string(), AnimSectionHandle::Idx(0));
build_context.sprite_section_index.insert(h, smap);
ct.sprites.push(Self { content.sprites.push(Self {
name: sprite_name, name: sprite_name,
start_at: StartEdge::Top { start_at: StartEdge::Top {
section: AnimSectionHandle::Idx(0), section: AnimSectionHandle::Idx(0),
@ -438,42 +424,41 @@ impl crate::Build for Sprite {
edge_top: SectionEdge::Stop, edge_top: SectionEdge::Stop,
edge_bot: SectionEdge::Stop, edge_bot: SectionEdge::Stop,
}], }],
sections_by_name: HashMap::new(),
handle: h, handle: h,
aspect, aspect,
}); });
} }
syntax::Sprite::OneSection(s) => { syntax::Sprite::OneSection(s) => {
let mut section_names: HashMap<String, _> = HashMap::new();
// Name the one section in this sprite "anim"
section_names.insert("anim".to_owned(), AnimSectionHandle::Idx(0));
let sprite_handle = SpriteHandle { let sprite_handle = SpriteHandle {
index: ct.sprites.len(), index: content.sprites.len(),
}; };
ct.sprite_index.insert(sprite_name.clone(), sprite_handle); content
.sprite_index
.insert(sprite_name.clone(), sprite_handle);
let mut smap = HashMap::new();
smap.insert("anim".to_string(), AnimSectionHandle::Idx(0));
build_context
.sprite_section_index
.insert(sprite_handle, smap);
let (dim, section) = s let (dim, section) = s
.add_to(ct, |s| { .add_to(sprite_handle, build_context, content)
if s == "anim" {
Some(AnimSectionHandle::Idx(0))
} else {
None
}
})
.with_context(|| format!("while parsing sprite `{}`", sprite_name))?; .with_context(|| format!("while parsing sprite `{}`", sprite_name))?;
let aspect = dim.0 as f32 / dim.1 as f32; let aspect = dim.0 as f32 / dim.1 as f32;
let mut sections = Vec::new(); let mut sections = Vec::new();
sections.push(section); sections.push(section);
ct.sprites.push(Self { content.sprites.push(Self {
name: sprite_name, name: sprite_name,
sections, sections,
start_at: StartEdge::Top { start_at: StartEdge::Top {
section: AnimSectionHandle::Idx(0), section: AnimSectionHandle::Idx(0),
}, },
sections_by_name: {
let mut h = HashMap::new();
h.insert("anim".to_string(), AnimSectionHandle::Idx(0));
h
},
handle: sprite_handle, handle: sprite_handle,
aspect, aspect,
}); });
@ -486,13 +471,19 @@ impl crate::Build for Sprite {
} }
let sprite_handle = SpriteHandle { let sprite_handle = SpriteHandle {
index: ct.sprites.len(), index: content.sprites.len(),
}; };
ct.sprite_index.insert(sprite_name.clone(), sprite_handle); content
.sprite_index
.insert(sprite_name.clone(), sprite_handle);
build_context
.sprite_section_index
.insert(sprite_handle, section_names.clone());
let start_at = let start_at = s
resolve_edge_as_start(&s.start_at.val, |x| section_names.get(x).copied()) .start_at
.with_context(|| format!("while loading sprite `{}`", sprite_name))?; .resolve_as_start(sprite_handle, build_context)
.with_context(|| format!("while loading sprite `{}`", sprite_name))?;
let mut sections = Vec::with_capacity(section_names.len()); let mut sections = Vec::with_capacity(section_names.len());
let mut dim = None; let mut dim = None;
@ -504,7 +495,7 @@ impl crate::Build for Sprite {
for (k, _) in names { for (k, _) in names {
let v = s.section.get(k).unwrap(); let v = s.section.get(k).unwrap();
let (d, s) = v let (d, s) = v
.add_to(ct, |x| section_names.get(x).copied()) .add_to(sprite_handle, build_context, content)
.with_context(|| format!("while parsing section `{}`", k)) .with_context(|| format!("while parsing section `{}`", k))
.with_context(|| format!("while parsing sprite `{}`", sprite_name))?; .with_context(|| format!("while parsing sprite `{}`", sprite_name))?;
@ -524,12 +515,11 @@ impl crate::Build for Sprite {
let dim = dim.unwrap(); let dim = dim.unwrap();
let aspect = dim.0 as f32 / dim.1 as f32; let aspect = dim.0 as f32 / dim.1 as f32;
ct.sprites.push(Self { content.sprites.push(Self {
name: sprite_name, name: sprite_name,
sections, sections,
start_at, start_at,
handle: sprite_handle, handle: sprite_handle,
sections_by_name: section_names,
aspect, aspect,
}); });
} }

View File

@ -148,7 +148,7 @@ fn resolve_coordinates(
if sum.is_empty() { if sum.is_empty() {
a.to_string() a.to_string()
} else { } else {
format!("{sum} -> {a}") sum + " -> " + a
} }
}) })
); );

View File

@ -0,0 +1,342 @@
use anyhow::{Context, Result};
use nalgebra::{Point2, Vector2};
use serde::Deserialize;
use crate::{handle::SpriteHandle, Content, ContentBuildContext, SectionEdge};
pub(crate) mod syntax {
use crate::{sprite::syntax::SectionEdge, Content, ContentBuildContext};
use anyhow::{bail, Context, Result};
use nalgebra::{Point2, Vector2};
use serde::Deserialize;
// Raw serde syntax structs.
// These are never seen by code outside this crate.
#[derive(Debug, Deserialize)]
pub struct Ui {
pub landed: UiLanded,
pub outfitter: UiOutfitter,
}
#[derive(Debug, Deserialize)]
pub struct UiLanded {
pub frame: UiSprite,
pub landscape: UiSprite,
pub button: UiSprite,
pub planet_name: UiText,
pub planet_desc: UiText,
}
#[derive(Debug, Deserialize)]
pub struct UiOutfitter {
pub se_box: UiSprite,
pub exit_button: UiSprite,
}
#[derive(Debug, Deserialize)]
pub struct UiRect {
pub pos: [f32; 2],
pub dim: [f32; 2],
pub anchor_self: super::UiPositionAnchor,
pub anchor_parent: super::UiPositionAnchor,
}
#[derive(Debug, Deserialize)]
pub struct EdgeSpec {
pub edge: SectionEdge,
pub duration: f32,
}
#[derive(Debug, Deserialize)]
pub struct UiText {
pub rect: UiRect,
pub font_size: f32,
pub line_height: f32,
pub align: super::UiTextAlign,
}
impl UiText {
pub fn build(
self,
_build_context: &ContentBuildContext,
_ct: &Content,
) -> Result<super::UiTextConfig> {
let rect = {
super::UiRect {
pos: Point2::new(self.rect.pos[0], self.rect.pos[1]),
dim: Vector2::new(self.rect.dim[0], self.rect.dim[1]),
anchor_self: self.rect.anchor_self,
anchor_parent: self.rect.anchor_parent,
}
};
return Ok(super::UiTextConfig {
rect,
font_size: self.font_size,
line_height: self.line_height,
align: self.align,
});
}
}
#[derive(Debug, Deserialize)]
pub struct UiSprite {
pub sprite: Option<String>,
pub rect: UiRect,
pub mask: Option<String>,
pub on_mouse_enter: Option<EdgeSpec>,
pub on_mouse_leave: Option<EdgeSpec>,
}
impl UiSprite {
pub fn build(
self,
build_context: &ContentBuildContext,
ct: &Content,
// If true, fail if self.sprite is missing.
// If false, fail if self.sprite exists.
// This is false for sprites that may change---for example, planet landscapes
should_have_sprite: bool,
) -> Result<super::UiSpriteConfig> {
let sprite = {
if should_have_sprite {
if self.sprite.is_none() {
bail!("no sprite given, but expected a value")
}
match ct.sprite_index.get(self.sprite.as_ref().unwrap()) {
None => bail!("ui sprite `{}` doesn't exist", self.sprite.unwrap()),
Some(t) => Some(*t),
}
} else {
if self.sprite.is_some() {
bail!("got a sprite, but didn't expect one")
}
None
}
};
let mask = if let Some(mask) = self.mask {
Some(match ct.sprite_index.get(&mask) {
None => bail!("mask `{}` doesn't exist", mask),
Some(t) => *t,
})
} else {
None
};
let on_mouse_enter = {
if let Some(x) = self.on_mouse_enter {
if sprite.is_none() {
bail!("got `on_mouse_enter` on a ui element with no fixed sprite")
}
Some(
x.edge
.resolve_as_edge(sprite.unwrap(), build_context, x.duration)
.with_context(|| format!("failed to resolve mouse enter edge"))?,
)
} else {
None
}
};
let on_mouse_leave = {
if let Some(x) = self.on_mouse_leave {
if sprite.is_none() {
bail!("got `on_mouse_leave` on a ui element with no fixed sprite")
}
Some(
x.edge
.resolve_as_edge(sprite.unwrap(), build_context, x.duration)
.with_context(|| format!("failed to resolve mouse leave edge"))?,
)
} else {
None
}
};
let rect = {
super::UiRect {
pos: Point2::new(self.rect.pos[0], self.rect.pos[1]),
dim: Vector2::new(self.rect.dim[0], self.rect.dim[1]),
anchor_self: self.rect.anchor_self,
anchor_parent: self.rect.anchor_parent,
}
};
return Ok(super::UiSpriteConfig {
sprite,
mask,
on_mouse_enter,
on_mouse_leave,
rect,
});
}
}
}
/// How to align text in a text box
#[derive(Debug, Deserialize, Clone, Copy)]
pub enum UiTextAlign {
/// Center-align
#[serde(rename = "center")]
Center,
/// Left-align
#[serde(rename = "left")]
Left,
}
/// How to position a UI sprite
#[derive(Debug, Deserialize, Clone, Copy)]
pub enum UiPositionAnchor {
/// Anchored at center
#[serde(rename = "center")]
Center,
/// Anchored at top-left
#[serde(rename = "northwest")]
NorthWest,
/// Anchored at top-right
#[serde(rename = "northeast")]
NorthEast,
/// Anchored at bottom-left
#[serde(rename = "southwest")]
SouthWest,
/// Anchored at bottom-right
#[serde(rename = "southeast")]
SouthEast,
}
/// UI Configuration
#[derive(Debug, Clone)]
pub struct Ui {
/// Landed interface frame
pub landed_frame: UiSpriteConfig,
/// Landed interface image
pub landed_landscape: UiSpriteConfig,
/// Test button
pub landed_button: UiSpriteConfig,
/// Landed planet name
pub landed_planet_name: UiTextConfig,
/// Landed planet description
pub landed_planet_desc: UiTextConfig,
/// Outfitter exit button
pub outfitter_exit_button: UiSpriteConfig,
/// Outfitter south-east box
pub outfitter_se_box: UiSpriteConfig,
}
/// A UI sprite's position
#[derive(Debug, Clone)]
pub struct UiRect {
/// The position of the center of this sprite, in logical pixels,
/// with 0, 0 at the center of the screen
pub pos: Point2<f32>,
/// This sprite's w and h, in logical pixels.
pub dim: Vector2<f32>,
/// The point on this sprite that pos is anchored to
pub anchor_self: UiPositionAnchor,
/// The point on the parent that pos is relative to
pub anchor_parent: UiPositionAnchor,
}
/// A single UI sprite instance
#[derive(Debug, Clone)]
pub struct UiSpriteConfig {
/// The sprite to show
pub sprite: Option<SpriteHandle>,
/// The mask to use
pub mask: Option<SpriteHandle>,
/// This sprite's position and size
pub rect: UiRect,
/// Animation edge to take when mouse enters this sprite
pub on_mouse_enter: Option<SectionEdge>,
/// Animation edge to take when mouse leaves this sprite
pub on_mouse_leave: Option<SectionEdge>,
}
/// A UI text box
#[derive(Debug, Clone)]
pub struct UiTextConfig {
/// Text box location and dimensions
pub rect: UiRect,
/// Text box font size
pub font_size: f32,
/// Text box line height
pub line_height: f32,
/// Text box alignment
pub align: UiTextAlign,
}
impl crate::Build for Ui {
type InputSyntaxType = syntax::Ui;
fn build(
ui: Self::InputSyntaxType,
build_context: &mut ContentBuildContext,
ct: &mut Content,
) -> Result<()> {
ct.ui = Some(Ui {
landed_frame: ui
.landed
.frame
.build(build_context, ct, true)
.with_context(|| format!("in ui config (landed_frame)"))?,
landed_landscape: ui
.landed
.landscape
.build(build_context, ct, false)
.with_context(|| format!("in ui config (landed_landscape)"))?,
landed_button: ui
.landed
.button
.build(build_context, ct, true)
.with_context(|| format!("in ui config (landed_button)"))?,
landed_planet_name: ui
.landed
.planet_name
.build(build_context, ct)
.with_context(|| format!("in ui config (landed_planet_name)"))?,
landed_planet_desc: ui
.landed
.planet_desc
.build(build_context, ct)
.with_context(|| format!("in ui config (landed_planet_desc)"))?,
outfitter_exit_button: ui
.outfitter
.exit_button
.build(build_context, ct, true)
.with_context(|| format!("in ui config (outfitter_exit_button)"))?,
outfitter_se_box: ui
.outfitter
.se_box
.build(build_context, ct, true)
.with_context(|| format!("in ui config (outfitter_se_box)"))?,
});
return Ok(());
}
}

View File

@ -247,7 +247,7 @@ impl SpriteAutomaton {
// Edge case: we're stopped and got a request to transition. // Edge case: we're stopped and got a request to transition.
// we should transition right away. // we should transition right away.
if let Some(e) = self.next_edge_override.take() { if let Some(e) = self.next_edge_override {
self.take_edge(ct, e); self.take_edge(ct, e);
} }

View File

@ -4,12 +4,11 @@ use galactica_system::data::ShipPersonality;
use galactica_system::phys::{PhysImage, PhysSim, PhysSimShipHandle, PhysStepResources}; use galactica_system::phys::{PhysImage, PhysSim, PhysSimShipHandle, PhysStepResources};
use galactica_util::timing::Timing; use galactica_util::timing::Timing;
use nalgebra::Point2; use nalgebra::Point2;
use std::rc::Rc;
use std::time::Instant; use std::time::Instant;
pub struct Game { pub struct Game {
// Core game data // Core game data
ct: Rc<Content>, ct: Content,
phys_sim: PhysSim, phys_sim: PhysSim,
timing: Timing, timing: Timing,
start_instant: Instant, start_instant: Instant,
@ -44,7 +43,7 @@ impl<'a> Game {
return player; return player;
} }
pub fn new(ct: Rc<Content>) -> Self { pub fn new(ct: Content) -> Self {
let mut phys_sim = PhysSim::new(&ct, SystemHandle { index: 0 }); let mut phys_sim = PhysSim::new(&ct, SystemHandle { index: 0 });
let a = phys_sim.add_ship( let a = phys_sim.add_ship(
@ -119,7 +118,7 @@ impl<'a> Game {
self.timing.start_frame(); self.timing.start_frame();
self.phys_sim.step( self.phys_sim.step(
PhysStepResources { PhysStepResources {
ct: self.ct.clone(), ct: &self.ct,
t: self.last_update.elapsed().as_secs_f32() * self.time_scale, t: self.last_update.elapsed().as_secs_f32() * self.time_scale,
timing: &mut self.timing, timing: &mut self.timing,
}, },

View File

@ -21,7 +21,6 @@ use nalgebra::Vector2;
use std::{ use std::{
fs, fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
rc::Rc,
time::Instant, time::Instant,
}; };
use winit::{ use winit::{
@ -113,17 +112,17 @@ fn try_main() -> Result<()> {
} }
// TODO: pretty error if missing (also in cli) // TODO: pretty error if missing (also in cli)
let content = Rc::new(Content::load_dir( let content = Content::load_dir(
PathBuf::from("./content"), PathBuf::from("./content"),
PathBuf::from("./assets"), PathBuf::from("./assets"),
atlas_index, atlas_index,
)?); )?;
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
let mut gpu = pollster::block_on(galactica_render::GPUState::new( let mut gpu = pollster::block_on(galactica_render::GPUState::new(
window, window,
content.clone(), &content,
RenderScenes::System, RenderScenes::System,
))?; ))?;
gpu.init(&content); gpu.init(&content);
@ -148,7 +147,7 @@ fn try_main() -> Result<()> {
camera_pos: player.camera.pos, camera_pos: player.camera.pos,
camera_zoom: player.camera.zoom, camera_zoom: player.camera.zoom,
current_time: game.get_current_time(), current_time: game.get_current_time(),
ct: content.clone(), ct: &content,
phys_img: &phys_img, phys_img: &phys_img,
player: &player, player: &player,
time_since_last_run: last_run.elapsed().as_secs_f32(), time_since_last_run: last_run.elapsed().as_secs_f32(),

View File

@ -174,7 +174,7 @@ impl PhysEffect {
return; return;
} }
self.anim.step(&res.ct, res.t); self.anim.step(res.ct, res.t);
self.lifetime -= res.t; self.lifetime -= res.t;
if self.lifetime <= 0.0 { if self.lifetime <= 0.0 {

View File

@ -67,7 +67,7 @@ impl PhysProjectile {
wrapper: &mut PhysWrapper, wrapper: &mut PhysWrapper,
) { ) {
self.lifetime -= res.t; self.lifetime -= res.t;
self.anim.step(&res.ct, res.t); self.anim.step(res.ct, res.t);
if self.lifetime <= 0.0 { if self.lifetime <= 0.0 {
self.destroy(res, new, wrapper, true); self.destroy(res, new, wrapper, true);
@ -102,7 +102,7 @@ impl PhysProjectile {
None => {} None => {}
Some(handle) => { Some(handle) => {
new.effects.push(PhysEffect::new( new.effects.push(PhysEffect::new(
&res.ct, res.ct,
wrapper, wrapper,
*handle, *handle,
*rb.translation(), *rb.translation(),

View File

@ -84,12 +84,12 @@ impl ShipCollapseSequence {
let pos: Vector2<f32> = if let Some(pos) = spawner.pos { let pos: Vector2<f32> = if let Some(pos) = spawner.pos {
Vector2::new(pos.x, pos.y) Vector2::new(pos.x, pos.y)
} else { } else {
self.random_in_ship(&res.ct, ship_content, &collider) self.random_in_ship(res.ct, ship_content, &collider)
}; };
let pos = ship_pos + (ship_rot * pos); let pos = ship_pos + (ship_rot * pos);
new.effects.push(PhysEffect::new( new.effects.push(PhysEffect::new(
&res.ct, res.ct,
wrapper, wrapper,
spawner.effect, spawner.effect,
pos, pos,
@ -124,13 +124,13 @@ impl ShipCollapseSequence {
let pos = if let Some(pos) = spawner.pos { let pos = if let Some(pos) = spawner.pos {
Vector2::new(pos.x, pos.y) Vector2::new(pos.x, pos.y)
} else { } else {
self.random_in_ship(&res.ct, ship_content, &collider) self.random_in_ship(res.ct, ship_content, &collider)
}; };
// Position, adjusted for ship rotation // Position, adjusted for ship rotation
let pos = ship_pos + (ship_rot * pos); let pos = ship_pos + (ship_rot * pos);
new.effects.push(PhysEffect::new( new.effects.push(PhysEffect::new(
&res.ct, res.ct,
wrapper, wrapper,
spawner.effect, spawner.effect,
pos, pos,

View File

@ -100,32 +100,32 @@ impl PhysShip {
} }
self.data.step(res.t); self.data.step(res.t);
self.anim.step(&res.ct, res.t); self.anim.step(res.ct, res.t);
for (_, e) in &mut self.engine_anim { for (_, e) in &mut self.engine_anim {
e.step(&res.ct, res.t); e.step(res.ct, res.t);
} }
// Flare animations // Flare animations
if !self.controls.thrust && self.last_controls.thrust { if !self.controls.thrust && self.last_controls.thrust {
let flare = self.get_flare(&res.ct); let flare = self.get_flare(res.ct);
if flare.is_some() { if flare.is_some() {
let flare_outfit = flare.unwrap(); let flare_outfit = flare.unwrap();
let flare = res.ct.get_outfit(flare_outfit); let flare = res.ct.get_outfit(flare_outfit);
if flare.engine_flare_on_stop.is_some() { if flare.engine_flare_on_stop.is_some() {
for (_, e) in &mut self.engine_anim { for (_, e) in &mut self.engine_anim {
e.jump_to(&res.ct, flare.engine_flare_on_stop.unwrap()); e.jump_to(res.ct, flare.engine_flare_on_stop.unwrap());
} }
} }
}; };
} else if self.controls.thrust && !self.last_controls.thrust { } else if self.controls.thrust && !self.last_controls.thrust {
let flare = self.get_flare(&res.ct); let flare = self.get_flare(res.ct);
if flare.is_some() { if flare.is_some() {
let flare_outfit = flare.unwrap(); let flare_outfit = flare.unwrap();
let flare = res.ct.get_outfit(flare_outfit); let flare = res.ct.get_outfit(flare_outfit);
if flare.engine_flare_on_start.is_some() { if flare.engine_flare_on_start.is_some() {
for (_, e) in &mut self.engine_anim { for (_, e) in &mut self.engine_anim {
e.jump_to(&res.ct, flare.engine_flare_on_start.unwrap()); e.jump_to(res.ct, flare.engine_flare_on_start.unwrap());
} }
} }
}; };
@ -232,7 +232,7 @@ impl PhysShip {
.collect(); .collect();
for (gun_point, outfit) in pairs { for (gun_point, outfit) in pairs {
if self.data.fire_gun(&res.ct, &gun_point) { if self.data.fire_gun(res.ct, &gun_point) {
let outfit = outfit.unwrap(); let outfit = outfit.unwrap();
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
@ -285,7 +285,7 @@ impl PhysShip {
let collider = wrapper.insert_collider(collider, rigid_body); let collider = wrapper.insert_collider(collider, rigid_body);
new.projectiles.push(PhysProjectile::new( new.projectiles.push(PhysProjectile::new(
&res.ct, res.ct,
outfit.projectile.clone(), outfit.projectile.clone(),
rigid_body, rigid_body,
self.data.get_faction(), self.data.get_faction(),
@ -456,7 +456,7 @@ impl PhysShip {
let pos = ship_pos + (Rotation2::new(ship_ang) * pos); let pos = ship_pos + (Rotation2::new(ship_ang) * pos);
new.effects.push(PhysEffect::new( new.effects.push(PhysEffect::new(
&res.ct, res.ct,
wrapper, wrapper,
e.effect, e.effect,
pos, pos,

View File

@ -131,7 +131,7 @@ impl PhysSim {
None => {} None => {}
Some(x) => { Some(x) => {
self.effects.push(PhysEffect::new( self.effects.push(PhysEffect::new(
&res.ct, res.ct,
&mut self.wrapper, &mut self.wrapper,
*x, *x,
pos, pos,

View File

@ -1,12 +1,10 @@
use std::rc::Rc;
use galactica_content::Content; use galactica_content::Content;
use galactica_util::timing::Timing; use galactica_util::timing::Timing;
/// External resources we need to compute time steps /// External resources we need to compute time steps
pub struct PhysStepResources<'a> { pub struct PhysStepResources<'a> {
/// Game content /// Game content
pub ct: Rc<Content>, pub ct: &'a Content,
/// Length of time step /// Length of time step
pub t: f32, pub t: f32,