From 90cd4dc88df3f891ecaa0ee9b9010fe432154503 Mon Sep 17 00:00:00 2001 From: Holger Schemel Date: Sat, 18 Apr 2020 00:14:20 +0200 Subject: [PATCH] added mouse click events to CE change events (experimental) This addition is sort of fundamental to the R'n'D game engine in that it extends the previous limitation of game engine input to only four direction and two buttons, now also handling mouse clicks to elements in the playfield as configurable custom element change events. In contrast to the Mirror Magic game engine (which already handles mouse input), recording mouse click events to tapes still has to be implemented. --- src/editor.c | 16 ++++++++++++++++ src/events.c | 41 +++++++++++++++++++++++++++-------------- src/files.c | 15 +++++++++++++++ src/game.c | 22 ++++++++++++++++++++++ src/main.h | 12 +++++++++++- 5 files changed, 91 insertions(+), 15 deletions(-) diff --git a/src/editor.c b/src/editor.c index f648853f..838a898d 100644 --- a/src/editor.c +++ b/src/editor.c @@ -1965,6 +1965,10 @@ static struct ValueTextInfo options_change_direct_action[] = #endif { CE_VALUE_GETS_ZERO, "CE value gets 0" }, { CE_SCORE_GETS_ZERO, "CE score gets 0" }, + { CE_UNDEFINED, " " }, + { CE_HEADLINE_SPECIAL_EVENTS, "[mouse events]" }, + { CE_CLICKED_BY_MOUSE, "clicked by mouse" }, + { CE_PRESSED_BY_MOUSE, "pressed by mouse" }, { -1, NULL } }; @@ -1994,6 +1998,10 @@ static struct ValueTextInfo options_change_other_action[] = { CE_SCORE_CHANGES_OF_X, "CE score changes of" }, { CE_VALUE_GETS_ZERO_OF_X, "CE value gets 0 of" }, { CE_SCORE_GETS_ZERO_OF_X, "CE score gets 0 of" }, + { CE_UNDEFINED, " " }, + { CE_HEADLINE_SPECIAL_EVENTS, "[mouse events]" }, + { CE_MOUSE_CLICKED_ON_X, "mouse clicked on" }, + { CE_MOUSE_PRESSED_ON_X, "mouse pressed on" }, { -1, NULL } }; @@ -8144,6 +8152,8 @@ static void CopyCustomElementPropertiesToEditor(int element) HAS_CHANGE_EVENT(element, CE_SCORE_CHANGES) ? CE_SCORE_CHANGES : HAS_CHANGE_EVENT(element, CE_VALUE_GETS_ZERO) ? CE_VALUE_GETS_ZERO : HAS_CHANGE_EVENT(element, CE_SCORE_GETS_ZERO) ? CE_SCORE_GETS_ZERO : + HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ? CE_CLICKED_BY_MOUSE : + HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ? CE_PRESSED_BY_MOUSE : custom_element_change.direct_action); // set "change by other element action" selectbox help value @@ -8171,6 +8181,8 @@ static void CopyCustomElementPropertiesToEditor(int element) HAS_CHANGE_EVENT(element, CE_SCORE_CHANGES_OF_X) ? CE_SCORE_CHANGES_OF_X : HAS_CHANGE_EVENT(element, CE_VALUE_GETS_ZERO_OF_X) ? CE_VALUE_GETS_ZERO_OF_X : HAS_CHANGE_EVENT(element, CE_SCORE_GETS_ZERO_OF_X) ? CE_SCORE_GETS_ZERO_OF_X : + HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ? CE_MOUSE_CLICKED_ON_X : + HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X) ? CE_MOUSE_PRESSED_ON_X : custom_element_change.other_action); } @@ -8302,6 +8314,8 @@ static void CopyCustomElementPropertiesToGame(int element) custom_element_change_events[CE_SCORE_CHANGES] = FALSE; custom_element_change_events[CE_VALUE_GETS_ZERO] = FALSE; custom_element_change_events[CE_SCORE_GETS_ZERO] = FALSE; + custom_element_change_events[CE_CLICKED_BY_MOUSE] = FALSE; + custom_element_change_events[CE_PRESSED_BY_MOUSE] = FALSE; custom_element_change_events[custom_element_change.direct_action] = custom_element_change_events[CE_BY_DIRECT_ACTION]; @@ -8329,6 +8343,8 @@ static void CopyCustomElementPropertiesToGame(int element) custom_element_change_events[CE_SCORE_CHANGES_OF_X] = FALSE; custom_element_change_events[CE_VALUE_GETS_ZERO_OF_X] = FALSE; custom_element_change_events[CE_SCORE_GETS_ZERO_OF_X] = FALSE; + custom_element_change_events[CE_MOUSE_CLICKED_ON_X] = FALSE; + custom_element_change_events[CE_MOUSE_PRESSED_ON_X] = FALSE; custom_element_change_events[custom_element_change.other_action] = custom_element_change_events[CE_BY_OTHER_ACTION]; diff --git a/src/events.c b/src/events.c index 6def0cb6..2b553720 100644 --- a/src/events.c +++ b/src/events.c @@ -94,6 +94,25 @@ static int FilterEvents(const Event *event) ((MotionEvent *)event)->y -= video.screen_yoffset; } + if (event->type == EVENT_BUTTONPRESS || + event->type == EVENT_BUTTONRELEASE || + event->type == EVENT_MOTIONNOTIFY) + { + // do not reset mouse cursor before all pending events have been processed + if (gfx.cursor_mode == cursor_mode_last && + ((game_status == GAME_MODE_TITLE && + gfx.cursor_mode == CURSOR_NONE) || + (game_status == GAME_MODE_PLAYING && + gfx.cursor_mode == CURSOR_PLAYFIELD))) + { + SetMouseCursor(CURSOR_DEFAULT); + + DelayReached(&special_cursor_delay, 0); + + cursor_mode_last = CURSOR_DEFAULT; + } + } + // non-motion events are directly passed to event handler functions if (event->type != EVENT_MOTIONNOTIFY) return 1; @@ -111,20 +130,6 @@ static int FilterEvents(const Event *event) gfx.mouse_x = motion->x; gfx.mouse_y = motion->y; - // do no reset mouse cursor before all pending events have been processed - if (gfx.cursor_mode == cursor_mode_last && - ((game_status == GAME_MODE_TITLE && - gfx.cursor_mode == CURSOR_NONE) || - (game_status == GAME_MODE_PLAYING && - gfx.cursor_mode == CURSOR_PLAYFIELD))) - { - SetMouseCursor(CURSOR_DEFAULT); - - DelayReached(&special_cursor_delay, 0); - - cursor_mode_last = CURSOR_DEFAULT; - } - // skip mouse motion events without pressed button outside level editor if (button_status == MB_RELEASED && game_status != GAME_MODE_EDITOR && game_status != GAME_MODE_PLAYING) @@ -322,6 +327,10 @@ static void HandleMouseCursor(void) { // when playing, display a special mouse pointer inside the playfield + // display normal pointer if mouse pressed + if (button_status != MB_RELEASED) + DelayReached(&special_cursor_delay, 0); + if (gfx.cursor_mode != CURSOR_PLAYFIELD && cursor_inside_playfield && DelayReached(&special_cursor_delay, special_cursor_delay_value)) @@ -1348,6 +1357,8 @@ static void HandleButtonOrFinger_FollowFinger(int mx, int my, int button) static void HandleButtonOrFinger(int mx, int my, int button) { + boolean valid_mouse_event = (mx != -1 && my != -1 && button != -1); + if (game_status != GAME_MODE_PLAYING) return; @@ -1364,6 +1375,8 @@ static void HandleButtonOrFinger(int mx, int my, int button) { if (strEqual(setup.touch.control_type, TOUCH_CONTROL_FOLLOW_FINGER)) HandleButtonOrFinger_FollowFinger(mx, my, button); + else if (level.has_mouse_events && valid_mouse_event) + SetPlayerMouseAction(mx, my, button); } } diff --git a/src/files.c b/src/files.c index 54835e28..fa807e8f 100644 --- a/src/files.c +++ b/src/files.c @@ -1733,6 +1733,9 @@ static void setLevelInfoToDefaults_Level(struct LevelInfo *level) // set all bug compatibility flags to "false" => do not emulate this bug level->use_action_after_change_bug = FALSE; + // other flags that may be set due to certain level properties + level->has_mouse_events = FALSE; + if (leveldir_current) { // try to determine better author name than 'anonymous' @@ -6561,6 +6564,18 @@ static void LoadLevel_InitCustomElements(struct LevelInfo *level) element_info[element].ignition_delay = 8; } } + + // check for custom elements which have mouse click events defined + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + + if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || + HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) || + HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) || + HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X)) + level->has_mouse_events = TRUE; + } } static void LoadLevel_InitElements(struct LevelInfo *level) diff --git a/src/game.c b/src/game.c index a82ca079..fd1bdc53 100644 --- a/src/game.c +++ b/src/game.c @@ -11687,6 +11687,8 @@ void GameActions_RND_Main(void) void GameActions_RND(void) { + static struct MouseActionInfo mouse_action_last = { 0 }; + struct MouseActionInfo mouse_action = local_player->effective_mouse_action; int magic_wall_x = 0, magic_wall_y = 0; int i, x, y, element, graphic, last_gfx_frame; @@ -11873,6 +11875,24 @@ void GameActions_RND(void) #endif } + if (mouse_action.button) + { + int new_button = (mouse_action.button && mouse_action_last.button == 0); + + x = local_player->mouse_action.lx; + y = local_player->mouse_action.ly; + element = Feld[x][y]; + + if (new_button) + { + CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE); + CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X); + } + + CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE); + CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X); + } + SCAN_PLAYFIELD(x, y) { element = Feld[x][y]; @@ -12214,6 +12234,8 @@ void GameActions_RND(void) // use random number generator in every frame to make it less predictable if (game.engine_version >= VERSION_IDENT(3,1,1,0)) RND(1); + + mouse_action_last = mouse_action; } static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y) diff --git a/src/main.h b/src/main.h index bca6d626..41af237f 100644 --- a/src/main.h +++ b/src/main.h @@ -252,11 +252,18 @@ #define CE_VALUE_CHANGES_OF_X 41 #define CE_SCORE_CHANGES 42 #define CE_SCORE_CHANGES_OF_X 43 +#define CE_CLICKED_BY_MOUSE 44 +#define CE_PRESSED_BY_MOUSE 45 +#define CE_MOUSE_CLICKED_ON_X 46 +#define CE_MOUSE_PRESSED_ON_X 47 -#define NUM_CHANGE_EVENTS 44 +#define NUM_CHANGE_EVENTS 48 #define NUM_CE_BITFIELDS ((NUM_CHANGE_EVENTS + 31) / 32) +#define CE_HEADLINE_SPECIAL_EVENTS 250 +#define CE_UNDEFINED 255 + #define CE_BITMASK_DEFAULT 0 #define CH_EVENT_BITFIELD_NR(e) (e / 32) @@ -3187,6 +3194,9 @@ struct LevelInfo // runtime flags to handle bugs in old levels (not stored in level file) boolean use_action_after_change_bug; + + // runtime flags to indicate level properties (not stored in level file) + boolean has_mouse_events; }; struct NetworkLevelInfo -- 2.34.1