1use crate::err::show_mangrove_error;
7use crate::script::{compile, MangroveError};
8use crate::util::{get_impl_func, get_impl_func_optional};
9use crate::{ErrorResource, ScriptMessage, SourceMapResource};
10use limnus_clock::Clock;
11use limnus_gamepad::{Axis, AxisValueType, Button, ButtonValueType, GamePadId, GamepadMessage};
12use monotonic_time_rs::{Millis, MillisDuration};
13use std::cell::RefCell;
14use std::rc::Rc;
15use swamp::prelude::{App, Fp, LoRe, LoReM, LocalResource, Msg, Plugin, Re, ReM, UpdatePhase};
16use swamp_script::prelude::*;
17
18pub fn logic_tick(
21 mut script: LoReM<ScriptLogic>,
22 _source_map: Re<SourceMapResource>,
23 clock: LoRe<Clock>,
24 error: Re<ErrorResource>,
25) {
26 if clock.clock.now() < script.next_time {
27 return;
28 }
29
30 script.next_time += MillisDuration::from_millis(16);
31
32 if error.has_errors {
34 return;
35 }
36 script.tick(None).expect("script.tick() crashed");
37}
38
39pub fn input_tick(mut script: LoReM<ScriptLogic>, gamepad_messages: Msg<GamepadMessage>) {
40 for gamepad_message in gamepad_messages.iter_current() {
41 script.gamepad(gamepad_message);
42 }
43}
44
45#[derive(Debug)]
46pub struct ScriptLogicContext {}
47
48#[derive(LocalResource, Debug)]
49pub struct ScriptLogic {
50 logic_value_ref: ValueRef,
51 logic_fn: ResolvedInternalFunctionDefinitionRef,
52 gamepad_axis_changed_fn: Option<ResolvedInternalFunctionDefinitionRef>,
53 gamepad_button_changed_fn: Option<ResolvedInternalFunctionDefinitionRef>,
54 external_functions: ExternalFunctions<ScriptLogicContext>,
55 constants: Constants,
56 script_context: ScriptLogicContext,
57 resolved_program: ResolvedProgram,
58 input_module: ResolvedModuleRef,
59 next_time: Millis,
60}
61
62impl ScriptLogic {
63 pub fn new(
64 logic_value_ref: ValueRef,
65 logic_fn: ResolvedInternalFunctionDefinitionRef,
66 gamepad_axis_changed_fn: Option<ResolvedInternalFunctionDefinitionRef>,
67 gamepad_button_changed_fn: Option<ResolvedInternalFunctionDefinitionRef>,
68 external_functions: ExternalFunctions<ScriptLogicContext>,
69 constants: Constants,
70 resolved_program: ResolvedProgram,
71 input_module: ResolvedModuleRef,
72 now: Millis,
73 ) -> Self {
74 Self {
75 logic_value_ref,
76 logic_fn,
77 gamepad_axis_changed_fn,
78 gamepad_button_changed_fn,
79 external_functions,
80 script_context: ScriptLogicContext {},
81 constants,
82 resolved_program,
83 input_module,
84 next_time: now,
85 }
86 }
87
88 #[must_use]
89 pub fn immutable_logic_value(&self) -> Value {
90 self.logic_value_ref.borrow().clone()
91 }
92
93 #[must_use]
96 pub fn main_module(&self) -> ResolvedModuleRef {
97 let root_module_path = &["logic".to_string()].to_vec();
98
99 self.resolved_program
100 .modules
101 .get(root_module_path)
102 .expect("logic module should exist in logic")
103 }
104
105 pub fn tick(
108 &mut self,
109 debug_source_map: Option<&dyn SourceMapLookup>,
110 ) -> Result<(), ExecuteError> {
111 let variable_value_ref =
112 VariableValue::Reference(ValueReference(self.logic_value_ref.clone()));
113 let _ = util_execute_function(
114 &self.external_functions,
115 &self.constants,
116 &self.logic_fn,
117 &[variable_value_ref],
118 &mut self.script_context,
119 debug_source_map,
120 )?;
121
122 Ok(())
123 }
124
125 fn execute(
126 &mut self,
127 fn_def: &ResolvedInternalFunctionDefinitionRef,
128 arguments: &[Value],
129 ) -> Result<(), ExecuteError> {
130 let mut complete_arguments = Vec::new();
131 complete_arguments.push(VariableValue::Reference(ValueReference(
132 self.logic_value_ref.clone(),
133 ))); for arg in arguments {
135 complete_arguments.push(VariableValue::Value(arg.clone()));
136 }
137
138 let _ = util_execute_function(
139 &self.external_functions,
140 &self.constants,
141 fn_def,
142 &complete_arguments,
143 &mut self.script_context,
144 None,
145 )?;
146
147 Ok(())
148 }
149
150 pub fn gamepad(&mut self, msg: &GamepadMessage) {
151 match msg {
152 GamepadMessage::Connected(_, _) => {}
153 GamepadMessage::Disconnected(_) => {}
154 GamepadMessage::Activated(_) => {}
155 GamepadMessage::ButtonChanged(gamepad_id, button, value) => {
156 self.button_changed(*gamepad_id, *button, *value);
157 }
158 GamepadMessage::AxisChanged(gamepad_id, axis, value) => {
159 self.axis_changed(*gamepad_id, *axis, *value);
160 }
161 }
162 }
163
164 fn axis_changed(&mut self, gamepad_id: GamePadId, axis: Axis, value: AxisValueType) {
165 let script_axis_value = {
166 let input_module_ref = self.input_module.borrow();
167 let axis_str = match axis {
168 Axis::LeftStickX => "LeftStickX",
169 Axis::LeftStickY => "LeftStickY",
170 Axis::RightStickX => "RightStickX",
171 Axis::RightStickY => "RightStickY",
172 };
173
174 let variant = input_module_ref
175 .namespace
176 .borrow()
177 .get_enum_variant_type_str("Axis", axis_str)
178 .expect("axis")
179 .clone();
180
181 Value::EnumVariantSimple(variant)
182 };
183
184 if let Some(found_fn) = &self.gamepad_axis_changed_fn {
185 let gamepad_id_value = Value::Int(gamepad_id as i32);
186 let axis_value = Value::Float(Fp::from(value));
187
188 let fn_ref = found_fn.clone();
189
190 self.execute(&fn_ref, &[gamepad_id_value, script_axis_value, axis_value])
191 .expect("gamepad_axis_changed");
192 }
193 }
194
195 fn button_changed(&mut self, gamepad_id: GamePadId, button: Button, value: ButtonValueType) {
196 let script_button_value = {
197 let input_module_ref = self.input_module.borrow();
198 let button_str = match button {
199 Button::South => "South",
200 Button::East => "East",
201 Button::North => "North",
202 Button::West => "West",
203 Button::LeftTrigger => "LeftTrigger",
204 Button::LeftTrigger2 => "LeftTrigger2",
205 Button::RightTrigger => "RightTrigger",
206 Button::RightTrigger2 => "RightTrigger2",
207 Button::Select => "Select",
208 Button::Start => "Start",
209 Button::Mode => "Mode",
210 Button::LeftThumb => "LeftThumb",
211 Button::RightThumb => "RightThumb",
212 Button::DPadUp => "DPadUp",
213 Button::DPadDown => "DPadDown",
214 Button::DPadLeft => "DPadLeft",
215 Button::DPadRight => "DPadRight",
216 };
217
218 let variant = input_module_ref
219 .namespace
220 .borrow()
221 .get_enum_variant_type_str("Button", button_str)
222 .expect("button name failed")
223 .clone();
224
225 Value::EnumVariantSimple(variant)
226 };
227
228 if let Some(found_fn) = &self.gamepad_button_changed_fn {
229 let gamepad_id_value = Value::Int(
230 i32::try_from(gamepad_id).expect("could not convert gamepad button to i32"),
231 );
232 let button_value = Value::Float(Fp::from(value));
233
234 let fn_ref = found_fn.clone();
235
236 self.execute(
237 &fn_ref,
238 &[gamepad_id_value, script_button_value, button_value],
239 )
240 .expect("gamepad_button_changed");
241 }
242 }
243}
244
245pub fn input_module(
248 resolve_state: &mut ResolvedProgramState,
249) -> Result<(ResolvedModule, ResolvedEnumTypeRef, ResolvedEnumTypeRef), ResolveError> {
250 let module_path = ["input".to_string()];
251 let module = ResolvedModule::new(&module_path);
252
253 let axis_enum_type_ref = {
254 let axis_enum_type_id = resolve_state.allocate_number(); let parent = ResolvedEnumType {
257 name: ResolvedLocalTypeIdentifier(ResolvedNode {
258 span: Span::default(),
259 }),
260 assigned_name: "Axis".to_string(),
261 module_path: Vec::from(module_path.clone()),
262 number: axis_enum_type_id,
263 };
264 let parent_ref = Rc::new(parent);
265
266 let axis_enum_type_ref = module.namespace.borrow_mut().add_enum_type(parent_ref)?;
267
268 let variant_names = ["LeftStickX", "LeftStickY", "RightStickX", "RightStickY"];
269 for variant_name in variant_names {
270 let variant_type_id = resolve_state.allocate_number(); let variant = ResolvedEnumVariantType::new(
272 axis_enum_type_ref.clone(),
273 ResolvedLocalTypeIdentifier(ResolvedNode {
274 span: Span::default(),
275 }),
276 variant_name,
277 ResolvedEnumVariantContainerType::Nothing,
278 variant_type_id,
279 );
280 module
281 .namespace
282 .borrow_mut()
283 .add_enum_variant("Axis", variant_name, variant)?;
284 }
285 axis_enum_type_ref
286 };
287
288 let button_enum_type_ref = {
289 let button_enum_type_id = resolve_state.allocate_number(); let parent = ResolvedEnumType {
292 name: ResolvedLocalTypeIdentifier(ResolvedNode {
293 span: Span::default(),
294 }),
295 assigned_name: "Button".to_string(),
296 module_path: Vec::from(module_path),
297 number: button_enum_type_id,
298 };
299 let parent_ref = Rc::new(parent);
300 let button_enum_type_ref = module.namespace.borrow_mut().add_enum_type(parent_ref)?;
301
302 let button_names = [
303 "South",
304 "East",
305 "North",
306 "West",
307 "LeftTrigger",
308 "LeftTrigger2",
309 "RightTrigger",
310 "RightTrigger2",
311 "Select",
312 "Start",
313 "Mode",
314 "LeftThumb",
315 "RightThumb",
316 "DPadUp",
317 "DPadDown",
318 "DPadLeft",
319 "DPadRight",
320 ];
321
322 for button_variant_name in button_names {
323 let variant_type_id = resolve_state.allocate_number(); let variant = ResolvedEnumVariantType {
325 owner: button_enum_type_ref.clone(),
326 data: ResolvedEnumVariantContainerType::Nothing,
327 name: ResolvedLocalTypeIdentifier(ResolvedNode {
328 span: Span::default(),
329 }),
330 assigned_name: button_variant_name.to_string(),
331 number: variant_type_id,
332 };
333
334 module.namespace.borrow_mut().add_enum_variant(
335 "Button",
336 button_variant_name,
337 variant,
338 )?;
339 }
340 button_enum_type_ref
341 };
342
343 Ok((module, axis_enum_type_ref, button_enum_type_ref))
344}
345
346pub fn boot(source_map: &mut SourceMapResource, now: Millis) -> Result<ScriptLogic, MangroveError> {
351 let mut resolved_program = ResolvedProgram::new();
352 let mut external_functions = ExternalFunctions::<ScriptLogicContext>::new();
353
354 let (input_module, _axis_enum_type, _button_enum_type) =
355 input_module(&mut resolved_program.state)?;
356 let input_module_ref = Rc::new(RefCell::new(input_module));
357 resolved_program.modules.add(input_module_ref.clone());
358
359 let base_path = source_map.base_path().to_path_buf();
360
361 compile(
362 base_path.as_path(),
363 "logic.swamp",
364 &["logic".to_string()],
365 &mut resolved_program,
366 &mut external_functions,
367 &mut source_map.wrapper.source_map,
368 "logic",
369 )?;
370
371 let root_module_path = &["logic".to_string()];
372 let main_fn = {
373 let main_module = resolved_program
374 .modules
375 .get(root_module_path)
376 .expect("could not find main module");
377
378 let binding = main_module.borrow();
379 let function_ref = binding
380 .namespace
381 .borrow()
382 .get_internal_function("main")
383 .expect("No main function")
384 .clone();
385
386 Rc::clone(&function_ref) };
388
389 let mut script_context = ScriptLogicContext {};
390 resolved_program.modules.finalize()?;
391 let mut constants = Constants::new();
392 eval_constants(
393 &external_functions,
394 &mut constants,
395 &resolved_program.modules,
396 &mut script_context,
397 )?;
398
399 let logic_value = util_execute_function(
400 &external_functions,
401 &constants,
402 &main_fn,
403 &[],
404 &mut script_context,
405 None,
406 )?;
407
408 let Value::Struct(logic_struct_type_ref, _) = &logic_value else {
409 return Err(MangroveError::Other("needs to be logic struct".to_string()));
410 };
411
412 let logic_fn = get_impl_func(logic_struct_type_ref, "tick");
413 let gamepad_axis_changed_fn =
414 get_impl_func_optional(logic_struct_type_ref, "gamepad_axis_changed");
415 let gamepad_button_changed_fn =
416 get_impl_func_optional(logic_struct_type_ref, "gamepad_button_changed");
417
418 let logic_value_ref = Rc::new(RefCell::new(logic_value));
420
421 Ok(ScriptLogic::new(
422 logic_value_ref,
423 logic_fn,
424 gamepad_axis_changed_fn,
425 gamepad_button_changed_fn,
426 external_functions,
427 constants,
428 resolved_program,
429 input_module_ref,
430 now,
431 ))
432}
433
434pub fn detect_reload_tick(
435 script_messages: Msg<ScriptMessage>,
436 mut script_logic: LoReM<ScriptLogic>,
437 mut source_map_resource: ReM<SourceMapResource>,
438 clock: LoRe<Clock>,
439 mut err: ReM<ErrorResource>,
440) {
441 for msg in script_messages.iter_previous() {
442 match msg {
443 ScriptMessage::Reload => match boot(&mut source_map_resource, clock.clock.now()) {
444 Ok(new_logic) => *script_logic = new_logic,
445 Err(mangrove_error) => {
446 show_mangrove_error(&mangrove_error, &source_map_resource.wrapper.source_map);
447 err.has_errors = true;
448
449 }
452 },
453 }
454 }
455}
456
457pub struct ScriptLogicPlugin;
458
459impl Plugin for ScriptLogicPlugin {
460 fn build(&self, app: &mut App) {
461 app.add_system(UpdatePhase::Update, detect_reload_tick);
462 app.add_system(UpdatePhase::Update, logic_tick);
463 app.add_system(UpdatePhase::Update, input_tick);
464
465 let now = app.local_resources().fetch::<Clock>().clock.now();
466
467 app.insert_local_resource(ScriptLogic {
470 logic_value_ref: Rc::new(RefCell::new(Value::default())),
471 logic_fn: Rc::new(ResolvedInternalFunctionDefinition {
472 body: ResolvedExpression::Break(ResolvedNode::default()),
473 name: ResolvedLocalIdentifier(ResolvedNode::default()),
474 signature: FunctionTypeSignature {
475 first_parameter_is_self: false,
476 parameters: vec![],
477 return_type: Box::from(ResolvedType::Any),
478 },
479 constants: vec![],
480 }),
481 gamepad_axis_changed_fn: None,
482 gamepad_button_changed_fn: None,
483 external_functions: ExternalFunctions::new(),
484 constants: Constants { values: vec![] },
485 script_context: ScriptLogicContext {},
486 resolved_program: ResolvedProgram {
487 state: ResolvedProgramState {
488 array_types: vec![],
489 number: 0,
490 external_function_number: 0,
491 },
492 modules: ResolvedModules::default(),
493 },
494 input_module: Rc::new(RefCell::new(ResolvedModule {
495 definitions: vec![],
496 expression: None,
497 namespace: Rc::new(RefCell::new(ResolvedModuleNamespace::new(&[]))),
498 })),
499 next_time: now,
500 });
501 }
502}