Fixed radar
parent
c1e831d60c
commit
39f52ddafc
File diff suppressed because it is too large
Load Diff
|
@ -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"] }
|
||||
|
|
|
@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -33,3 +33,4 @@ bytemuck = { workspace = true }
|
|||
glyphon = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rhai = { workspace = true }
|
||||
rapier2d = { workspace = true }
|
||||
|
|
|
@ -3,7 +3,7 @@ use rhai::{CustomType, TypeBuilder};
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Color {
|
||||
pub val: Vector4<f32>,
|
||||
val: Vector4<f32>,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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>()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
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(),
|
||||
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(
|
||||
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(),
|
||||
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(),
|
||||
UiElement::RadialBar(RadialBar::new(
|
||||
name.clone(),
|
||||
stroke,
|
||||
color,
|
||||
rect,
|
||||
1.0,
|
||||
)));
|
||||
)),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -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)),
|
||||
_ => {}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue