Added landed object image
parent
6b4c80ce0f
commit
eca7e7f5e6
|
@ -40,7 +40,7 @@ zoom_max = 2000.0
|
|||
# TODO: move to user config file
|
||||
ui_scale = 2
|
||||
|
||||
start_ui_scene = "landed"
|
||||
start_ui_scene = "flying"
|
||||
ui_scene.landed = "ui/landed.rhai"
|
||||
ui_scene.flying = "ui/flying.rhai"
|
||||
ui_scene.outfitter = "ui/outfitter.rhai"
|
||||
|
|
|
@ -28,6 +28,7 @@ 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.luna.sprite = "planet::luna"
|
||||
|
|
|
@ -6,6 +6,8 @@ fn config() {
|
|||
}
|
||||
|
||||
fn init(state) {
|
||||
let player = state.player_ship();
|
||||
|
||||
let frame = SpriteBuilder(
|
||||
"frame",
|
||||
"ui::planet",
|
||||
|
@ -18,7 +20,13 @@ fn init(state) {
|
|||
|
||||
let landscape = SpriteBuilder(
|
||||
"landscape",
|
||||
"ui::landscape::test",
|
||||
{
|
||||
if player.is_landed() {
|
||||
player.landed_on().image();
|
||||
} else {
|
||||
"";
|
||||
}
|
||||
},
|
||||
Rect(
|
||||
-180.0, 142.0, 274.0, 135.0,
|
||||
SpriteAnchor::NorthWest,
|
||||
|
@ -46,13 +54,33 @@ fn init(state) {
|
|||
SpriteAnchor::Center
|
||||
)
|
||||
);
|
||||
title.set_text("Title");
|
||||
if player.is_landed() {
|
||||
title.set_text(player.landed_on().name());
|
||||
} else {
|
||||
title.set_text("");
|
||||
}
|
||||
|
||||
let desc = TextBoxBuilder(
|
||||
"desc",
|
||||
7.5, 8.0, TextBoxFont::SansSerif, TextBoxJustify::Left,
|
||||
Rect(
|
||||
-178.92, -20.3, 343.0, 81.467,
|
||||
SpriteAnchor::NorthWest,
|
||||
SpriteAnchor::Center
|
||||
)
|
||||
);
|
||||
if player.is_landed() {
|
||||
desc.set_text(player.landed_on().desc());
|
||||
} else {
|
||||
desc.set_text("");
|
||||
}
|
||||
|
||||
return [
|
||||
button,
|
||||
landscape,
|
||||
frame,
|
||||
title,
|
||||
desc,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ fn init(state) {
|
|||
SpriteAnchor::SouthWest
|
||||
)
|
||||
);
|
||||
exit_text.set_text("Earth");
|
||||
exit_text.set_text("Exit");
|
||||
|
||||
let exit_button = SpriteBuilder(
|
||||
"exit_button",
|
||||
|
@ -70,7 +70,7 @@ fn init(state) {
|
|||
SpriteAnchor::NorthWest
|
||||
)
|
||||
);
|
||||
ship_name.set_text("Earth");
|
||||
ship_name.set_text("Hyperion");
|
||||
|
||||
let ship_type = TextBoxBuilder(
|
||||
"ship_type",
|
||||
|
@ -81,7 +81,11 @@ fn init(state) {
|
|||
SpriteAnchor::NorthWest
|
||||
)
|
||||
);
|
||||
ship_type.set_text("Earth");
|
||||
if state.player_ship().is_some() {
|
||||
ship_type.set_text(state.player_ship().name());
|
||||
} else {
|
||||
ship_type.set_text("ERR: SHIP IS NONE");
|
||||
}
|
||||
|
||||
let ship_stats = TextBoxBuilder(
|
||||
"ship_stats",
|
||||
|
@ -96,10 +100,6 @@ fn init(state) {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let outfit_bg = SpriteBuilder(
|
||||
"outfit_bg",
|
||||
"ui::outfitter-outfit-bg",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use galactica_util::to_radians;
|
||||
use nalgebra::{Point2, Point3};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
@ -31,6 +31,7 @@ pub(crate) mod syntax {
|
|||
pub landable: Option<bool>,
|
||||
pub name: Option<String>,
|
||||
pub desc: Option<String>,
|
||||
pub image: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -126,10 +127,13 @@ pub struct SystemObject {
|
|||
pub landable: bool,
|
||||
|
||||
/// The display name of this object
|
||||
pub name: String,
|
||||
pub name: Option<String>,
|
||||
|
||||
/// The description of this object
|
||||
pub desc: String,
|
||||
/// The description of this object (shown on landed ui)
|
||||
pub desc: Option<String>,
|
||||
|
||||
/// This object's image (shown on landed ui)
|
||||
pub image: Option<SpriteHandle>,
|
||||
}
|
||||
|
||||
/// Helper function for resolve_position, never called on its own.
|
||||
|
@ -220,8 +224,43 @@ impl crate::Build for System {
|
|||
Some(t) => *t,
|
||||
};
|
||||
|
||||
let image_handle = match &obj.image {
|
||||
Some(x) => match content.sprite_index.get(x) {
|
||||
None => bail!(
|
||||
"In system `{}`: sprite `{}` doesn't exist",
|
||||
system_name,
|
||||
obj.sprite
|
||||
),
|
||||
Some(t) => Some(*t),
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
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 `{}`", label))
|
||||
.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 `{}`", label))
|
||||
.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 `{}`", label))
|
||||
.with_context(|| format!("in system `{}`", system_name));
|
||||
}
|
||||
}
|
||||
|
||||
objects.push(SystemObject {
|
||||
sprite: sprite_handle,
|
||||
image: image_handle,
|
||||
pos: resolve_position(&system.object, &obj, cycle_detector)
|
||||
.with_context(|| format!("in object {:#?}", label))?,
|
||||
size: obj.size,
|
||||
|
@ -231,19 +270,13 @@ impl crate::Build for System {
|
|||
body_index: 0,
|
||||
},
|
||||
landable: obj.landable.unwrap_or(false),
|
||||
name: obj
|
||||
.name
|
||||
.as_ref()
|
||||
.map(|x| x.clone())
|
||||
.unwrap_or("".to_string()),
|
||||
|
||||
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("<br>", "\n"))
|
||||
.unwrap_or("".to_string()),
|
||||
.map(|x| x.replace("\n", " ").replace("<br>", "\n")),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -26,18 +26,24 @@ use rhai::{exported_module, Engine};
|
|||
|
||||
pub fn register_into_engine(engine: &mut Engine) {
|
||||
engine
|
||||
.build_type::<State>()
|
||||
.build_type::<ShipState>()
|
||||
// Helpers
|
||||
.build_type::<Rect>()
|
||||
.build_type::<Color>()
|
||||
.build_type::<SceneConfig>()
|
||||
// State
|
||||
.build_type::<State>()
|
||||
.build_type::<ShipState>()
|
||||
.build_type::<SystemObjectState>()
|
||||
// Builders
|
||||
.build_type::<RadialBuilder>()
|
||||
.build_type::<SpriteElement>()
|
||||
.build_type::<SceneConfig>()
|
||||
.build_type::<SpriteBuilder>()
|
||||
.build_type::<TextBoxBuilder>()
|
||||
// Events
|
||||
.build_type::<MouseClickEvent>()
|
||||
.build_type::<MouseHoverEvent>()
|
||||
.build_type::<PlayerShipStateEvent>()
|
||||
// Larger modules
|
||||
.register_type_with_name::<SpriteAnchor>("SpriteAnchor")
|
||||
.register_static_module("SpriteAnchor", exported_module!(spriteanchor_mod).into())
|
||||
.register_type_with_name::<TextBoxFont>("TextBoxFont")
|
||||
|
|
|
@ -26,7 +26,18 @@ impl SpriteElement {
|
|||
}
|
||||
|
||||
pub fn take_edge(&mut self, edge_name: &str, duration: f32) {
|
||||
let sprite_handle = self.sprite.as_ref().borrow().anim.get_sprite();
|
||||
if self.sprite.as_ref().borrow().anim.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let sprite_handle = self
|
||||
.sprite
|
||||
.as_ref()
|
||||
.borrow()
|
||||
.anim
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_sprite();
|
||||
let sprite = self.ct.get_sprite(sprite_handle);
|
||||
|
||||
let edge = resolve_edge_as_edge(edge_name, duration, |x| {
|
||||
|
@ -48,6 +59,8 @@ impl SpriteElement {
|
|||
.as_ref()
|
||||
.borrow_mut()
|
||||
.anim
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.jump_to(&self.ct, edge);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
use galactica_system::{data, phys::PhysSimShipHandle};
|
||||
use galactica_content::{Ship, SystemObject, SystemObjectHandle};
|
||||
use galactica_system::{
|
||||
data::{self, ShipData},
|
||||
phys::PhysSimShipHandle,
|
||||
};
|
||||
use log::error;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use std::rc::Rc;
|
||||
|
||||
|
@ -15,13 +20,41 @@ unsafe impl Send for ShipState {}
|
|||
unsafe impl Sync for ShipState {}
|
||||
|
||||
impl ShipState {
|
||||
pub fn get_state(&mut self) -> &data::ShipState {
|
||||
fn get_content(&mut self) -> &Ship {
|
||||
let ship = self
|
||||
.input
|
||||
.phys_img
|
||||
.get_ship(self.ship.as_ref().unwrap())
|
||||
.unwrap();
|
||||
ship.ship.get_data().get_state()
|
||||
let handle = ship.ship.get_data().get_content();
|
||||
self.input.ct.get_ship(handle)
|
||||
}
|
||||
|
||||
fn get_data(&mut self) -> &ShipData {
|
||||
let ship = self
|
||||
.input
|
||||
.phys_img
|
||||
.get_ship(self.ship.as_ref().unwrap())
|
||||
.unwrap();
|
||||
ship.ship.get_data()
|
||||
}
|
||||
|
||||
fn landed_on(&mut self) -> SystemObjectState {
|
||||
let input = self.input.clone();
|
||||
match self.get_data().get_state() {
|
||||
data::ShipState::Landed { target } => {
|
||||
return SystemObjectState {
|
||||
input,
|
||||
object: Some(*target),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return SystemObjectState {
|
||||
input,
|
||||
object: None,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,14 +63,84 @@ impl CustomType for ShipState {
|
|||
builder
|
||||
.with_name("ShipState")
|
||||
.with_fn("is_some", |s: &mut Self| s.ship.is_some())
|
||||
.with_fn("is_dead", |s: &mut Self| s.get_state().is_dead())
|
||||
.with_fn("is_landed", |s: &mut Self| s.get_state().is_landed())
|
||||
.with_fn("is_landing", |s: &mut Self| s.get_state().is_landing())
|
||||
.with_fn("is_flying", |s: &mut Self| s.get_state().is_flying())
|
||||
.with_fn("is_unlanding", |s: &mut Self| s.get_state().is_unlanding())
|
||||
.with_fn("is_dead", |s: &mut Self| s.get_data().get_state().is_dead())
|
||||
.with_fn("is_landed", |s: &mut Self| {
|
||||
s.get_data().get_state().is_landed()
|
||||
})
|
||||
.with_fn("is_landing", |s: &mut Self| {
|
||||
s.get_data().get_state().is_landing()
|
||||
})
|
||||
.with_fn("is_flying", |s: &mut Self| {
|
||||
s.get_data().get_state().is_flying()
|
||||
})
|
||||
.with_fn("is_unlanding", |s: &mut Self| {
|
||||
s.get_data().get_state().is_unlanding()
|
||||
})
|
||||
.with_fn("is_collapsing", |s: &mut Self| {
|
||||
s.get_state().is_collapsing()
|
||||
});
|
||||
s.get_data().get_state().is_collapsing()
|
||||
})
|
||||
.with_fn("name", |s: &mut Self| s.get_content().name.clone())
|
||||
.with_fn("thumbnail", |s: &mut Self| s.get_content().thumb)
|
||||
.with_fn("landed_on", |s: &mut Self| s.landed_on());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SystemObjectState {
|
||||
object: Option<SystemObjectHandle>,
|
||||
input: Rc<RenderInput>,
|
||||
}
|
||||
|
||||
// TODO: remove this
|
||||
unsafe impl Send for SystemObjectState {}
|
||||
unsafe impl Sync for SystemObjectState {}
|
||||
|
||||
impl SystemObjectState {
|
||||
fn get_content(&mut self) -> &SystemObject {
|
||||
self.input.ct.get_system_object(self.object.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for SystemObjectState {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder
|
||||
.with_name("SystemObjectState")
|
||||
//
|
||||
// Get landable name
|
||||
.with_fn("name", |s: &mut Self| {
|
||||
s.get_content()
|
||||
.name
|
||||
.as_ref()
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or_else(|| {
|
||||
error!("UI called `name()` on a system object which doesn't provide one");
|
||||
"".to_string()
|
||||
})
|
||||
})
|
||||
//
|
||||
// Get landable description
|
||||
.with_fn("desc", |s: &mut Self| {
|
||||
s.get_content()
|
||||
.desc
|
||||
.as_ref()
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or_else(|| {
|
||||
error!("UI called `name()` on a system object which doesn't provide one");
|
||||
"".to_string()
|
||||
})
|
||||
})
|
||||
//
|
||||
// Get landable landscape image
|
||||
.with_fn("image", |s: &mut Self| {
|
||||
let handle = s.get_content().image;
|
||||
if let Some(handle) = handle {
|
||||
s.input.ct.get_sprite(handle).name.clone()
|
||||
} else {
|
||||
error!("UI called `image()` on a system object which doesn't provide one");
|
||||
"".to_string()
|
||||
}
|
||||
})
|
||||
.with_fn("is_some", |s: &mut Self| s.object.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -196,8 +196,6 @@ impl UiManager {
|
|||
self.fps_indicator.step(&input, state);
|
||||
}
|
||||
|
||||
let mut keep_doing_actions = true;
|
||||
|
||||
// Send player state change events
|
||||
if {
|
||||
let player = input.player.ship;
|
||||
|
@ -236,7 +234,9 @@ impl UiManager {
|
|||
})?;
|
||||
|
||||
if let Some(action) = action.try_cast::<SceneAction>() {
|
||||
keep_doing_actions = self.handle_action(state, input.clone(), action)?;
|
||||
if self.handle_action(state, input.clone(), action)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,10 +248,6 @@ impl UiManager {
|
|||
sprite.step(&input, state);
|
||||
sprite.push_to_buffer(&input, state);
|
||||
|
||||
if !keep_doing_actions {
|
||||
continue;
|
||||
}
|
||||
|
||||
let event = sprite.check_events(&input, state);
|
||||
// we MUST drop here, since script calls mutate the sprite RefCell
|
||||
drop(sprite);
|
||||
|
@ -304,7 +300,9 @@ impl UiManager {
|
|||
})?;
|
||||
|
||||
if let Some(action) = action.try_cast::<SceneAction>() {
|
||||
keep_doing_actions = self.handle_action(state, input.clone(), action)?;
|
||||
if self.handle_action(state, input.clone(), action)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,7 +321,7 @@ impl UiManager {
|
|||
}
|
||||
|
||||
/// Do a SceneAction.
|
||||
/// Returns true if we should execute the action that is next, and false otherwise.
|
||||
/// If this returns true, all evaluation stops immediately.
|
||||
fn handle_action(
|
||||
&mut self,
|
||||
state: &mut RenderState,
|
||||
|
@ -331,10 +329,10 @@ impl UiManager {
|
|||
action: SceneAction,
|
||||
) -> Result<bool> {
|
||||
Ok(match action {
|
||||
SceneAction::None => true,
|
||||
SceneAction::None => false,
|
||||
SceneAction::GoTo(s) => {
|
||||
self.set_scene(state, input.clone(), s.clone())?;
|
||||
false
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
use galactica_content::{Content, SpriteAutomaton, SpriteHandle};
|
||||
use galactica_util::to_radians;
|
||||
use log::error;
|
||||
|
||||
use super::super::api::Rect;
|
||||
use crate::{ui::event::Event, vertexbuffer::types::UiInstance, RenderInput, RenderState};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Sprite {
|
||||
pub anim: SpriteAutomaton,
|
||||
pub rect: Rect,
|
||||
pub mask: Option<SpriteHandle>,
|
||||
/// If this is none, this was constructed with an invalid sprite
|
||||
pub anim: Option<SpriteAutomaton>,
|
||||
|
||||
rect: Rect,
|
||||
mask: Option<SpriteHandle>,
|
||||
pub name: String,
|
||||
|
||||
has_mouse: bool,
|
||||
|
@ -25,10 +28,24 @@ impl Sprite {
|
|||
mask: Option<String>,
|
||||
rect: Rect,
|
||||
) -> Self {
|
||||
let sprite_handle = ct.get_sprite_handle(&sprite).unwrap(); // TODO: errors
|
||||
let sprite_handle = ct.get_sprite_handle(&sprite);
|
||||
|
||||
if sprite_handle.is_none() {
|
||||
error!("UI constructed a sprite named `{name}` using an invalid source `{sprite}`")
|
||||
}
|
||||
|
||||
let mask = {
|
||||
if mask.is_some() {
|
||||
Some(ct.get_sprite_handle(&mask.unwrap()).unwrap())
|
||||
let m = ct.get_sprite_handle(mask.as_ref().unwrap());
|
||||
if m.is_none() {
|
||||
error!(
|
||||
"UI constructed a sprite named `{name}` using an invalid mask` {}`",
|
||||
mask.unwrap()
|
||||
);
|
||||
None
|
||||
} else {
|
||||
m
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -36,7 +53,7 @@ impl Sprite {
|
|||
|
||||
Self {
|
||||
name,
|
||||
anim: SpriteAutomaton::new(&ct, sprite_handle),
|
||||
anim: sprite_handle.map(|x| SpriteAutomaton::new(&ct, x)),
|
||||
rect,
|
||||
mask,
|
||||
has_mouse: false,
|
||||
|
@ -46,11 +63,15 @@ impl Sprite {
|
|||
}
|
||||
|
||||
pub fn push_to_buffer(&self, input: &RenderInput, state: &mut RenderState) {
|
||||
if self.anim.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let rect = self.rect.to_centered(state, input.ct.get_config().ui_scale);
|
||||
|
||||
// TODO: use both dimensions,
|
||||
// not just height
|
||||
let anim_state = self.anim.get_texture_idx();
|
||||
let anim_state = self.anim.as_ref().unwrap().get_texture_idx();
|
||||
|
||||
state.push_ui_buffer(UiInstance {
|
||||
position: rect.pos.into(),
|
||||
|
@ -117,6 +138,13 @@ impl Sprite {
|
|||
}
|
||||
|
||||
pub fn step(&mut self, input: &RenderInput, _state: &mut RenderState) {
|
||||
self.anim.step(&input.ct, input.time_since_last_run);
|
||||
if self.anim.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.anim
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.step(&input.ct, input.time_since_last_run);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue