From d232a1f55d49f92e03882a8b8b763946ae633224 Mon Sep 17 00:00:00 2001 From: Dove6 <24943032+Dove6@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:30:24 +0200 Subject: [PATCH] Implement mouse input handling Handle parametrized events for Animation, Behavior, BoolVar, Button and Timer classes. Fix how image position is interpreted. Throw in case of duplicated properties while parsing objects. Implement some more missing methods. --- pixlib/src/graphics_plugin.rs | 6 +- pixlib/src/inputs_plugin.rs | 22 +- pixlib/src/main.rs | 13 +- pixlib_parser/src/classes/animation.rs | 276 ++++++++++------ pixlib_parser/src/classes/behavior.rs | 4 +- pixlib_parser/src/classes/bool.rs | 95 +++--- pixlib_parser/src/classes/button.rs | 36 ++- pixlib_parser/src/classes/image.rs | 49 ++- pixlib_parser/src/classes/mod.rs | 2 +- pixlib_parser/src/classes/mouse.rs | 425 ++++++++++++++++++------- pixlib_parser/src/classes/object.rs | 37 ++- pixlib_parser/src/classes/sound.rs | 32 ++ pixlib_parser/src/classes/timer.rs | 25 +- pixlib_parser/src/runner/events.rs | 7 +- pixlib_parser/src/runner/mod.rs | 162 ++++++++-- pixlib_parser/src/runner/tests.rs | 20 +- 16 files changed, 861 insertions(+), 350 deletions(-) diff --git a/pixlib/src/graphics_plugin.rs b/pixlib/src/graphics_plugin.rs index 896b18e..096b54d 100644 --- a/pixlib/src/graphics_plugin.rs +++ b/pixlib/src/graphics_plugin.rs @@ -291,10 +291,10 @@ pub fn update_images( sprite.flip_x = false; sprite.flip_y = false; sprite.anchor = Anchor::TopLeft; - let base_position = image.get_position().unwrap(); + let position = image.get_position().unwrap(); *transform = Transform::from_xyz( - base_position.0 as f32 + image_definition.offset_px.0 as f32, - base_position.1 as f32 + image_definition.offset_px.1 as f32, + position.0 as f32, + position.1 as f32, image.get_priority().unwrap() as f32 + (*script_index as f32) / 100f32 + (*object_index as f32) / 100000f32, diff --git a/pixlib/src/inputs_plugin.rs b/pixlib/src/inputs_plugin.rs index 48fe3c3..fde2cb3 100644 --- a/pixlib/src/inputs_plugin.rs +++ b/pixlib/src/inputs_plugin.rs @@ -48,17 +48,21 @@ pub fn queue_mouse_input( .single() .cursor_position() .unwrap_or(Vec2::new(0f32, 0f32)); + in_events.push_back(MouseEvent::MovedTo { + x: cursor_position.x as isize, + y: cursor_position.y as isize, + }); if buttons.just_pressed(MouseButton::Left) { - in_events.push_back(MouseEvent::LeftButtonPressed { - x: cursor_position.x as u32, - y: cursor_position.y as u32, - }); + in_events.push_back(MouseEvent::LeftButtonPressed); } - if buttons.pressed(MouseButton::Right) { - in_events.push_back(MouseEvent::RightButtonPressed { - x: cursor_position.x as u32, - y: cursor_position.y as u32, - }); + if buttons.just_pressed(MouseButton::Right) { + in_events.push_back(MouseEvent::RightButtonPressed); + } + if buttons.just_released(MouseButton::Left) { + in_events.push_back(MouseEvent::LeftButtonReleased); + } + if buttons.just_released(MouseButton::Right) { + in_events.push_back(MouseEvent::RightButtonReleased); } } diff --git a/pixlib/src/main.rs b/pixlib/src/main.rs index 7bdd591..6f41433 100644 --- a/pixlib/src/main.rs +++ b/pixlib/src/main.rs @@ -101,11 +101,14 @@ fn main() { }) .insert_non_send_resource(InsertedDiskResource(inserted_disk)) .insert_resource(ChosenScene::default()) - .insert_non_send_resource(ScriptRunner(CnvRunner::new( - layered_fs, - Arc::new(GamePaths::default()), - runner_issue_manager, - ))) + .insert_non_send_resource(ScriptRunner( + CnvRunner::try_new( + layered_fs, + Arc::new(GamePaths::default()), + runner_issue_manager, + ) + .unwrap(), + )) .insert_resource(ObjectBuilderIssueManager(issue_manager)) .init_state::() .add_systems(Startup, setup) diff --git a/pixlib_parser/src/classes/animation.rs b/pixlib_parser/src/classes/animation.rs index 53b526f..c4d3238 100644 --- a/pixlib_parser/src/classes/animation.rs +++ b/pixlib_parser/src/classes/animation.rs @@ -29,20 +29,20 @@ pub struct AnimationProperties { pub visible: Option, // VISIBLE pub on_click: Option>, // ONCLICK signal - pub on_collision: Option>, // ONCOLLISION signal - pub on_collision_finished: Option>, // ONCOLLISIONFINISHED signal + pub on_collision: HashMap>, // ONCOLLISION signal + pub on_collision_finished: HashMap>, // ONCOLLISIONFINISHED signal pub on_done: Option>, // ONDONE signal - pub on_finished: Option>, // ONFINISHED signal - pub on_first_frame: Option>, // ONFIRSTFRAME signal + pub on_finished: HashMap>, // ONFINISHED signal + pub on_first_frame: HashMap>, // ONFIRSTFRAME signal pub on_focus_off: Option>, // ONFOCUSOFF signal pub on_focus_on: Option>, // ONFOCUSON signal - pub on_frame_changed: Option>, // ONFRAMECHANGED signal + pub on_frame_changed: HashMap>, // ONFRAMECHANGED signal pub on_init: Option>, // ONINIT signal - pub on_paused: Option>, // ONPAUSED signal + pub on_paused: HashMap>, // ONPAUSED signal pub on_release: Option>, // ONRELEASE signal - pub on_resumed: Option>, // ONRESUMED signal - pub on_signal: Option>, // ONSIGNAL signal - pub on_started: Option>, // ONSTARTED signal + pub on_resumed: HashMap>, // ONRESUMED signal + pub on_signal: HashMap>, // ONSIGNAL signal + pub on_started: HashMap>, // ONSTARTED signal } #[derive(Debug, Clone, Default)] @@ -77,41 +77,59 @@ struct AnimationState { #[derive(Debug, Clone)] pub struct AnimationEventHandlers { - pub on_click: Option>, // ONCLICK signal - pub on_collision: Option>, // ONCOLLISION signal - pub on_collision_finished: Option>, // ONCOLLISIONFINISHED signal - pub on_done: Option>, // ONDONE signal - pub on_finished: Option>, // ONFINISHED signal - pub on_first_frame: Option>, // ONFIRSTFRAME signal + pub on_click: Option>, // ONCLICK signal + pub on_collision: HashMap>, // ONCOLLISION signal + pub on_collision_finished: HashMap>, // ONCOLLISIONFINISHED signal + pub on_done: Option>, // ONDONE signal + pub on_finished: HashMap>, // ONFINISHED signal + pub on_first_frame: HashMap>, // ONFIRSTFRAME signal pub on_focus_off: Option>, // ONFOCUSOFF signal - pub on_focus_on: Option>, // ONFOCUSON signal - pub on_frame_changed: Option>, // ONFRAMECHANGED signal - pub on_init: Option>, // ONINIT signal - pub on_paused: Option>, // ONPAUSED signal - pub on_release: Option>, // ONRELEASE signal - pub on_resumed: Option>, // ONRESUMED signal - pub on_signal: Option>, // ONSIGNAL signal - pub on_started: Option>, // ONSTARTED signal + pub on_focus_on: Option>, // ONFOCUSON signal + pub on_frame_changed: HashMap>, // ONFRAMECHANGED signal + pub on_init: Option>, // ONINIT signal + pub on_paused: HashMap>, // ONPAUSED signal + pub on_release: Option>, // ONRELEASE signal + pub on_resumed: HashMap>, // ONRESUMED signal + pub on_signal: HashMap>, // ONSIGNAL signal + pub on_started: HashMap>, // ONSTARTED signal } impl EventHandler for AnimationEventHandlers { - fn get(&self, name: &str, _argument: Option<&str>) -> Option<&Arc> { + fn get(&self, name: &str, argument: Option<&str>) -> Option<&Arc> { match name { "ONCLICK" => self.on_click.as_ref(), - "ONCOLLISION" => self.on_collision.as_ref(), - "ONCOLLISIONFINISHED" => self.on_collision_finished.as_ref(), + "ONCOLLISION" => argument + .and_then(|a| self.on_collision.get(a)) + .or(self.on_collision.get("")), + "ONCOLLISIONFINISHED" => argument + .and_then(|a| self.on_collision_finished.get(a)) + .or(self.on_collision_finished.get("")), "ONDONE" => self.on_done.as_ref(), - "ONFINISHED" => self.on_finished.as_ref(), - "ONFIRSTFRAME" => self.on_first_frame.as_ref(), + "ONFINISHED" => argument + .and_then(|a| self.on_finished.get(a)) + .or(self.on_finished.get("")), + "ONFIRSTFRAME" => argument + .and_then(|a| self.on_first_frame.get(a)) + .or(self.on_first_frame.get("")), "ONFOCUSOFF" => self.on_focus_off.as_ref(), "ONFOCUSON" => self.on_focus_on.as_ref(), - "ONFRAMECHANGED" => self.on_frame_changed.as_ref(), + "ONFRAMECHANGED" => argument + .and_then(|a| self.on_frame_changed.get(a)) + .or(self.on_frame_changed.get("")), "ONINIT" => self.on_init.as_ref(), - "ONPAUSED" => self.on_paused.as_ref(), + "ONPAUSED" => argument + .and_then(|a| self.on_paused.get(a)) + .or(self.on_paused.get("")), "ONRELEASE" => self.on_release.as_ref(), - "ONRESUMED" => self.on_resumed.as_ref(), - "ONSIGNAL" => self.on_signal.as_ref(), - "ONSTARTED" => self.on_started.as_ref(), + "ONRESUMED" => argument + .and_then(|a| self.on_resumed.get(a)) + .or(self.on_resumed.get("")), + "ONSIGNAL" => argument + .and_then(|a| self.on_signal.get(a)) + .or(self.on_signal.get("")), + "ONSTARTED" => argument + .and_then(|a| self.on_started.get(a)) + .or(self.on_started.get("")), _ => None, } } @@ -198,6 +216,7 @@ impl Animation { &self, ) -> RunnerResult> { // eprintln!("[ANIMO: {}] is_visible: {}", self.parent.name, self.is_visible); + let context = RunnerContext::new_minimal(&self.parent.parent.runner, &self.parent); let state = self.state.borrow(); if !state.is_visible { return Ok(None); @@ -212,8 +231,19 @@ impl Animation { if sequence.frames.is_empty() { return Ok(None); } - let frame = &sequence.frames[state.current_frame.frame_idx]; - let sprite = &loaded_data.sprites[frame.sprite_idx]; + let Some(frame) = sequence.frames.get(state.current_frame.frame_idx) else { + return Err(RunnerError::FrameIndexNotFound { + object_name: context.current_object.name.clone(), + sequence_name: sequence.name.clone(), + index: state.current_frame.frame_idx, + }); + }; + let Some(sprite) = loaded_data.sprites.get(frame.sprite_idx) else { + return Err(RunnerError::SpriteIndexNotFound { + object_name: context.current_object.name.clone(), + index: frame.sprite_idx, + }); + }; // eprintln!("[ANIMO: {}] [current frame] position: {:?} + {:?}, hash: {:?}", self.parent.name, sprite.0.offset_px, frame.offset_px, sprite.1.hash); Ok(Some((frame.clone(), sprite.0.clone(), sprite.1.clone()))) } @@ -292,12 +322,13 @@ impl CnvType for Animation { self.state.borrow().get_end_y(); Ok(None) } - CallableIdentifier::Method("GETEVENTNAME") => { - self.state.borrow().get_event_name(); - Ok(None) - } + CallableIdentifier::Method("GETEVENTNAME") => self + .state + .borrow() + .get_sequence_name(context) + .map(|v| Some(CnvValue::String(v))), CallableIdentifier::Method("GETEVENTNUMBER") => { - self.state.borrow().get_event_number(); + self.state.borrow().get_sequence_index(); Ok(None) } CallableIdentifier::Method("GETFPS") => { @@ -312,10 +343,11 @@ impl CnvType for Animation { self.state.borrow().get_frame_name(); Ok(None) } - CallableIdentifier::Method("GETFRAMENO") => { - self.state.borrow().get_frame_no(); - Ok(None) - } + CallableIdentifier::Method("GETFRAMENO") => self + .state + .borrow() + .get_frame_index() + .map(|v| Some(CnvValue::Integer(v as i32))), CallableIdentifier::Method("GETHEIGHT") => { self.state.borrow().get_height(); Ok(None) @@ -329,15 +361,17 @@ impl CnvType for Animation { Ok(None) } CallableIdentifier::Method("GETNOE") => { - self.state.borrow().get_noe(); + self.state.borrow().get_sequence_count(); Ok(None) } CallableIdentifier::Method("GETNOF") => { - self.state.borrow().get_nof(); + self.state.borrow().get_total_frame_count(); Ok(None) } CallableIdentifier::Method("GETNOFINEVENT") => { - self.state.borrow().get_nof_in_event(&arguments[0].to_str()); + self.state + .borrow() + .get_sequence_frame_count(&arguments[0].to_str()); Ok(None) } CallableIdentifier::Method("GETOPACITY") => { @@ -641,31 +675,44 @@ impl CnvType for Animation { .and_then(discard_if_empty) .map(parse_event_handler) .transpose()?; - let on_collision = properties - .remove("ONCOLLISION") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; - let on_collision_finished = properties - .remove("ONCOLLISIONFINISHED") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_collision = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONCOLLISION" { + on_collision.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONCOLLISION^") { + on_collision.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } + let mut on_collision_finished = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONCOLLISIONFINISHED" { + on_collision_finished.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONCOLLISIONFINISHED^") { + on_collision_finished + .insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } let on_done = properties .remove("ONDONE") .and_then(discard_if_empty) .map(parse_event_handler) .transpose()?; - let on_finished = properties - .remove("ONFINISHED") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; - let on_first_frame = properties - .remove("ONFIRSTFRAME") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_finished = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONFINISHED" { + on_finished.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONFINISHED^") { + on_finished.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } + let mut on_first_frame = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONFIRSTFRAME" { + on_first_frame.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONFIRSTFRAME^") { + on_first_frame.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } let on_focus_off = properties .remove("ONFOCUSOFF") .and_then(discard_if_empty) @@ -676,41 +723,56 @@ impl CnvType for Animation { .and_then(discard_if_empty) .map(parse_event_handler) .transpose()?; - let on_frame_changed = properties - .remove("ONFRAMECHANGED") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_frame_changed = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONFRAMECHANGED" { + on_frame_changed.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONFRAMECHANGED^") { + on_frame_changed.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } let on_init = properties .remove("ONINIT") .and_then(discard_if_empty) .map(parse_event_handler) .transpose()?; - let on_paused = properties - .remove("ONPAUSED") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_paused = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONPAUSED" { + on_paused.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONPAUSED^") { + on_paused.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } let on_release = properties .remove("ONRELEASE") .and_then(discard_if_empty) .map(parse_event_handler) .transpose()?; - let on_resumed = properties - .remove("ONRESUMED") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; - let on_signal = properties - .remove("ONSIGNAL") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; - let on_started = properties - .remove("ONSTARTED") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_resumed = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONRESUMED" { + on_resumed.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONRESUMED^") { + on_resumed.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } + let mut on_signal = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONSIGNAL" { + on_signal.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONSIGNAL^") { + on_signal.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } + let mut on_started = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONSTARTED" { + on_started.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONSTARTED^") { + on_started.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } Ok(CnvContent::Animation(Animation::from_initial_properties( parent, AnimationProperties { @@ -835,12 +897,21 @@ impl AnimationState { todo!() } - pub fn get_event_name(&self) { + pub fn get_sequence_name(&self, context: RunnerContext) -> RunnerResult { // GETEVENTNAME - todo!() + let AnimationFileData::Loaded(loaded_file) = &self.file_data else { + return Err(RunnerError::NoDataLoaded); + }; + let Some(sequence) = loaded_file.sequences.get(self.current_frame.sequence_idx) else { + return Err(RunnerError::SequenceIndexNotFound { + object_name: context.current_object.name.clone(), + index: self.current_frame.sequence_idx, + }); + }; + Ok(sequence.name.clone()) } - pub fn get_event_number(&self) { + pub fn get_sequence_index(&self) { // GETEVENTNUMBER todo!() } @@ -860,9 +931,9 @@ impl AnimationState { todo!() } - pub fn get_frame_no(&self) -> usize { + pub fn get_frame_index(&self) -> RunnerResult { // GETFRAMENO INTEGER - todo!() + Ok(self.current_frame.frame_idx) } pub fn get_height(&self) { @@ -880,17 +951,17 @@ impl AnimationState { todo!() } - pub fn get_noe(&self) { + pub fn get_sequence_count(&self) { // GETNOE todo!() } - pub fn get_nof(&self) { + pub fn get_total_frame_count(&self) { // GETNOF todo!() } - pub fn get_nof_in_event(&self, _sequence_name: &str) -> usize { + pub fn get_sequence_frame_count(&self, _sequence_name: &str) -> usize { // GETNOFINEVENT INTEGER (STRING event) todo!() } @@ -1025,6 +1096,10 @@ impl AnimationState { source: std::io::Error::from(std::io::ErrorKind::NotFound), })?; let data = parse_ann(&data); + self.current_frame = FrameIdentifier { + sequence_idx: data.sequences.len().saturating_sub(1), + frame_idx: 0, + }; self.file_data = AnimationFileData::Loaded(LoadedAnimation { filename: Some(filename.to_owned()), sequences: data @@ -1186,6 +1261,7 @@ impl AnimationState { }) }); } + self.is_visible = true; Ok(()) } diff --git a/pixlib_parser/src/classes/behavior.rs b/pixlib_parser/src/classes/behavior.rs index 45e7f29..dc808f7 100644 --- a/pixlib_parser/src/classes/behavior.rs +++ b/pixlib_parser/src/classes/behavior.rs @@ -41,7 +41,9 @@ impl EventHandler for BehaviorEventHandlers { match name { "ONDONE" => self.on_done.as_ref(), "ONINIT" => self.on_init.as_ref(), - "ONSIGNAL" => self.on_signal.get(argument.unwrap_or("")), + "ONSIGNAL" => argument + .and_then(|a| self.on_signal.get(a)) + .or(self.on_signal.get("")), _ => None, } } diff --git a/pixlib_parser/src/classes/bool.rs b/pixlib_parser/src/classes/bool.rs index 95bc8ee..34c8b50 100644 --- a/pixlib_parser/src/classes/bool.rs +++ b/pixlib_parser/src/classes/bool.rs @@ -16,12 +16,12 @@ pub struct BoolVarProperties { pub to_ini: Option, // TOINI pub value: Option, // VALUE - pub on_brutal_changed: Option>, // ONBRUTALCHANGED signal - pub on_changed: Option>, // ONCHANGED signal - pub on_done: Option>, // ONDONE signal - pub on_init: Option>, // ONINIT signal - pub on_net_changed: Option>, // ONNETCHANGED signal - pub on_signal: Option>, // ONSIGNAL signal + pub on_brutal_changed: HashMap>, // ONBRUTALCHANGED signal + pub on_changed: HashMap>, // ONCHANGED signal + pub on_done: Option>, // ONDONE signal + pub on_init: Option>, // ONINIT signal + pub on_net_changed: HashMap>, // ONNETCHANGED signal + pub on_signal: HashMap>, // ONSIGNAL signal } #[derive(Debug, Clone, Default)] @@ -33,23 +33,31 @@ struct BoolVarState { #[derive(Debug, Clone)] struct BoolVarEventHandlers { - pub on_brutal_changed: Option>, // ONBRUTALCHANGED signal - pub on_changed: Option>, // ONCHANGED signal - pub on_done: Option>, // ONDONE signal - pub on_init: Option>, // ONINIT signal - pub on_net_changed: Option>, // ONNETCHANGED signal - pub on_signal: Option>, // ONSIGNAL signal + pub on_brutal_changed: HashMap>, // ONBRUTALCHANGED signal + pub on_changed: HashMap>, // ONCHANGED signal + pub on_done: Option>, // ONDONE signal + pub on_init: Option>, // ONINIT signal + pub on_net_changed: HashMap>, // ONNETCHANGED signal + pub on_signal: HashMap>, // ONSIGNAL signal } impl EventHandler for BoolVarEventHandlers { - fn get(&self, name: &str, _argument: Option<&str>) -> Option<&Arc> { + fn get(&self, name: &str, argument: Option<&str>) -> Option<&Arc> { match name { - "ONBRUTALCHANGED" => self.on_brutal_changed.as_ref(), - "ONCHANGED" => self.on_changed.as_ref(), + "ONBRUTALCHANGED" => argument + .and_then(|a| self.on_brutal_changed.get(a)) + .or(self.on_brutal_changed.get("")), + "ONCHANGED" => argument + .and_then(|a| self.on_changed.get(a)) + .or(self.on_changed.get("")), "ONDONE" => self.on_done.as_ref(), "ONINIT" => self.on_init.as_ref(), - "ONNETCHANGED" => self.on_net_changed.as_ref(), - "ONSIGNAL" => self.on_signal.as_ref(), + "ONNETCHANGED" => argument + .and_then(|a| self.on_net_changed.get(a)) + .or(self.on_net_changed.get("")), + "ONSIGNAL" => argument + .and_then(|a| self.on_signal.get(a)) + .or(self.on_signal.get("")), _ => None, } } @@ -196,16 +204,23 @@ impl CnvType for BoolVar { .and_then(discard_if_empty) .map(parse_bool) .transpose()?; - let on_brutal_changed = properties - .remove("ONBRUTALCHANGED") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; - let on_changed = properties - .remove("ONCHANGED") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_brutal_changed = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONBRUTALCHANGED" { + on_brutal_changed.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONBRUTALCHANGED^") { + on_brutal_changed + .insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } + let mut on_changed = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONCHANGED" { + on_changed.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONCHANGED^") { + on_changed.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } let on_done = properties .remove("ONDONE") .and_then(discard_if_empty) @@ -216,16 +231,22 @@ impl CnvType for BoolVar { .and_then(discard_if_empty) .map(parse_event_handler) .transpose()?; - let on_net_changed = properties - .remove("ONNETCHANGED") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; - let on_signal = properties - .remove("ONSIGNAL") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_net_changed = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONNETCHANGED" { + on_net_changed.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONNETCHANGED^") { + on_net_changed.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } + let mut on_signal = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONSIGNAL" { + on_signal.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONSIGNAL^") { + on_signal.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } Ok(CnvContent::Bool(Self::from_initial_properties( parent, BoolVarProperties { diff --git a/pixlib_parser/src/classes/button.rs b/pixlib_parser/src/classes/button.rs index 2c8f58e..60fb496 100644 --- a/pixlib_parser/src/classes/button.rs +++ b/pixlib_parser/src/classes/button.rs @@ -34,7 +34,7 @@ pub struct ButtonProperties { pub on_init: Option>, // ONINIT signal pub on_paused: Option>, // ONPAUSED signal pub on_released: Option>, // ONRELEASED signal - pub on_signal: Option>, // ONSIGNAL signal + pub on_signal: HashMap>, // ONSIGNAL signal pub on_start_dragging: Option>, // ONSTARTDRAGGING signal } @@ -49,6 +49,9 @@ pub struct ButtonState { pub graphics_on_click: Option, pub priority: isize, pub rect: Option, + + // deduced from methods + pub is_visible: bool, } #[derive(Debug, Clone)] @@ -63,12 +66,12 @@ pub struct ButtonEventHandlers { pub on_init: Option>, // ONINIT signal pub on_paused: Option>, // ONPAUSED signal pub on_released: Option>, // ONRELEASED signal - pub on_signal: Option>, // ONSIGNAL signal + pub on_signal: HashMap>, // ONSIGNAL signal pub on_start_dragging: Option>, // ONSTARTDRAGGING signal } impl EventHandler for ButtonEventHandlers { - fn get(&self, name: &str, _argument: Option<&str>) -> Option<&Arc> { + fn get(&self, name: &str, argument: Option<&str>) -> Option<&Arc> { match name { "ONACTION" => self.on_action.as_ref(), "ONCLICKED" => self.on_clicked.as_ref(), @@ -80,7 +83,9 @@ impl EventHandler for ButtonEventHandlers { "ONINIT" => self.on_init.as_ref(), "ONPAUSED" => self.on_paused.as_ref(), "ONRELEASED" => self.on_released.as_ref(), - "ONSIGNAL" => self.on_signal.as_ref(), + "ONSIGNAL" => argument + .and_then(|a| self.on_signal.get(a)) + .or(self.on_signal.get("")), "ONSTARTDRAGGING" => self.on_start_dragging.as_ref(), _ => None, } @@ -102,10 +107,11 @@ pub struct Button { impl Button { pub fn from_initial_properties(parent: Arc, props: ButtonProperties) -> Self { + let is_enabled = props.enable.unwrap_or(true); Self { parent, state: RefCell::new(ButtonState { - is_enabled: props.enable.unwrap_or_default(), + is_enabled, is_accented: props.accent.unwrap_or_default(), is_draggable: props.draggable.unwrap_or_default(), graphics_normal: props.gfx_standard, @@ -113,6 +119,7 @@ impl Button { graphics_on_click: props.gfx_on_click, priority: props.priority.unwrap_or_default() as isize, rect: props.rect, + is_visible: is_enabled, }), event_handlers: ButtonEventHandlers { on_action: props.on_action, @@ -134,6 +141,12 @@ impl Button { sound_on_click: props.snd_on_click, } } + + // custom + + pub fn update(&self, context: RunnerContext) -> RunnerResult<()> { + Ok(()) + } } impl CnvType for Button { @@ -296,11 +309,14 @@ impl CnvType for Button { .and_then(discard_if_empty) .map(parse_event_handler) .transpose()?; - let on_signal = properties - .remove("ONSIGNAL") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_signal = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONSIGNAL" { + on_signal.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONSIGNAL^") { + on_signal.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } let on_start_dragging = properties .remove("ONSTARTDRAGGING") .and_then(discard_if_empty) diff --git a/pixlib_parser/src/classes/image.rs b/pixlib_parser/src/classes/image.rs index 7e3384b..9f38523 100644 --- a/pixlib_parser/src/classes/image.rs +++ b/pixlib_parser/src/classes/image.rs @@ -50,6 +50,7 @@ struct ImageState { // general graphics state pub position: (isize, isize), + pub default_position: (isize, isize), pub opacity: usize, // anchor: ???, pub is_flipped_horizontally: bool, @@ -222,12 +223,16 @@ impl CnvType for Image { CallableIdentifier::Method("GETPIXEL") => { self.state.borrow_mut().get_pixel().map(|_| None) } - CallableIdentifier::Method("GETPOSITIONX") => { - self.state.borrow_mut().get_position_x().map(|_| None) - } - CallableIdentifier::Method("GETPOSITIONY") => { - self.state.borrow_mut().get_position_y().map(|_| None) - } + CallableIdentifier::Method("GETPOSITIONX") => self + .state + .borrow() + .get_position_x() + .map(|v| Some(CnvValue::Integer(v as i32))), + CallableIdentifier::Method("GETPOSITIONY") => self + .state + .borrow() + .get_position_y() + .map(|v| Some(CnvValue::Integer(v as i32))), CallableIdentifier::Method("GETPRIORITY") => self .state .borrow_mut() @@ -314,9 +319,14 @@ impl CnvType for Image { CallableIdentifier::Method("SETPRIORITY") => { self.state.borrow_mut().set_priority().map(|_| None) } - CallableIdentifier::Method("SETRESETPOSITION") => { - self.state.borrow_mut().set_reset_position().map(|_| None) - } + CallableIdentifier::Method("SETRESETPOSITION") => self + .state + .borrow_mut() + .set_reset_position( + arguments[0].to_int() as isize, + arguments[1].to_int() as isize, + ) + .map(|_| None), CallableIdentifier::Method("SETSCALEFACTOR") => { self.state.borrow_mut().set_scale_factor().map(|_| None) } @@ -553,14 +563,14 @@ impl ImageState { todo!() } - pub fn get_position_x(&mut self) -> RunnerResult<()> { + pub fn get_position_x(&self) -> RunnerResult { // GETPOSITIONX - todo!() + Ok(self.position.0) } - pub fn get_position_y(&mut self) -> RunnerResult<()> { + pub fn get_position_y(&self) -> RunnerResult { // GETPOSITIONY - todo!() + Ok(self.position.1) } pub fn get_priority(&self) -> RunnerResult { @@ -631,6 +641,11 @@ impl ImageState { let converted_data = data .image_data .to_rgba8888(data.header.color_format, data.header.compression_type); + self.default_position = ( + data.header.x_position_px as isize, + data.header.y_position_px as isize, + ); + self.position = self.default_position; self.file_data = ImageFileData::Loaded(LoadedImage { filename: Some(filename.to_owned()), image: ( @@ -685,7 +700,8 @@ impl ImageState { pub fn reset_position(&mut self) -> RunnerResult<()> { // RESETPOSITION - todo!() + self.position = self.default_position; + Ok(()) } pub fn save(&mut self) -> RunnerResult<()> { @@ -724,9 +740,10 @@ impl ImageState { todo!() } - pub fn set_reset_position(&mut self) -> RunnerResult<()> { + pub fn set_reset_position(&mut self, x: isize, y: isize) -> RunnerResult<()> { // SETRESETPOSITION - todo!() + self.default_position = (x, y); + Ok(()) } pub fn set_scale_factor(&mut self) -> RunnerResult<()> { diff --git a/pixlib_parser/src/classes/mod.rs b/pixlib_parser/src/classes/mod.rs index 2274ccf..31358de 100644 --- a/pixlib_parser/src/classes/mod.rs +++ b/pixlib_parser/src/classes/mod.rs @@ -316,7 +316,7 @@ pub use image::Image; pub use int::IntegerVar; pub use keyboard::Keyboard; pub use lalrpop_util::ParseError; -pub use mouse::Mouse; +pub use mouse::{InternalMouseEvent, Mouse}; pub use multiarray::MultiArray; pub use music::Music; pub use object::CnvObject; diff --git a/pixlib_parser/src/classes/mouse.rs b/pixlib_parser/src/classes/mouse.rs index d627e16..ea86859 100644 --- a/pixlib_parser/src/classes/mouse.rs +++ b/pixlib_parser/src/classes/mouse.rs @@ -1,68 +1,105 @@ -use std::{any::Any, cell::RefCell}; +use std::{ + any::Any, + collections::VecDeque, + sync::RwLock, + time::{SystemTime, UNIX_EPOCH}, +}; use content::EventHandler; use initable::Initable; use parsers::{discard_if_empty, parse_event_handler, parse_i32, Rect}; -use crate::{ast::ParsedScript, common::DroppableRefMut, runner::InternalEvent}; +use crate::{ + ast::ParsedScript, + common::DroppableRefMut, + runner::{InternalEvent, MouseEvent}, +}; use super::*; +const DOUBLE_CLICK_MAX_INTERVAL_SECONDS: f64 = 0.5; + #[derive(Debug, Clone)] pub struct MouseProperties { // MOUSE pub mouse: Option, // MOUSE pub raw: Option, // RAW - pub on_click: Option>, // ONCLICK signal - pub on_dbl_click: Option>, // ONDBLCLICK signal - pub on_done: Option>, // ONDONE signal - pub on_init: Option>, // ONINIT signal - pub on_move: Option>, // ONMOVE signal - pub on_release: Option>, // ONRELEASE signal - pub on_signal: Option>, // ONSIGNAL signal + pub on_click: HashMap>, // ONCLICK signal + pub on_dbl_click: HashMap>, // ONDBLCLICK signal + pub on_done: Option>, // ONDONE signal + pub on_init: Option>, // ONINIT signal + pub on_move: Option>, // ONMOVE signal + pub on_release: HashMap>, // ONRELEASE signal + pub on_signal: HashMap>, // ONSIGNAL signal } #[derive(Debug, Clone, Default)] struct MouseState { // deduced from methods is_enabled: bool, - are_events_enabled: bool, + are_events_enabled: bool, // TODO: use this is_visible: bool, clip_rect: Option, + + position: (isize, isize), + last_click_position: (isize, isize), + last_click_time_seconds: f64, + is_left_button_down: bool, + is_right_button_down: bool, + is_locked: bool, + + events_out: VecDeque, } #[derive(Debug, Clone)] pub struct MouseEventHandlers { - pub on_click: Option>, // ONCLICK signal - pub on_dbl_click: Option>, // ONDBLCLICK signal - pub on_done: Option>, // ONDONE signal - pub on_init: Option>, // ONINIT signal - pub on_move: Option>, // ONMOVE signal - pub on_release: Option>, // ONRELEASE signal - pub on_signal: Option>, // ONSIGNAL signal + pub on_click: HashMap>, // ONCLICK signal + pub on_dbl_click: HashMap>, // ONDBLCLICK signal + pub on_done: Option>, // ONDONE signal + pub on_init: Option>, // ONINIT signal + pub on_move: Option>, // ONMOVE signal + pub on_release: HashMap>, // ONRELEASE signal + pub on_signal: HashMap>, // ONSIGNAL signal } impl EventHandler for MouseEventHandlers { - fn get(&self, name: &str, _argument: Option<&str>) -> Option<&Arc> { + fn get(&self, name: &str, argument: Option<&str>) -> Option<&Arc> { match name { - "ONCLICK" => self.on_click.as_ref(), - "ONDBLCLICK" => self.on_dbl_click.as_ref(), + "ONCLICK" => argument + .and_then(|a| self.on_click.get(a)) + .or(self.on_click.get("")), + "ONDBLCLICK" => argument + .and_then(|a| self.on_dbl_click.get(a)) + .or(self.on_dbl_click.get("")), "ONDONE" => self.on_done.as_ref(), "ONINIT" => self.on_init.as_ref(), "ONMOVE" => self.on_move.as_ref(), - "ONRELEASE" => self.on_release.as_ref(), - "ONSIGNAL" => self.on_signal.as_ref(), + "ONRELEASE" => argument + .and_then(|a| self.on_release.get(a)) + .or(self.on_release.get("")), + "ONSIGNAL" => argument + .and_then(|a| self.on_signal.get(a)) + .or(self.on_signal.get("")), _ => None, } } } +lazy_static! { + static ref GLOBAL_MOUSE_STATE: Arc> = Arc::new(RwLock::new(MouseState { + is_enabled: true, + are_events_enabled: true, + is_visible: true, + ..Default::default() + })); +} + #[derive(Debug, Clone)] pub struct Mouse { parent: Arc, - state: RefCell, + state: Arc>, event_handlers: MouseEventHandlers, mouse: String, @@ -73,12 +110,7 @@ impl Mouse { pub fn from_initial_properties(parent: Arc, props: MouseProperties) -> Self { Self { parent, - state: RefCell::new(MouseState { - is_enabled: true, - are_events_enabled: true, - is_visible: true, - ..Default::default() - }), + state: Arc::clone(&GLOBAL_MOUSE_STATE), event_handlers: MouseEventHandlers { on_click: props.on_click, on_dbl_click: props.on_dbl_click, @@ -92,6 +124,27 @@ impl Mouse { raw: props.raw.unwrap_or_default(), } } + + pub fn handle_incoming_event(event: MouseEvent) -> RunnerResult<()> { + let mut mouse_state = GLOBAL_MOUSE_STATE.write().unwrap(); + match event { + MouseEvent::MovedTo { x, y } => mouse_state.set_position(x, y), + MouseEvent::LeftButtonPressed => mouse_state.set_left_button_down(true), + MouseEvent::LeftButtonReleased => mouse_state.set_left_button_down(false), + MouseEvent::RightButtonPressed => mouse_state.set_right_button_down(true), + MouseEvent::RightButtonReleased => mouse_state.set_right_button_down(false), + } + } + + pub fn handle_outgoing_events( + mut handler: impl FnMut(InternalMouseEvent) -> RunnerResult<()>, + ) -> RunnerResult<()> { + let mut mouse_state = GLOBAL_MOUSE_STATE.write().unwrap(); + for event in mouse_state.events_out.drain(..) { + handler(event)?; + } + Ok(()) + } } impl CnvType for Mouse { @@ -115,66 +168,102 @@ impl CnvType for Mouse { ) -> RunnerResult> { // println!("Calling method: {:?} of object: {:?}", name, self); match name { - CallableIdentifier::Method("CLICK") => self.state.borrow_mut().click().map(|_| None), + CallableIdentifier::Method("CLICK") => self + .state + .write() + .unwrap() + .click_left_button() + .map(|_| None), CallableIdentifier::Method("DISABLE") => { - self.state.borrow_mut().disable().map(|_| None) - } - CallableIdentifier::Method("DISABLESIGNAL") => { - self.state.borrow_mut().disable_signal().map(|_| None) + self.state.write().unwrap().disable().map(|_| None) } - CallableIdentifier::Method("ENABLE") => self.state.borrow_mut().enable().map(|_| None), - CallableIdentifier::Method("ENABLESIGNAL") => { - self.state.borrow_mut().enable_signal().map(|_| None) + CallableIdentifier::Method("DISABLESIGNAL") => self + .state + .write() + .unwrap() + .disable_event_handling() + .map(|_| None), + CallableIdentifier::Method("ENABLE") => { + self.state.write().unwrap().enable().map(|_| None) } + CallableIdentifier::Method("ENABLESIGNAL") => self + .state + .write() + .unwrap() + .enable_event_handling() + .map(|_| None), CallableIdentifier::Method("GETLASTCLICKPOSX") => self .state - .borrow() - .get_last_click_pos_x() + .read() + .unwrap() + .get_last_click_position_x() .map(|v| Some(CnvValue::Integer(v as i32))), CallableIdentifier::Method("GETLASTCLICKPOSY") => self .state - .borrow() - .get_last_click_pos_y() + .read() + .unwrap() + .get_last_click_position_y() .map(|v| Some(CnvValue::Integer(v as i32))), CallableIdentifier::Method("GETPOSX") => self .state - .borrow() - .get_pos_x() + .read() + .unwrap() + .get_position_x() .map(|v| Some(CnvValue::Integer(v as i32))), CallableIdentifier::Method("GETPOSY") => self .state - .borrow() - .get_pos_y() + .read() + .unwrap() + .get_position_y() .map(|v| Some(CnvValue::Integer(v as i32))), - CallableIdentifier::Method("HIDE") => self.state.borrow_mut().hide().map(|_| None), + CallableIdentifier::Method("HIDE") => self.state.write().unwrap().hide().map(|_| None), CallableIdentifier::Method("ISLBUTTONDOWN") => self .state - .borrow() - .is_l_button_down() + .read() + .unwrap() + .is_left_button_down() .map(|v| Some(CnvValue::Bool(v))), CallableIdentifier::Method("ISRBUTTONDOWN") => self .state - .borrow() - .is_r_button_down() + .read() + .unwrap() + .is_right_button_down() .map(|v| Some(CnvValue::Bool(v))), CallableIdentifier::Method("LOCKACTIVECURSOR") => { - self.state.borrow_mut().lock_active_cursor().map(|_| None) + self.state.write().unwrap().lock_cursor().map(|_| None) } - CallableIdentifier::Method("MOUSERELEASE") => { - self.state.borrow_mut().mouse_release().map(|_| None) - } - CallableIdentifier::Method("MOVE") => self.state.borrow_mut().move_by().map(|_| None), - CallableIdentifier::Method("SET") => self.state.borrow_mut().set().map(|_| None), + CallableIdentifier::Method("MOUSERELEASE") => self + .state + .write() + .unwrap() + .release_left_button() + .map(|_| None), + CallableIdentifier::Method("MOVE") => self + .state + .write() + .unwrap() + .move_by( + arguments[0].to_int() as isize, + arguments[1].to_int() as isize, + ) + .map(|_| None), + CallableIdentifier::Method("SET") => self.state.write().unwrap().set().map(|_| None), CallableIdentifier::Method("SETACTIVERECT") => { - self.state.borrow_mut().set_active_rect().map(|_| None) + self.state.write().unwrap().set_active_rect().map(|_| None) } CallableIdentifier::Method("SETCLIPRECT") => { - self.state.borrow_mut().set_clip_rect().map(|_| None) + self.state.write().unwrap().set_clip_rect().map(|_| None) } - CallableIdentifier::Method("SETPOSITION") => { - self.state.borrow_mut().set_position().map(|_| None) - } - CallableIdentifier::Method("SHOW") => self.state.borrow_mut().show().map(|_| None), + CallableIdentifier::Method("SETPOSITION") => self + .state + .write() + .unwrap() + .set_position( + arguments[0].to_int() as isize, + arguments[1].to_int() as isize, + ) + .map(|_| None), + CallableIdentifier::Method("SHOW") => self.state.write().unwrap().show().map(|_| None), CallableIdentifier::Event(event_name) => { if let Some(code) = self .event_handlers @@ -198,16 +287,22 @@ impl CnvType for Mouse { .and_then(discard_if_empty) .map(parse_i32) .transpose()?; - let on_click = properties - .remove("ONCLICK") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; - let on_dbl_click = properties - .remove("ONDBLCLICK") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_click = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONCLICK" { + on_click.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONCLICK^") { + on_click.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } + let mut on_dbl_click = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONDBLCLICK" { + on_dbl_click.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONDBLCLICK^") { + on_dbl_click.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } let on_done = properties .remove("ONDONE") .and_then(discard_if_empty) @@ -223,16 +318,22 @@ impl CnvType for Mouse { .and_then(discard_if_empty) .map(parse_event_handler) .transpose()?; - let on_release = properties - .remove("ONRELEASE") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; - let on_signal = properties - .remove("ONSIGNAL") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_release = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONRELEASE" { + on_release.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONRELEASE^") { + on_release.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } + let mut on_signal = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONSIGNAL" { + on_signal.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONSIGNAL^") { + on_signal.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } Ok(CnvContent::Mouse(Self::from_initial_properties( parent, MouseProperties { @@ -268,79 +369,89 @@ impl Initable for Mouse { } impl MouseState { - pub fn click(&mut self) -> RunnerResult<()> { + pub fn click_left_button(&mut self) -> RunnerResult<()> { // CLICK - todo!() + self.set_left_button_down(true) } pub fn disable(&mut self) -> RunnerResult<()> { // DISABLE - todo!() + self.is_enabled = false; + Ok(()) } - pub fn disable_signal(&mut self) -> RunnerResult<()> { + pub fn disable_event_handling(&mut self) -> RunnerResult<()> { // DISABLESIGNAL - todo!() + self.are_events_enabled = false; + Ok(()) } pub fn enable(&mut self) -> RunnerResult<()> { // ENABLE - todo!() + self.is_enabled = true; + Ok(()) } - pub fn enable_signal(&mut self) -> RunnerResult<()> { + pub fn enable_event_handling(&mut self) -> RunnerResult<()> { // ENABLESIGNAL - todo!() + self.are_events_enabled = true; + Ok(()) } - pub fn get_last_click_pos_x(&self) -> RunnerResult { + pub fn get_last_click_position_x(&self) -> RunnerResult { // GETLASTCLICKPOSX - todo!() + Ok(self.last_click_position.0) } - pub fn get_last_click_pos_y(&self) -> RunnerResult { + pub fn get_last_click_position_y(&self) -> RunnerResult { // GETLASTCLICKPOSY - todo!() + Ok(self.last_click_position.1) } - pub fn get_pos_x(&self) -> RunnerResult { + pub fn get_position_x(&self) -> RunnerResult { // GETPOSX - todo!() + Ok(self.position.0) } - pub fn get_pos_y(&self) -> RunnerResult { + pub fn get_position_y(&self) -> RunnerResult { // GETPOSY - todo!() + Ok(self.position.1) } pub fn hide(&mut self) -> RunnerResult<()> { // HIDE - todo!() + self.is_visible = false; + Ok(()) } - pub fn is_l_button_down(&self) -> RunnerResult { + pub fn is_left_button_down(&self) -> RunnerResult { // ISLBUTTONDOWN - todo!() + Ok(self.is_left_button_down) } - pub fn is_r_button_down(&self) -> RunnerResult { + pub fn is_right_button_down(&self) -> RunnerResult { // ISRBUTTONDOWN - todo!() + Ok(self.is_right_button_down) } - pub fn lock_active_cursor(&mut self) -> RunnerResult<()> { + pub fn lock_cursor(&mut self) -> RunnerResult<()> { // LOCKACTIVECURSOR - todo!() + self.is_locked = true; + self.events_out.push_back(InternalMouseEvent::CursorLocked); + Ok(()) } - pub fn mouse_release(&mut self) -> RunnerResult<()> { + pub fn release_left_button(&mut self) -> RunnerResult<()> { // MOUSERELEASE - todo!() + self.set_left_button_down(false) } - pub fn move_by(&mut self) -> RunnerResult<()> { + pub fn move_by(&mut self, x: isize, y: isize) -> RunnerResult<()> { // MOVE - todo!() + self.position = (self.position.0 + x, self.position.1 + y); + self.events_out + .push_back(InternalMouseEvent::MovedBy { x, y }); + Ok(()) } pub fn set(&mut self) -> RunnerResult<()> { @@ -358,13 +469,103 @@ impl MouseState { todo!() } - pub fn set_position(&mut self) -> RunnerResult<()> { + pub fn set_position(&mut self, x: isize, y: isize) -> RunnerResult<()> { // SETPOSITION - todo!() + let position_diff = (x - self.position.0, y - self.position.1); + self.position = (x, y); + if position_diff.0 != 0 && position_diff.1 != 0 { + self.events_out.push_back(InternalMouseEvent::MovedBy { + x: position_diff.0, + y: position_diff.1, + }); + } + Ok(()) } pub fn show(&mut self) -> RunnerResult<()> { // SHOW - todo!() + self.is_visible = true; + Ok(()) } + + // custom + + pub fn set_left_button_down(&mut self, is_down: bool) -> RunnerResult<()> { + if is_down != self.is_left_button_down { + if is_down { + self.events_out + .push_back(InternalMouseEvent::LeftButtonPressed { + x: self.position.0, + y: self.position.1, + }); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs_f64(); + if now - self.last_click_time_seconds <= DOUBLE_CLICK_MAX_INTERVAL_SECONDS { + self.events_out + .push_back(InternalMouseEvent::LeftButtonDoubleClicked { + x: self.position.0, + y: self.position.1, + }); + } + self.last_click_position = self.position; + self.last_click_time_seconds = now; + } else { + self.events_out + .push_back(InternalMouseEvent::LeftButtonReleased { + x: self.position.0, + y: self.position.1, + }); + } + } + self.is_left_button_down = is_down; + Ok(()) + } + + pub fn set_right_button_down(&mut self, is_down: bool) -> RunnerResult<()> { + if is_down != self.is_right_button_down { + if is_down { + self.events_out + .push_back(InternalMouseEvent::RightButtonPressed { + x: self.position.0, + y: self.position.1, + }); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs_f64(); + if now - self.last_click_time_seconds <= DOUBLE_CLICK_MAX_INTERVAL_SECONDS { + self.events_out + .push_back(InternalMouseEvent::RightButtonDoubleClicked { + x: self.position.0, + y: self.position.1, + }); + } + self.last_click_position = self.position; + self.last_click_time_seconds = now; + } else { + self.events_out + .push_back(InternalMouseEvent::RightButtonReleased { + x: self.position.0, + y: self.position.1, + }); + } + } + self.is_right_button_down = is_down; + Ok(()) + } +} + +#[derive(Debug, Clone)] +pub enum InternalMouseEvent { + LeftButtonPressed { x: isize, y: isize }, + LeftButtonReleased { x: isize, y: isize }, + RightButtonPressed { x: isize, y: isize }, + RightButtonReleased { x: isize, y: isize }, + LeftButtonDoubleClicked { x: isize, y: isize }, + RightButtonDoubleClicked { x: isize, y: isize }, + MovedBy { x: isize, y: isize }, + CursorLocked, + CursorReleased, } diff --git a/pixlib_parser/src/classes/object.rs b/pixlib_parser/src/classes/object.rs index 762c5f9..666ac8a 100644 --- a/pixlib_parser/src/classes/object.rs +++ b/pixlib_parser/src/classes/object.rs @@ -39,9 +39,18 @@ impl CnvObjectBuilder { } } - pub fn add_property(&mut self, property: String, value: String) -> &mut Self { - self.properties.insert(property, value); // TODO: report duplicates - self + pub fn add_property( + &mut self, + property: String, + value: String, + ) -> Result<&mut Self, ObjectBuilderError> { + if let Some(old_value) = self.properties.insert(property.clone(), value) { + return Err(ObjectBuilderError::new( + self.name.clone(), + ObjectBuildErrorKind::PropertyAlreadyPresent(property, old_value), + )); + }; + Ok(self) } pub fn build(self) -> Result, ObjectBuilderError> { @@ -101,6 +110,8 @@ impl Issue for ObjectBuilderError { pub enum ObjectBuildErrorKind { #[error("Missing type property")] MissingType, + #[error("Property {0} already present with value {1}")] + PropertyAlreadyPresent(String, String), #[error("Parsing error: {0}")] ParsingError(TypeParsingError), #[error("Parser issue: {0}")] @@ -139,18 +150,24 @@ impl CnvObject { arguments: &[CnvValue], context: Option, ) -> RunnerResult> { - // println!("Calling method: {:?} of: {:?}", identifier, self.name); let context = context .map(|c| c.with_current_object(self.clone())) .unwrap_or(RunnerContext::new_minimal(&self.parent.runner, self)); - self.content.borrow().call_method( - identifier, - &arguments + let arguments = if matches!(identifier, CallableIdentifier::Method(_)) { + arguments .iter() .map(|v| v.to_owned().resolve(context.clone())) - .collect::>(), - context, - ) + .collect::>() + } else { + arguments.to_owned() + }; + println!( + "Calling method: {:?} of: {:?} with arguments: {:?}", + identifier, self.name, arguments + ); + self.content + .borrow() + .call_method(identifier, &arguments, context) // println!("Result is {:?}", result); } diff --git a/pixlib_parser/src/classes/sound.rs b/pixlib_parser/src/classes/sound.rs index e368f7b..dfe4afa 100644 --- a/pixlib_parser/src/classes/sound.rs +++ b/pixlib_parser/src/classes/sound.rs @@ -257,6 +257,38 @@ impl SoundState { .use_and_drop_mut(|events| { events.push_back(SoundEvent::SoundStarted(self.file_data.clone())) }); + context + .runner + .internal_events + .borrow_mut() + .use_and_drop_mut(|events| { + events.push_back(InternalEvent { + object: context.current_object.clone(), + callable: CallableIdentifier::Event("ONSTARTED").to_owned(), + arguments: Vec::new(), + }) + }); + // FIXME: short-circuiting + self.is_playing = false; + context + .runner + .events_out + .sound + .borrow_mut() + .use_and_drop_mut(|events| { + events.push_back(SoundEvent::SoundStopped(self.file_data.clone())) + }); + context + .runner + .internal_events + .borrow_mut() + .use_and_drop_mut(|events| { + events.push_back(InternalEvent { + object: context.current_object.clone(), + callable: CallableIdentifier::Event("ONFINISHED").to_owned(), + arguments: Vec::new(), + }) + }); Ok(()) } diff --git a/pixlib_parser/src/classes/timer.rs b/pixlib_parser/src/classes/timer.rs index 3646eeb..70bc5bc 100644 --- a/pixlib_parser/src/classes/timer.rs +++ b/pixlib_parser/src/classes/timer.rs @@ -18,7 +18,7 @@ pub struct TimerProperties { pub on_done: Option>, // ONDONE signal pub on_init: Option>, // ONINIT signal pub on_signal: Option>, // ONSIGNAL signal - pub on_tick: Option>, // ONTICK signal + pub on_tick: HashMap>, // ONTICK signal } #[derive(Debug, Clone, Default)] @@ -38,16 +38,18 @@ pub struct TimerEventHandlers { pub on_done: Option>, // ONDONE signal pub on_init: Option>, // ONINIT signal pub on_signal: Option>, // ONSIGNAL signal - pub on_tick: Option>, // ONTICK signal + pub on_tick: HashMap>, // ONTICK signal } impl EventHandler for TimerEventHandlers { - fn get(&self, name: &str, _argument: Option<&str>) -> Option<&Arc> { + fn get(&self, name: &str, argument: Option<&str>) -> Option<&Arc> { match name { "ONDONE" => self.on_done.as_ref(), "ONINIT" => self.on_init.as_ref(), "ONSIGNAL" => self.on_signal.as_ref(), - "ONTICK" => self.on_tick.as_ref(), + "ONTICK" => argument + .and_then(|a| self.on_tick.get(a)) + .or(self.on_tick.get("")), _ => None, } } @@ -182,11 +184,14 @@ impl CnvType for Timer { .and_then(discard_if_empty) .map(parse_event_handler) .transpose()?; - let on_tick = properties - .remove("ONTICK") - .and_then(discard_if_empty) - .map(parse_event_handler) - .transpose()?; + let mut on_tick = HashMap::new(); + for (k, v) in properties.iter() { + if k == "ONTICK" { + on_tick.insert(String::from(""), parse_event_handler(v.to_owned())?); + } else if let Some(argument) = k.strip_prefix("ONTICK^") { + on_tick.insert(String::from(argument), parse_event_handler(v.to_owned())?); + } + } Ok(CnvContent::Timer(Self::from_initial_properties( parent, TimerProperties { @@ -295,7 +300,7 @@ impl TimerState { events.push_back(InternalEvent { object: timer.parent.clone(), callable: CallableIdentifier::Event("ONTICK").to_owned(), - arguments: Vec::new(), + arguments: vec![CnvValue::Integer(self.current_ticks as i32)], }) }); } diff --git a/pixlib_parser/src/runner/events.rs b/pixlib_parser/src/runner/events.rs index 9fb2e2d..7491d9c 100644 --- a/pixlib_parser/src/runner/events.rs +++ b/pixlib_parser/src/runner/events.rs @@ -21,8 +21,11 @@ pub enum TimerEvent { #[derive(Debug, Clone)] pub enum MouseEvent { - LeftButtonPressed { x: u32, y: u32 }, - RightButtonPressed { x: u32, y: u32 }, + MovedTo { x: isize, y: isize }, + LeftButtonPressed, + LeftButtonReleased, + RightButtonPressed, + RightButtonReleased, } pub use keyboard_types::Code as KeyboardKey; diff --git a/pixlib_parser/src/runner/mod.rs b/pixlib_parser/src/runner/mod.rs index 53e2d1f..8a7ae44 100644 --- a/pixlib_parser/src/runner/mod.rs +++ b/pixlib_parser/src/runner/mod.rs @@ -33,7 +33,10 @@ use std::{cell::RefCell, collections::HashMap, sync::Arc}; use events::{IncomingEvents, OutgoingEvents}; use crate::classes::Animation; +use crate::classes::CallableIdentifier; use crate::classes::CnvContent; +use crate::classes::InternalMouseEvent; +use crate::classes::Mouse; use crate::classes::Scene; use crate::common::DroppableRefMut; use crate::common::IssueKind; @@ -124,9 +127,18 @@ pub enum RunnerError { IoError { source: std::io::Error, }, + ObjectBuilderError { + source: ObjectBuilderError, + }, Other, } +impl From for RunnerError { + fn from(value: ObjectBuilderError) -> Self { + Self::ObjectBuilderError { source: value } + } +} + pub type RunnerResult = std::result::Result; #[derive(Clone)] @@ -231,11 +243,11 @@ impl RunnerContext { #[allow(clippy::arc_with_non_send_sync)] impl CnvRunner { - pub fn new( + pub fn try_new( filesystem: Arc>, game_paths: Arc, issue_manager: IssueManager, - ) -> Arc { + ) -> RunnerResult> { let runner = Arc::new(Self { scripts: RefCell::new(ScriptContainer::default()), filesystem, @@ -258,46 +270,139 @@ impl CnvRunner { runner .global_objects .borrow_mut() - .use_and_drop_mut(|objects| { + .use_and_drop_mut::>(|objects| { + let mut range = 0usize..; objects .push_object({ let mut builder = CnvObjectBuilder::new( Arc::clone(&global_script), "RANDOM".to_owned(), - 0, + range.next().unwrap(), ); - builder.add_property("TYPE".into(), "RAND".to_owned()); + builder.add_property("TYPE".into(), "RAND".to_owned())?; builder.build().unwrap() }) - .unwrap() - }); - runner + .unwrap(); + objects + .push_object({ + let mut builder = CnvObjectBuilder::new( + Arc::clone(&global_script), + "KEYBOARD".to_owned(), + range.next().unwrap(), + ); + builder.add_property("TYPE".into(), "KEYBOARD".to_owned())?; + builder.build().unwrap() + }) + .unwrap(); + objects + .push_object({ + let mut builder = CnvObjectBuilder::new( + Arc::clone(&global_script), + "MOUSE".to_owned(), + range.next().unwrap(), + ); + builder.add_property("TYPE".into(), "MOUSE".to_owned())?; + builder.build().unwrap() + }) + .unwrap(); + objects + .push_object({ + let mut builder = CnvObjectBuilder::new( + Arc::clone(&global_script), + "SYSTEM".to_owned(), + range.next().unwrap(), + ); + builder.add_property("TYPE".into(), "SYSTEM".to_owned())?; + builder.build().unwrap() + }) + .unwrap(); + Ok(()) + })?; + Ok(runner) } pub fn step(self: &Arc) -> RunnerResult<()> { - let mut timer_events = self.events_in.timer.borrow_mut(); - while let Some(evt) = timer_events.pop_front() { - match evt { - TimerEvent::Elapsed { seconds } => { - let mut buffer = Vec::new(); - self.find_objects( - |o| matches!(&*o.content.borrow(), CnvContent::Animation(_)), - &mut buffer, - ); - for animation_object in buffer { - let guard = animation_object.content.borrow(); - let animation: Option<&Animation> = (&*guard).into(); - let animation = animation.unwrap(); - animation.step(seconds)?; + self.events_in + .timer + .borrow_mut() + .use_and_drop_mut::>(|events| { + while let Some(evt) = events.pop_front() { + match evt { + TimerEvent::Elapsed { seconds } => { + let mut buffer = Vec::new(); + self.find_objects( + |o| matches!(&*o.content.borrow(), CnvContent::Animation(_)), + &mut buffer, + ); + for animation_object in buffer { + let guard = animation_object.content.borrow(); + let animation: Option<&Animation> = (&*guard).into(); + let animation = animation.unwrap(); + animation.step(seconds)?; + } + } } } - } - } + Ok(()) + })?; + self.events_in + .mouse + .borrow_mut() + .use_and_drop_mut::>(|events| { + while let Some(evt) = events.pop_front() { + // eprintln!("Handling incoming mouse event: {:?}", evt); + Mouse::handle_incoming_event(evt)?; + } + Ok(()) + })?; let mut to_init = Vec::new(); self.find_objects(|o| !*o.initialized.borrow(), &mut to_init); for object in to_init { object.init(None)?; } + let mut mouse_objects = Vec::new(); + self.find_objects( + |o| matches!(*o.content.borrow(), CnvContent::Mouse(_)), + &mut mouse_objects, + ); + self.internal_events + .borrow_mut() + .use_and_drop_mut::>(|internal_events| { + Mouse::handle_outgoing_events(|mouse_event| { + eprintln!("Handling internal mouse event: {:?}", mouse_event); + let callable = CallableIdentifier::Event(match mouse_event { + InternalMouseEvent::LeftButtonPressed { .. } + | InternalMouseEvent::RightButtonPressed { .. } => "ONCLICK", + InternalMouseEvent::LeftButtonReleased { .. } + | InternalMouseEvent::RightButtonReleased { .. } => "ONRELEASE", + InternalMouseEvent::LeftButtonDoubleClicked { .. } + | InternalMouseEvent::RightButtonDoubleClicked { .. } => "ONDBLCLICK", + InternalMouseEvent::MovedBy { .. } => "ONMOVE", + _ => return Ok(()), + }); + let arguments = match mouse_event { + InternalMouseEvent::LeftButtonPressed { .. } + | InternalMouseEvent::LeftButtonReleased { .. } + | InternalMouseEvent::LeftButtonDoubleClicked { .. } => { + vec![CnvValue::String("LEFT".into())] + } + InternalMouseEvent::RightButtonPressed { .. } + | InternalMouseEvent::RightButtonReleased { .. } + | InternalMouseEvent::RightButtonDoubleClicked { .. } => { + vec![CnvValue::String("RIGHT".into())] + } + _ => Vec::new(), + }; + for mouse_object in mouse_objects.iter() { + internal_events.push_back(InternalEvent { + object: Arc::clone(mouse_object), + callable: callable.to_owned(), + arguments: arguments.clone(), + }) + } + Ok(()) + }) + })?; while let Some(evt) = self .internal_events .borrow_mut() @@ -345,7 +450,7 @@ impl CnvRunner { CnvDeclaration::PropertyAssignment { parent, property, - property_key: _property_key, + property_key, value, } => { let Some(obj) = name_to_object @@ -357,7 +462,12 @@ impl CnvRunner { &parent, &objects ); }; - obj.add_property(property, value); + obj.add_property( + property_key + .map(|suffix| property.clone() + "^" + &suffix) + .unwrap_or(property), + value, + )?; } } } diff --git a/pixlib_parser/src/runner/tests.rs b/pixlib_parser/src/runner/tests.rs index a4b758e..292f2d8 100644 --- a/pixlib_parser/src/runner/tests.rs +++ b/pixlib_parser/src/runner/tests.rs @@ -21,11 +21,12 @@ fn surrounding_quotes_should_be_handled_correctly_with_direct_set( argument: &str, expected: &str, ) { - let runner = CnvRunner::new( + let runner = CnvRunner::try_new( Arc::new(RefCell::new(DummyFileSystem)), Default::default(), Default::default(), - ); + ) + .unwrap(); let script = format!( r" OBJECT=TESTSTR @@ -75,11 +76,12 @@ fn surrounding_quotes_should_be_handled_correctly_with_direct_set_and_inconvenie argument: &str, expected: &str, ) { - let runner = CnvRunner::new( + let runner = CnvRunner::try_new( Arc::new(RefCell::new(DummyFileSystem)), Default::default(), Default::default(), - ); + ) + .unwrap(); let script = format!( r" OBJECT=TESTSTR @@ -141,11 +143,12 @@ fn surrounding_quotes_should_be_handled_correctly_with_one_level_indirect_set( argument: &str, expected: &str, ) { - let runner = CnvRunner::new( + let runner = CnvRunner::try_new( Arc::new(RefCell::new(DummyFileSystem)), Default::default(), Default::default(), - ); + ) + .unwrap(); let script = format!( r" OBJECT=TESTSTR @@ -199,11 +202,12 @@ fn surrounding_quotes_should_be_handled_correctly_with_two_level_indirect_set_an argument: &str, expected: &str, ) { - let runner = CnvRunner::new( + let runner = CnvRunner::try_new( Arc::new(RefCell::new(DummyFileSystem)), Default::default(), Default::default(), - ); + ) + .unwrap(); let script = format!( r" OBJECT=TESTSTR