Fixed radar

master
Mark 2024-02-04 22:41:26 -08:00
parent c1e831d60c
commit 39f52ddafc
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
17 changed files with 815 additions and 274 deletions

388
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -74,10 +74,4 @@ lazy_static = "1.4.0"
clap = { version = "4.4.18", features = ["derive"] }
log = "0.4.20"
log4rs = { version = "1.2.0", features = ["console_appender"] }
rhai = { version = "1.17.0", features = [
"f32_float",
"only_i32",
"metadata",
"no_custom_syntax",
"no_closure",
] }
rhai = { version = "1.17.1", features = ["f32_float", "no_custom_syntax"] }

View File

@ -32,6 +32,46 @@ fn init(state) {
Anchor::NorthEast
)
);
let radar_size = 150.0;
let radar_range = 4000.0;
add_sprite(
"radar",
"ui::radar",
Rect(
5.0, -5.0,
radar_size, radar_size,
Anchor::NorthWest,
Anchor::NorthWest
)
);
let init_pos = Rect(
(radar_size / 2.0 + 5),
(radar_size / -2.0 - 5),
3.5, 3.5,
Anchor::Center,
Anchor::NorthWest
);
add_sprite("radar.frame.ne", "ui::radarframe", init_pos);
add_sprite("radar.frame.se", "ui::radarframe", init_pos);
add_sprite("radar.frame.sw", "ui::radarframe", init_pos);
add_sprite("radar.frame.nw", "ui::radarframe", init_pos);
sprite_set_angle("radar.frame.se", 90.0);
sprite_set_angle("radar.frame.sw", 180.0);
sprite_set_angle("radar.frame.nw", 270.0);
sprite_set_color("radar.frame.ne", Color(0.3, 0.3, 0.3, 1.0));
sprite_set_color("radar.frame.se", Color(0.3, 0.3, 0.3, 1.0));
sprite_set_color("radar.frame.sw", Color(0.3, 0.3, 0.3, 1.0));
sprite_set_color("radar.frame.nw", Color(0.3, 0.3, 0.3, 1.0));
add_sprite("radar.arrow", "ui::centerarrow", init_pos);
}
fn event(state, event) {
@ -53,4 +93,209 @@ fn step(state) {
state.player_ship().get_hull()
/ state.player_ship().get_total_hull()
);
// TODO: share variables with init();
let radar_size = 150.0;
let radar_range = 4000.0;
let hide_range = 0.85;
let shrink_distance = 20.0;
let p_pos = state.player_ship().get_pos();
// Radar arrow
{
let q = Vector(0.0, 0.0) - state.player_ship().get_pos();
let m = q.norm();
let angle = Vector(1.0, 0.0).clockwise_angle(q);
let position = Vector(5.0 + (radar_size / 2.0), -5.0 - (radar_size / 2.0));
let rot = Vector(0.915 * (radar_size / 2.0), 0.0);
let pos = position + rot.rotate(angle);
sprite_set_rect("radar.arrow",
Rect(
pos.x(), pos.y(),
5.0, 5.0,
Anchor::Center,
Anchor::NorthWest
)
);
sprite_set_angle("radar.arrow", angle - 90.0);
sprite_set_color(
"radar.arrow",
Color(
1.0, 1.0, 1.0,
clamp((m - 200.0) /400.0, 0.0, 1.0)
)
)
}
// Ships
{
for s in state.ships() {
let uid = s.get_uid();
let sprite_name = `radar.ship.${uid}`;
if (
!s.is_some() ||
s.is_dead() ||
s.is_landed()
){
if sprite_exists(sprite_name) {
remove_sprite(sprite_name);
}
continue;
}
let color = Color(0.2, 0.2, 0.2, 1.0);
if s.is_flying() {
color = s.get_faction_color()
}
let size_init = s.get_size() * (1.0 / 50.0);
let pos = s.get_pos();
let d = (pos - p_pos) / radar_range;
let m = d.norm() + (size_init / (2.0 * radar_size));
if m < hide_range {
let size = (hide_range - m) * size_init * shrink_distance;
if size > size_init {
size = size_init;
}
if size < 1.0 {
if sprite_exists(sprite_name) {
remove_sprite(sprite_name);
}
continue;
}
let pos = Vector(radar_size / 2.0 + 5.0, radar_size / -2.0 - 5.0);
pos = pos + (d * radar_size / 2.0);
if !sprite_exists(sprite_name) {
add_sprite(
sprite_name,
"ui::shipblip",
Rect(
pos.x(), pos.y(),
size, size,
Anchor::Center,
Anchor::NorthWest
)
);
} else {
sprite_set_rect(
sprite_name,
Rect(
pos.x(), pos.y(),
size, size,
Anchor::Center,
Anchor::NorthWest
)
);
}
sprite_set_color(sprite_name, color);
}
}
}
// System objects
{
for o in state.objects() {
let sprite_name = `radar.object.${o.get_label()}`;
if !o.is_some() {
if sprite_exists(sprite_name) {
remove_sprite(sprite_name);
}
continue;
}
let size_init = (o.get_size() / o.get_pos_z()) / (radar_range * (1.0 / 300.0));
let pos = o.get_pos();
let d = (pos - p_pos) / radar_range;
let m = d.norm() + (size_init / (2.0 * radar_size));
if m < hide_range {
let size = (hide_range - m) * size_init * shrink_distance;
if size > size_init {
size = size_init;
}
if size < 1.0 {
// Don't draw tiny sprites, they flicker
if sprite_exists(sprite_name) {
remove_sprite(sprite_name);
}
continue;
}
let pos = (
Vector(radar_size / 2.0 + 5.0, radar_size / -2.0 - 5.0)
+ (d * radar_size / 2.0)
);
if !sprite_exists(sprite_name) {
add_sprite(
sprite_name,
"ui::planetblip",
Rect(
pos.x(), pos.y(),
size, size,
Anchor::Center,
Anchor::NorthWest
)
);
} else {
sprite_set_rect(
sprite_name,
Rect(
pos.x(), pos.y(),
size, size,
Anchor::Center,
Anchor::NorthWest
)
);
}
}
}
}
// Window frame
{
let dx = (((state.camera_zoom() / 2.0) * state.window_aspect()) / radar_range) * (radar_size / 2.0);
let dy = ((state.camera_zoom() / 2.0) / radar_range) * (radar_size / 2.0);
sprite_set_rect("radar.frame.ne",
Rect(
(radar_size / 2.0 + 5) - dx,
(radar_size / -2.0 - 5) + dy,
3.5, 3.5,
Anchor::Center,
Anchor::NorthWest
)
);
sprite_set_rect("radar.frame.se",
Rect(
(radar_size / 2.0 + 5) - dx,
(radar_size / -2.0 - 5) - dy,
3.5, 3.5,
Anchor::Center,
Anchor::NorthWest
)
);
sprite_set_rect("radar.frame.sw",
Rect(
(radar_size / 2.0 + 5) + dx,
(radar_size / -2.0 - 5) - dy,
3.5, 3.5,
Anchor::Center,
Anchor::NorthWest
)
);
sprite_set_rect("radar.frame.nw",
Rect(
(radar_size / 2.0 + 5) + dx,
(radar_size / -2.0 - 5) + dy,
3.5, 3.5,
Anchor::Center,
Anchor::NorthWest
)
);
}
}

View File

@ -6,7 +6,7 @@ pub(crate) mod syntax {
use anyhow::{bail, Context, Result};
use galactica_packer::SpriteAtlas;
use galactica_util::rhai_error_to_anyhow;
use rhai::Engine;
use rhai::{Engine, OptimizationLevel};
use serde::Deserialize;
use std::{
collections::HashMap,
@ -61,7 +61,9 @@ pub(crate) mod syntax {
}
};
let engine = Engine::new();
let mut engine = Engine::new_raw();
engine.set_optimization_level(OptimizationLevel::Full);
engine.set_max_expr_depths(0, 0);
let mut ui_scenes = HashMap::new();
for (n, p) in self.ui_scene {
ui_scenes.insert(

View File

@ -126,9 +126,12 @@ pub struct SystemObject {
/// If true, ships may land on this object
pub landable: bool,
/// The display name of this object
/// The pretty display name of this object
pub name: Option<String>,
/// The system-unique label of this object
pub label: String,
/// The description of this object (shown on landed ui)
pub desc: Option<String>,
@ -259,6 +262,7 @@ impl crate::Build for System {
}
objects.push(SystemObject {
label: label.clone(),
sprite: sprite_handle,
image: image_handle,
pos: resolve_position(&system.object, &obj, cycle_detector)

View File

@ -33,3 +33,4 @@ bytemuck = { workspace = true }
glyphon = { workspace = true }
log = { workspace = true }
rhai = { workspace = true }
rapier2d = { workspace = true }

View File

@ -3,7 +3,7 @@ use rhai::{CustomType, TypeBuilder};
#[derive(Debug, Clone)]
pub struct Color {
pub val: Vector4<f32>,
val: Vector4<f32>,
}
impl Color {

View File

@ -0,0 +1,28 @@
use rhai::{CustomType, TypeBuilder};
#[derive(Debug, Clone)]
pub struct SceneConfig {
pub show_phys: bool,
pub show_starfield: bool,
}
impl SceneConfig {
pub fn new() -> Self {
Self {
show_phys: false,
show_starfield: false,
}
}
}
impl CustomType for SceneConfig {
fn build(mut builder: TypeBuilder<Self>) {
builder
.with_name("SceneConfig")
.with_fn("SceneConfig", Self::new)
.with_fn("show_phys", |s: &mut Self, x: bool| s.show_phys = x)
.with_fn("show_starfield", |s: &mut Self, x: bool| {
s.show_starfield = x
});
}
}

View File

@ -3,12 +3,14 @@ mod color;
mod event;
mod rect;
mod state;
mod vector;
pub use anchor::*;
pub use color::*;
pub use event::*;
pub use rect::*;
pub use state::*;
pub use vector::*;
use rhai::{exported_module, Engine};
@ -17,6 +19,7 @@ pub fn register_into_engine(engine: &mut Engine) {
// Helpers
.build_type::<Rect>()
.build_type::<Color>()
.build_type::<UiVector>()
// State
.build_type::<State>()
.build_type::<ShipState>()

View File

@ -1,13 +1,16 @@
use galactica_content::{Ship, SystemObject, SystemObjectHandle};
use galactica_system::{
data::{self, ShipData},
phys::PhysSimShipHandle,
data::{self},
phys::{objects::PhysShip, PhysSimShipHandle},
};
use galactica_util::to_degrees;
use log::error;
use rhai::{CustomType, TypeBuilder};
use rapier2d::dynamics::RigidBody;
use rhai::{Array, CustomType, Dynamic, ImmutableString, TypeBuilder};
use std::sync::Arc;
use crate::RenderInput;
use super::{Color, UiVector};
use crate::{RenderInput, RenderState};
#[derive(Debug, Clone)]
pub struct ShipState {
@ -15,10 +18,6 @@ pub struct ShipState {
input: Arc<RenderInput>,
}
// TODO: remove this
unsafe impl Send for ShipState {}
unsafe impl Sync for ShipState {}
impl ShipState {
fn get_content(&mut self) -> &Ship {
let ship = self
@ -30,18 +29,27 @@ impl ShipState {
self.input.ct.get_ship(handle)
}
fn get_data(&mut self) -> &ShipData {
fn get_ship(&mut self) -> &PhysShip {
let ship = self
.input
.phys_img
.get_ship(self.ship.as_ref().unwrap())
.unwrap();
ship.ship.get_data()
&ship.ship
}
fn get_body(&mut self) -> &RigidBody {
let ship = self
.input
.phys_img
.get_ship(self.ship.as_ref().unwrap())
.unwrap();
&ship.rigidbody
}
fn landed_on(&mut self) -> SystemObjectState {
let input = self.input.clone();
match self.get_data().get_state() {
match self.get_ship().get_data().get_state() {
data::ShipState::Landed { target } => {
return SystemObjectState {
input,
@ -63,31 +71,48 @@ 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_data().get_state().is_dead())
.with_fn("is_dead", |s: &mut Self| {
s.get_ship().get_data().get_state().is_dead()
})
.with_fn("is_landed", |s: &mut Self| {
s.get_data().get_state().is_landed()
s.get_ship().get_data().get_state().is_landed()
})
.with_fn("is_landing", |s: &mut Self| {
s.get_data().get_state().is_landing()
s.get_ship().get_data().get_state().is_landing()
})
.with_fn("is_flying", |s: &mut Self| {
s.get_data().get_state().is_flying()
s.get_ship().get_data().get_state().is_flying()
})
.with_fn("is_unlanding", |s: &mut Self| {
s.get_data().get_state().is_unlanding()
s.get_ship().get_data().get_state().is_unlanding()
})
.with_fn("is_collapsing", |s: &mut Self| {
s.get_data().get_state().is_collapsing()
s.get_ship().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())
.with_fn("get_shields", |s: &mut Self| s.get_data().get_shields())
.with_fn("get_shields", |s: &mut Self| {
s.get_ship().get_data().get_shields()
})
.with_fn("get_total_shields", |s: &mut Self| {
s.get_data().get_outfits().get_total_shields()
s.get_ship().get_data().get_outfits().get_total_shields()
})
.with_fn("get_total_hull", |s: &mut Self| s.get_content().hull)
.with_fn("get_hull", |s: &mut Self| s.get_data().get_hull());
.with_fn("get_hull", |s: &mut Self| {
s.get_ship().get_data().get_hull()
})
.with_fn("get_size", |s: &mut Self| s.get_content().size)
.with_fn("get_uid", |s: &mut Self| format!("{}", s.get_ship().uid))
.with_fn("get_pos", |s: &mut Self| {
let t = s.get_body().translation();
UiVector::new(t.x, t.y)
})
.with_fn("get_faction_color", |s: &mut Self| {
let h = s.get_ship().get_data().get_faction();
let c = s.input.ct.get_faction(h).color;
Color::new(c[0], c[1], c[2], 1.0)
});
}
}
@ -97,10 +122,6 @@ pub struct SystemObjectState {
input: Arc<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())
@ -146,13 +167,30 @@ impl CustomType for SystemObjectState {
"".to_string()
}
})
.with_fn("is_some", |s: &mut Self| s.object.is_some());
.with_fn("is_some", |s: &mut Self| s.object.is_some())
.with_fn("==", |a: &mut Self, b: Self| a.object == b.object)
.with_fn("get_size", |s: &mut Self| s.get_content().size)
.with_fn("get_label", |s: &mut Self| {
ImmutableString::from(&s.get_content().label)
})
.with_fn("get_angle", |s: &mut Self| {
to_degrees(s.get_content().angle)
})
.with_fn("get_pos", |s: &mut Self| {
let t = s.get_content().pos;
UiVector::new(t.x, t.y)
})
.with_fn("get_pos_z", |s: &mut Self| {
let t = s.get_content().pos;
t.z
});
}
}
#[derive(Debug, Clone)]
pub struct State {
input: Arc<RenderInput>,
window_aspect: f32,
}
// TODO: remove this
@ -160,8 +198,11 @@ unsafe impl Send for State {}
unsafe impl Sync for State {}
impl State {
pub fn new(input: Arc<RenderInput>) -> Self {
Self { input }
pub fn new(state: &RenderState, input: Arc<RenderInput>) -> Self {
Self {
input,
window_aspect: state.window_aspect,
}
}
pub fn player_ship(&mut self) -> ShipState {
@ -170,12 +211,39 @@ impl State {
ship: self.input.player.ship.map(|x| PhysSimShipHandle(x)),
}
}
pub fn ships(&mut self) -> Array {
let mut a = Array::new();
for s in self.input.phys_img.iter_ships() {
a.push(Dynamic::from(ShipState {
input: self.input.clone(),
ship: Some(PhysSimShipHandle(s.ship.collider)),
}));
}
return a;
}
pub fn objects(&mut self) -> Array {
let mut a = Array::new();
let s = self.input.current_system;
for o in &self.input.ct.get_system(s).objects {
a.push(Dynamic::from(SystemObjectState {
input: self.input.clone(),
object: Some(o.handle),
}));
}
return a;
}
}
impl CustomType for State {
fn build(mut builder: TypeBuilder<Self>) {
builder
.with_name("State")
.with_fn("player_ship", Self::player_ship);
.with_fn("player_ship", Self::player_ship)
.with_fn("ships", Self::ships)
.with_fn("objects", Self::objects)
.with_fn("window_aspect", |s: &mut Self| s.window_aspect)
.with_fn("camera_zoom", |s: &mut Self| s.input.camera_zoom);
}
}

View File

@ -0,0 +1,47 @@
use galactica_util::{clockwise_angle, to_degrees, to_radians};
use nalgebra::{Rotation2, Vector2};
use rhai::{CustomType, TypeBuilder};
#[derive(Debug, Clone)]
pub struct UiVector {
val: Vector2<f32>,
}
impl UiVector {
pub fn new(x: f32, y: f32) -> Self {
Self {
val: Vector2::new(x, y),
}
}
pub fn rotate(&mut self, angle: f32) -> Self {
return UiVector {
val: Rotation2::new(to_radians(angle)) * self.val,
};
}
pub fn clockwise_angle(&mut self, other: UiVector) -> f32 {
return to_degrees(clockwise_angle(&self.val, &other.val));
}
}
impl CustomType for UiVector {
fn build(mut builder: TypeBuilder<Self>) {
builder
.with_name("Vector")
.with_fn("Vector", Self::new)
.with_fn("rotate", Self::rotate)
.with_fn("clockwise_angle", Self::clockwise_angle)
.with_fn("+", |a: UiVector, b: UiVector| UiVector {
val: a.val + b.val,
})
.with_fn("-", |a: UiVector, b: UiVector| UiVector {
val: a.val - b.val,
})
.with_fn("/", |s: &mut Self, x: f32| UiVector { val: s.val / x })
.with_fn("*", |s: &mut Self, x: f32| UiVector { val: s.val * x })
.with_fn("x", |s: &mut Self| s.val.x)
.with_fn("y", |s: &mut Self| s.val.y)
.with_fn("norm", |s: &mut Self| s.val.magnitude());
}
}

View File

@ -4,7 +4,10 @@ use galactica_system::phys::PhysSimShipHandle;
use galactica_util::rhai_error_to_anyhow;
use glyphon::{cosmic_text::Align, FamilyOwned, FontSystem, Style, Weight};
use log::{debug, error};
use rhai::{Dynamic, Engine, ImmutableString, Scope};
use rhai::{
packages::{BasicArrayPackage, BasicStringPackage, LogicPackage, MoreStringPackage, Package},
Dynamic, Engine, ImmutableString, Scope,
};
use std::{cell::RefCell, num::NonZeroU32, rc::Rc, sync::Arc};
use super::{
@ -31,6 +34,18 @@ impl UiScriptExecutor {
let elements = Rc::new(RefCell::new(UiState::new(ct.clone(), state)));
let mut engine = Engine::new_raw();
// Required for array iteration
// We may need to add more packages here later.
engine.register_global_module(BasicArrayPackage::new().as_shared_module());
engine.register_global_module(LogicPackage::new().as_shared_module());
engine.register_global_module(BasicStringPackage::new().as_shared_module());
engine.register_global_module(MoreStringPackage::new().as_shared_module());
engine.set_max_expr_depths(0, 0);
// Enables custom operators
engine.set_fast_operators(false);
api::register_into_engine(&mut engine);
Self::register_api(
ct.clone(),
@ -53,7 +68,7 @@ impl UiScriptExecutor {
}
/// Change the current scene
pub fn set_scene(&mut self, input: Arc<RenderInput>) -> Result<()> {
pub fn set_scene(&mut self, state: &RenderState, input: Arc<RenderInput>) -> Result<()> {
let current_scene = (*self.state).borrow().get_scene().clone();
if self.last_scene == current_scene {
return Ok(());
@ -85,7 +100,7 @@ impl UiScriptExecutor {
.get(current_scene.as_ref().unwrap().as_str())
.unwrap(),
"init",
(State::new(input.clone()),),
(State::new(state, input.clone()),),
),
)
.with_context(|| format!("while running `init()`"))
@ -104,7 +119,7 @@ impl UiScriptExecutor {
.borrow_mut()
.set_scene(ImmutableString::from(&ct.get_config().start_ui_scene));
}
self.set_scene(input.clone())?;
self.set_scene(state, input.clone())?;
let current_scene = (*self.state).borrow().get_scene().clone();
(*self.state).borrow_mut().step(state, input.clone());
@ -121,7 +136,7 @@ impl UiScriptExecutor {
&mut self.scope,
ast,
"step",
(State::new(input.clone()),),
(State::new(state, input.clone()),),
))
.with_context(|| format!("while calling `step()`"))
.with_context(|| format!("in ui scene `{}`", current_scene.as_ref().unwrap()))?;
@ -154,7 +169,7 @@ impl UiScriptExecutor {
.get(current_scene.as_ref().unwrap().as_str())
.unwrap(),
"event",
(State::new(input.clone()), PlayerShipStateEvent {}),
(State::new(state, input.clone()), PlayerShipStateEvent {}),
),
)
.with_context(|| format!("while handling player state change event"))
@ -214,7 +229,7 @@ impl UiScriptExecutor {
.get(current_scene.as_ref().unwrap().as_str())
.unwrap(),
"event",
(State::new(input.clone()), event_arg.clone()),
(State::new(state, input.clone()), event_arg.clone()),
),
)
.with_context(|| format!("while handling event `{:?}`", event_arg))
@ -222,6 +237,7 @@ impl UiScriptExecutor {
}
}
self.scope.rewind(0);
return Ok(());
}
}
@ -253,6 +269,12 @@ impl UiScriptExecutor {
let mut ui_state = c.borrow_mut();
ui_state.config.show_starfield = b;
});
engine.register_fn("print", move |d: Dynamic| {
debug!("{:?}", d);
});
engine.register_fn("clamp", move |x: f32, l: f32, h: f32| x.clamp(l, h));
}
// Sprites
@ -263,7 +285,6 @@ impl UiScriptExecutor {
"add_sprite",
move |name: ImmutableString, sprite: ImmutableString, rect: Rect| {
let mut ui_state = c.borrow_mut();
let len = ui_state.len();
let sprite_handle = ct.get_sprite_handle(sprite.as_str());
if sprite_handle.is_none() {
@ -271,16 +292,35 @@ impl UiScriptExecutor {
return;
}
ui_state.names.insert(name.clone(), len);
ui_state.elements.push(UiElement::Sprite(Sprite::new(
&ct,
if ui_state.elements.contains_key(&name) {
error!("tried to make a sprite using an existing name `{name}`");
return;
}
ui_state.names.push(name.clone());
ui_state.elements.insert(
name.clone(),
sprite_handle.unwrap(),
rect,
)));
UiElement::Sprite(Sprite::new(
&ct,
name.clone(),
sprite_handle.unwrap(),
rect,
)),
);
},
);
let c = s.clone();
engine.register_fn("remove_sprite", move |name: ImmutableString| {
let mut ui_state = c.borrow_mut();
if ui_state.elements.contains_key(&name) {
ui_state.elements.remove(&name).unwrap();
ui_state.names.retain(|x| *x != name);
} else {
error!("called `remove_sprite` on an invalid name `{name}`")
}
});
let c = s.clone();
let ct = ct_src.clone();
engine.register_fn(
@ -299,7 +339,7 @@ impl UiScriptExecutor {
}
_ => {
error!("called `set_sprite_mask` on an invalid name `{name}`")
error!("called `sprite_set_mask` on an invalid name `{name}`")
}
}
},
@ -339,6 +379,51 @@ impl UiScriptExecutor {
}
},
);
let c = s.clone();
engine.register_fn("sprite_set_angle", move |name: ImmutableString, x: f32| {
let mut ui_state = c.borrow_mut();
match ui_state.get_mut_by_name(&name) {
Some(UiElement::Sprite(s)) => s.set_angle(x),
_ => {
error!("called `sprite_set_angle` on an invalid name `{name}`")
}
}
});
let c = s.clone();
engine.register_fn("sprite_set_rect", move |name: ImmutableString, x: Rect| {
let mut ui_state = c.borrow_mut();
match ui_state.get_mut_by_name(&name) {
Some(UiElement::Sprite(s)) => s.set_rect(x),
_ => {
error!("called `sprite_set_rect` on an invalid name `{name}`")
}
}
});
let c = s.clone();
engine.register_fn(
"sprite_set_color",
move |name: ImmutableString, x: Color| {
let mut ui_state = c.borrow_mut();
match ui_state.get_mut_by_name(&name) {
Some(UiElement::Sprite(s)) => s.set_color(x),
_ => {
error!("called `sprite_set_color` on an invalid name `{name}`")
}
}
},
);
let c = s.clone();
engine.register_fn("sprite_exists", move |name: ImmutableString| {
let mut ui_state = c.borrow_mut();
match ui_state.get_mut_by_name(&name) {
Some(UiElement::Sprite(_)) => true,
_ => false,
}
});
}
// Textboxes
@ -354,17 +439,24 @@ impl UiScriptExecutor {
rect: Rect,
color: Color| {
let mut ui_state = c.borrow_mut();
let len = ui_state.len();
ui_state.names.insert(name.clone(), len);
ui_state.elements.push(UiElement::Text(TextBox::new(
&mut font.borrow_mut(),
if ui_state.elements.contains_key(&name) {
error!("tried to make a textbox using an existing name `{name}`");
return;
}
ui_state.names.push(name.clone());
ui_state.elements.insert(
name.clone(),
font_size,
line_height,
rect,
color,
)));
UiElement::Text(TextBox::new(
&mut font.borrow_mut(),
name.clone(),
font_size,
line_height,
rect,
color,
)),
);
},
);
@ -536,16 +628,23 @@ impl UiScriptExecutor {
// TODO: fix ugly spaces
move |name: ImmutableString, stroke: f32, color: Color, rect: Rect| {
let mut ui_state = c.borrow_mut();
let len = ui_state.len();
ui_state.names.insert(name.clone(), len);
ui_state.elements.push(UiElement::RadialBar(RadialBar::new(
if ui_state.elements.contains_key(&name) {
error!("tried to make a radialbar using an existing name `{name}`");
return;
}
ui_state.names.push(name.clone());
ui_state.elements.insert(
name.clone(),
stroke,
color,
rect,
1.0,
)));
UiElement::RadialBar(RadialBar::new(
name.clone(),
stroke,
color,
rect,
1.0,
)),
);
},
);

View File

@ -23,8 +23,8 @@ pub(crate) struct UiConfig {
}
pub(crate) struct UiState {
pub names: HashMap<ImmutableString, usize>,
pub elements: Vec<UiElement>,
pub elements: HashMap<ImmutableString, UiElement>,
pub names: Vec<ImmutableString>,
pub ct: Arc<Content>,
current_scene: Option<ImmutableString>,
@ -42,8 +42,8 @@ impl UiState {
pub fn new(ct: Arc<Content>, state: &mut RenderState) -> Self {
Self {
ct,
names: HashMap::new(),
elements: Vec::new(),
elements: HashMap::new(),
names: Vec::new(),
current_scene: None,
show_timings: true,
@ -79,15 +79,15 @@ impl UiState {
*/
pub fn get_mut_by_idx(&mut self, idx: usize) -> Option<&mut UiElement> {
self.elements.get_mut(idx)
let name = self.names.get(idx);
if name.is_none() {
return None;
}
self.elements.get_mut(name.unwrap())
}
pub fn get_mut_by_name(&mut self, name: &ImmutableString) -> Option<&mut UiElement> {
let idx = self.names.get(name);
if idx.is_none() {
return None;
}
self.get_mut_by_idx(*idx.unwrap())
self.elements.get_mut(name)
}
pub fn get_scene(&self) -> &Option<ImmutableString> {
@ -124,7 +124,7 @@ impl<'a> UiState {
v.push(self.fps_indicator.get_textarea(input, window))
}
for t in self.elements.iter() {
for t in self.elements.values() {
match &t {
UiElement::Text(x) => v.push(x.get_textarea(input, window)),
_ => {}

View File

@ -1,5 +1,9 @@
use super::super::api::Rect;
use crate::{ui::event::Event, vertexbuffer::types::UiInstance, RenderInput, RenderState};
use crate::{
ui::{api::Color, event::Event},
vertexbuffer::types::UiInstance,
RenderInput, RenderState,
};
use galactica_content::{Content, SpriteAutomaton, SpriteHandle};
use galactica_util::to_radians;
use rhai::ImmutableString;
@ -9,8 +13,12 @@ pub struct Sprite {
pub anim: SpriteAutomaton,
pub name: ImmutableString,
/// Sprite angle, in degrees
angle: f32,
rect: Rect,
mask: Option<SpriteHandle>,
color: Color,
/// If true, ignore mouse events until click is released
waiting_for_release: bool,
@ -24,6 +32,8 @@ impl Sprite {
name,
anim: SpriteAutomaton::new(&ct, sprite),
rect,
angle: 0.0,
color: Color::new(1.0, 1.0, 1.0, 1.0),
mask: None,
has_mouse: false,
has_click: false,
@ -35,6 +45,18 @@ impl Sprite {
self.mask = mask;
}
pub fn set_angle(&mut self, angle: f32) {
self.angle = angle;
}
pub fn set_rect(&mut self, rect: Rect) {
self.rect = rect;
}
pub fn set_color(&mut self, color: Color) {
self.color = color;
}
pub fn push_to_buffer(&self, input: &RenderInput, state: &mut RenderState) {
let rect = self
.rect
@ -46,9 +68,9 @@ impl Sprite {
state.push_ui_buffer(UiInstance {
position: rect.pos.into(),
angle: to_radians(90.0),
angle: to_radians(90.0 + self.angle),
dim: rect.dim.into(),
color: [1.0, 1.0, 1.0, 1.0],
color: self.color.as_array(),
texture_index: anim_state.texture_index(),
texture_fade: anim_state.fade,
mask_index: self

View File

@ -6,7 +6,15 @@ mod physsim;
mod physwrapper;
mod stepresources;
use std::sync::atomic::{AtomicU64, Ordering};
pub use physimage::*;
pub use physsim::{PhysSim, PhysSimShipHandle};
pub use physwrapper::PhysWrapper;
pub use stepresources::*;
/// A unique id given to each physics object
static PHYS_UID: AtomicU64 = AtomicU64::new(0);
fn get_phys_id() -> u64 {
PHYS_UID.fetch_add(1, Ordering::Relaxed)
}

View File

@ -14,6 +14,7 @@ use super::{autopilot, collapse::ShipCollapseSequence, controller::ShipControlle
use crate::{
data::{ShipAutoPilot, ShipData, ShipPersonality, ShipState},
phys::{
get_phys_id,
objects::{PhysEffect, PhysProjectile},
physsim::NewObjects,
PhysImage, PhysSimShipHandle, PhysStepResources, PhysWrapper,
@ -23,6 +24,9 @@ use crate::{
/// A ship instance in the physics system
#[derive(Debug, Clone)]
pub struct PhysShip {
/// This ship's unique id
pub uid: u64,
/// This ship's physics handle
pub rigid_body: RigidBodyHandle,
@ -71,6 +75,7 @@ impl PhysShip {
) -> Self {
let ship_ct = ct.get_ship(handle);
PhysShip {
uid: get_phys_id(),
anim: SpriteAutomaton::new(ct, ship_ct.sprite),
rigid_body,
collider,

View File

@ -13,6 +13,11 @@ pub fn to_radians(degrees: f32) -> f32 {
return (degrees / 360.0) * std::f32::consts::TAU;
}
/// Convert an angle in radians to degrees
pub fn to_degrees(radians: f32) -> f32 {
return (radians / std::f32::consts::TAU) * 360.0;
}
/// Compute the clockwise angle between two vectors
/// Returns a value in [-pi, pi]
pub fn clockwise_angle(a: &Vector2<f32>, b: &Vector2<f32>) -> f32 {