oxygengine_script_web/
state.rs1use crate::interface::WebScriptInterface;
2use core::{
3 ecs::World,
4 state::{State, StateChange},
5};
6use js_sys::{Function, JsString, Reflect};
7use wasm_bindgen::{JsCast, JsValue};
8
9pub trait WebScriptStateScripted {
10 fn on_enter(&mut self, _world: &mut World) {}
11 fn on_exit(&mut self, _world: &mut World) {}
12 fn on_process(&mut self, _world: &mut World) -> Option<String> {
13 None
14 }
15}
16
17pub struct WebScriptBootState {
18 initial_state_name: String,
19}
20
21impl WebScriptBootState {
22 pub fn new(initial_state_name: &str) -> Self {
23 Self {
24 initial_state_name: initial_state_name.to_owned(),
25 }
26 }
27}
28
29impl State for WebScriptBootState {
30 fn on_process(&mut self, _: &mut World) -> StateChange {
31 if WebScriptInterface::is_ready() {
32 return if let Some(result) = WebScriptInterface::build_state(&self.initial_state_name) {
33 StateChange::Swap(Box::new(WebScriptState::new(result)))
34 } else if let Some(result) =
35 WebScriptInterface::build_state_scripted(&self.initial_state_name)
36 {
37 StateChange::Swap(Box::new(WebScriptStateWrapped::new(result)))
38 } else {
39 StateChange::Pop
40 };
41 }
42 StateChange::None
43 }
44}
45
46pub(crate) struct WebScriptStateWrapped {
47 inner: Box<dyn WebScriptStateScripted>,
48}
49
50impl WebScriptStateWrapped {
51 pub fn new(inner: Box<dyn WebScriptStateScripted>) -> Self {
52 Self { inner }
53 }
54}
55
56impl State for WebScriptStateWrapped {
57 fn on_enter(&mut self, world: &mut World) {
58 WebScriptInterface::set_world(world);
59 self.inner.on_enter(world);
60 }
61
62 fn on_process(&mut self, world: &mut World) -> StateChange {
63 WebScriptInterface::set_world(world);
64 WebScriptInterface::run_systems();
65 WebScriptInterface::maintain_entities(world);
66
67 if let Some(name) = self.inner.on_process(world) {
68 if let Some(result) = WebScriptInterface::build_state(&name) {
69 return StateChange::Swap(Box::new(WebScriptState::new(result)));
70 } else if let Some(result) = WebScriptInterface::build_state_scripted(&name) {
71 return StateChange::Swap(Box::new(WebScriptStateWrapped::new(result)));
72 }
73 }
74 StateChange::None
75 }
76
77 fn on_exit(&mut self, world: &mut World) {
78 self.inner.on_exit(world);
79 WebScriptInterface::unset_world();
80 }
81}
82
83pub(crate) struct WebScriptState {
84 context: JsValue,
85 on_enter: Option<Function>,
86 on_process: Option<Function>,
87 on_exit: Option<Function>,
88}
89
90impl WebScriptState {
91 pub fn new(context: JsValue) -> Self {
92 let on_enter = if let Ok(m) = Reflect::get(&context, &JsValue::from_str("onEnter")) {
93 m.dyn_ref::<Function>().cloned()
94 } else {
95 None
96 };
97 let on_process = if let Ok(m) = Reflect::get(&context, &JsValue::from_str("onProcess")) {
98 m.dyn_ref::<Function>().cloned()
99 } else {
100 None
101 };
102 let on_exit = if let Ok(m) = Reflect::get(&context, &JsValue::from_str("onExit")) {
103 m.dyn_ref::<Function>().cloned()
104 } else {
105 None
106 };
107 Self {
108 context,
109 on_enter,
110 on_process,
111 on_exit,
112 }
113 }
114}
115
116impl State for WebScriptState {
117 fn on_enter(&mut self, world: &mut World) {
118 WebScriptInterface::set_world(world);
119
120 if let Some(on_enter) = &self.on_enter {
121 drop(on_enter.call0(&self.context));
122 }
123 }
124
125 fn on_process(&mut self, world: &mut World) -> StateChange {
126 WebScriptInterface::set_world(world);
127 WebScriptInterface::run_systems();
128 WebScriptInterface::maintain_entities(world);
129
130 if let Some(on_process) = &self.on_process {
131 if let Ok(result) = on_process.call0(&self.context) {
132 if let Some(result) = result.dyn_ref::<JsString>() {
133 let name = String::from(result);
134 if let Some(result) = WebScriptInterface::build_state(&name) {
135 return StateChange::Swap(Box::new(WebScriptState::new(result)));
136 } else if let Some(result) = WebScriptInterface::build_state_scripted(&name) {
137 return StateChange::Swap(Box::new(WebScriptStateWrapped::new(result)));
138 }
139 }
140 }
141 }
142 StateChange::None
143 }
144
145 fn on_exit(&mut self, _: &mut World) {
146 if let Some(on_exit) = &self.on_exit {
147 drop(on_exit.call0(&self.context));
148 }
149
150 WebScriptInterface::unset_world();
151 }
152}