sm64coopdx/src/pc/djui/djui_interactable.c
MysterD 0aa1e04f93 More lua improvements
Added mods folder for lua scripts
Created constants.lua so scripts could use constants internal to the C code
Created event hooks
Separated out lua functions into multiple files
2022-01-16 18:07:45 -08:00

454 lines
17 KiB
C

#include <string.h>
#include "djui.h"
#include "src/pc/controller/controller_sdl.h"
#include "src/pc/controller/controller_mouse.h"
#include "src/pc/controller/controller_keyboard.h"
#include "src/pc/utils/misc.h"
#include "audio_defines.h"
#include "audio/external.h"
enum PadHoldDirection { PAD_HOLD_DIR_NONE, PAD_HOLD_DIR_UP, PAD_HOLD_DIR_DOWN, PAD_HOLD_DIR_LEFT, PAD_HOLD_DIR_RIGHT };
static enum PadHoldDirection sKeyboardHoldDirection = PAD_HOLD_DIR_NONE;
static u16 sKeyboardButtons = 0;
static bool sIgnoreInteractableUntilCursorReleased = false;
struct DjuiBase* gDjuiHovered = NULL;
static struct DjuiBase* sInteractableFocus = NULL;
static struct DjuiBase* sInteractableBinding = NULL;
static struct DjuiBase* sMouseDown = NULL;
bool gInteractableOverridePad = false;
OSContPad gInteractablePad = { 0 };
static OSContPad sLastInteractablePad = { 0 };
static int sLastMouseButtons = 0;
static void djui_interactable_on_click(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_click == NULL) { return; }
base->interactable->on_click(base);
}
static void djui_interactable_on_hover(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_hover == NULL) { return; }
base->interactable->on_hover(base);
}
static void djui_interactable_on_hover_end(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_hover == NULL) { return; }
base->interactable->on_hover_end(base);
}
static void djui_interactable_on_cursor_down_begin(struct DjuiBase* base, bool inputCursor) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_cursor_down_begin == NULL) { return; }
if (gDjuiHovered != NULL) {
djui_interactable_on_hover_end(gDjuiHovered);
gDjuiHovered = NULL;
}
base->interactable->on_cursor_down_begin(base, inputCursor);
}
static void djui_interactable_on_cursor_down(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_cursor_down == NULL) { return; }
base->interactable->on_cursor_down(base);
}
static void djui_interactable_on_cursor_down_end(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_cursor_down_end == NULL) { return; }
base->interactable->on_cursor_down_end(base);
if (djui_cursor_inside_base(base)) {
djui_interactable_on_click(base);
}
}
static void djui_interactable_on_focus_begin(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_focus_begin == NULL) { return; }
base->interactable->on_focus_begin(base);
}
static void djui_interactable_on_focus(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_focus == NULL) { return; }
base->interactable->on_focus(base, &gInteractablePad);
}
static void djui_interactable_on_focus_end(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_focus_end == NULL) { return; }
base->interactable->on_focus_end(base);
}
static void djui_interactable_on_value_change(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_value_change == NULL) { return; }
base->interactable->on_value_change(base);
}
static void djui_interactable_on_bind(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
if (base->interactable->on_bind == NULL) { return; }
base->interactable->on_bind(base);
}
static void djui_interactable_cursor_update_active(struct DjuiBase* base) {
if (!base->visible) { return; }
if (!base->enabled) { return; }
static struct DjuiBase* insideParent = NULL;
if (!djui_cursor_inside_base(base)) { return; }
if (base->interactable != NULL) {
gDjuiHovered = base;
insideParent = base;
} else if (insideParent == NULL) {
gDjuiHovered = NULL;
}
// check all children
struct DjuiBaseChild* child = base->child;
while (child != NULL) {
djui_interactable_cursor_update_active(child->base);
child = child->next;
}
if (insideParent == base) {
insideParent = NULL;
}
}
bool djui_interactable_is_binding(void) {
return sInteractableBinding != NULL;
}
void djui_interactable_set_binding(struct DjuiBase* base) {
sInteractableBinding = base;
djui_cursor_set_visible(base == NULL);
if (base == NULL) {
sIgnoreInteractableUntilCursorReleased = true;
}
}
void djui_interactable_set_input_focus(struct DjuiBase* base) {
djui_interactable_on_focus_end(sInteractableFocus);
sInteractableFocus = base;
djui_interactable_on_focus_begin(base);
djui_cursor_set_visible(base == NULL);
}
bool djui_interactable_is_input_focus(struct DjuiBase* base) {
return sInteractableFocus == base;
}
bool djui_interactable_on_key_down(int scancode) {
if (sInteractableBinding != NULL) {
return true;
}
bool keyFocused = (sInteractableFocus != NULL)
&& (sInteractableFocus->interactable != NULL)
&& (sInteractableFocus->interactable->on_key_down != NULL);
if (keyFocused) {
bool consume = sInteractableFocus->interactable->on_key_down(sInteractableFocus, scancode);
if (consume) {
sKeyboardHoldDirection = PAD_HOLD_DIR_NONE;
sKeyboardButtons = 0;
return true;
}
}
if (scancode == SCANCODE_ESCAPE && djui_panel_is_active()) {
// pressed escape button on keyboard
djui_panel_back();
return true;
}
if (gDjuiChatBox != NULL && !gDjuiChatBoxFocus) {
bool pressChat = false;
for (int i = 0; i < MAX_BINDS; i++) {
if (scancode == (int)configKeyChat[i]) { pressChat = true; }
}
if (pressChat) {
djui_chat_box_toggle();
return true;
}
}
if (gDjuiPlayerList != NULL) {
for (int i = 0; i < MAX_BINDS; i++) {
if (scancode == (int)configKeyPlayerList[i]) {
djui_base_set_visible(&gDjuiPlayerList->base, true);
break;
}
}
}
if (gDjuiChatBoxFocus || djui_panel_is_active()) {
switch (scancode) {
case SCANCODE_UP: sKeyboardHoldDirection = PAD_HOLD_DIR_UP; return true;
case SCANCODE_DOWN: sKeyboardHoldDirection = PAD_HOLD_DIR_DOWN; return true;
case SCANCODE_LEFT: sKeyboardHoldDirection = PAD_HOLD_DIR_LEFT; return true;
case SCANCODE_RIGHT: sKeyboardHoldDirection = PAD_HOLD_DIR_RIGHT; return true;
case SCANCODE_ENTER: sKeyboardButtons |= PAD_BUTTON_A; return true;
}
}
return false;
}
void djui_interactable_on_key_up(int scancode) {
bool keyFocused = (sInteractableFocus != NULL)
&& (sInteractableFocus->interactable != NULL)
&& (sInteractableFocus->interactable->on_key_up != NULL);
if (gDjuiPlayerList != NULL) {
for (int i = 0; i < MAX_BINDS; i++) {
if (scancode == (int)configKeyPlayerList[i]) {
djui_base_set_visible(&gDjuiPlayerList->base, false);
break;
}
}
}
if (keyFocused) {
sInteractableFocus->interactable->on_key_up(sInteractableFocus, scancode);
sKeyboardHoldDirection = PAD_HOLD_DIR_NONE;
sKeyboardButtons = 0;
return;
}
OSContPad* pad = &gInteractablePad;
switch (scancode) {
case SCANCODE_UP: if (sKeyboardHoldDirection == PAD_HOLD_DIR_UP) { sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; pad->stick_y = 0; } break;
case SCANCODE_DOWN: if (sKeyboardHoldDirection == PAD_HOLD_DIR_DOWN) { sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; pad->stick_y = 0; } break;
case SCANCODE_LEFT: if (sKeyboardHoldDirection == PAD_HOLD_DIR_LEFT) { sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; pad->stick_x = 0; } break;
case SCANCODE_RIGHT: if (sKeyboardHoldDirection == PAD_HOLD_DIR_RIGHT) { sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; pad->stick_x = 0; } break;
case SCANCODE_ENTER: sKeyboardButtons &= ~PAD_BUTTON_A; break;
}
}
void djui_interactable_on_text_input(char* text) {
if (sInteractableFocus == NULL) { return; }
if (sInteractableFocus->interactable == NULL) { return; }
if (sInteractableFocus->interactable->on_text_input == NULL) { return; }
sInteractableFocus->interactable->on_text_input(sInteractableFocus, text);
}
void djui_interactable_update_pad(void) {
OSContPad* pad = &gInteractablePad;
pad->button |= sKeyboardButtons;
static enum PadHoldDirection lastPadHoldDirection = PAD_HOLD_DIR_NONE;
static f32 padHoldTimer = 0;
enum PadHoldDirection padHoldDirection = sKeyboardHoldDirection;
if (padHoldDirection != PAD_HOLD_DIR_NONE) {
switch (padHoldDirection) {
case PAD_HOLD_DIR_UP: pad->stick_x = 0; pad->stick_y = -64; break;
case PAD_HOLD_DIR_DOWN: pad->stick_x = 0; pad->stick_y = 64; break;
case PAD_HOLD_DIR_LEFT: pad->stick_x = -64; pad->stick_y = 0; break;
case PAD_HOLD_DIR_RIGHT: pad->stick_x = 64; pad->stick_y = 0; break;
default: break;
}
} else if (pad->stick_x == 0 && pad->stick_y == 0) {
padHoldDirection = PAD_HOLD_DIR_NONE;
} else if (abs(pad->stick_x) > abs(pad->stick_y)) {
padHoldDirection = (pad->stick_x < 0) ? PAD_HOLD_DIR_LEFT : PAD_HOLD_DIR_RIGHT;
} else {
padHoldDirection = (pad->stick_y > 0) ? PAD_HOLD_DIR_UP : PAD_HOLD_DIR_DOWN;
}
bool validPadHold = false;
if (padHoldDirection == PAD_HOLD_DIR_NONE) {
// nothing to do
} else if (padHoldDirection != lastPadHoldDirection) {
padHoldTimer = clock_elapsed() + 0.25f;
validPadHold = true;
} else if (clock_elapsed() > padHoldTimer) {
padHoldTimer = clock_elapsed() + 0.10f;
validPadHold = true;
}
if (validPadHold && sInteractableFocus == NULL) {
switch (padHoldDirection) {
case PAD_HOLD_DIR_UP: djui_cursor_move( 0, -1); break;
case PAD_HOLD_DIR_DOWN: djui_cursor_move( 0, 1); break;
case PAD_HOLD_DIR_LEFT: djui_cursor_move(-1, 0); break;
case PAD_HOLD_DIR_RIGHT: djui_cursor_move( 1, 0); break;
default: break;
}
}
lastPadHoldDirection = padHoldDirection;
}
void djui_interactable_update(void) {
// update pad
djui_interactable_update_pad();
// prevent pressing buttons when they should be ignored
int mouseButtons = mouse_window_buttons;
u16 padButtons = gInteractablePad.button;
if (sIgnoreInteractableUntilCursorReleased) {
if ((padButtons & PAD_BUTTON_A) || (mouseButtons & MOUSE_BUTTON_1)) {
padButtons &= ~PAD_BUTTON_A;
mouseButtons &= ~MOUSE_BUTTON_1;
} else {
sIgnoreInteractableUntilCursorReleased = false;
}
}
// update focused
if (sInteractableFocus) {
u16 mainButtons = PAD_BUTTON_A | PAD_BUTTON_B;
if ((mouseButtons & MOUSE_BUTTON_1) && !(sLastMouseButtons && MOUSE_BUTTON_1) && !djui_cursor_inside_base(sInteractableFocus)) {
// clicked outside of focused
djui_interactable_set_input_focus(NULL);
} else if ((padButtons & mainButtons) && !(sLastInteractablePad.button & mainButtons)) {
// pressed main face button
djui_interactable_set_input_focus(NULL);
} else {
djui_interactable_on_focus(sInteractableFocus);
}
} else if ((padButtons & PAD_BUTTON_B) && !(sLastInteractablePad.button & PAD_BUTTON_B)) {
// pressed back button on controller
djui_panel_back();
} else if ((padButtons & PAD_BUTTON_START) && !(sLastInteractablePad.button & PAD_BUTTON_START)) {
// pressed start button
if (gDjuiPanelPauseCreated) { djui_panel_shutdown(); }
}
if (sInteractableBinding != NULL) {
djui_interactable_on_bind(sInteractableBinding);
} else if ((padButtons & PAD_BUTTON_A) || (mouseButtons & MOUSE_BUTTON_1)) {
// cursor down events
if (gDjuiHovered != NULL) {
sMouseDown = gDjuiHovered;
gDjuiHovered = NULL;
djui_interactable_on_cursor_down_begin(sMouseDown, !mouseButtons);
} else {
djui_interactable_on_cursor_down(sMouseDown);
}
} else {
// cursor up event
if (sMouseDown != NULL) {
djui_interactable_on_cursor_down_end(sMouseDown);
sMouseDown = NULL;
}
struct DjuiBase* lastHovered = gDjuiHovered;
gDjuiHovered = NULL;
djui_interactable_cursor_update_active(&gDjuiRoot->base);
if (lastHovered != gDjuiHovered) {
djui_interactable_on_hover_end(lastHovered);
play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, gDefaultSoundArgs);
}
djui_interactable_on_hover(gDjuiHovered);
}
sLastInteractablePad = gInteractablePad;
sLastMouseButtons = mouseButtons;
}
void djui_interactable_hook_hover(struct DjuiBase* base,
void (*on_hover)(struct DjuiBase*),
void (*on_hover_end)(struct DjuiBase*)) {
struct DjuiInteractable* interactable = base->interactable;
interactable->on_hover = on_hover;
interactable->on_hover_end = on_hover_end;
}
void djui_interactable_hook_cursor_down(struct DjuiBase* base,
void (*on_cursor_down_begin)(struct DjuiBase*, bool),
void (*on_cursor_down)(struct DjuiBase*),
void (*on_cursor_down_end)(struct DjuiBase*)) {
struct DjuiInteractable* interactable = base->interactable;
interactable->on_cursor_down_begin = on_cursor_down_begin;
interactable->on_cursor_down = on_cursor_down;
interactable->on_cursor_down_end = on_cursor_down_end;
}
void djui_interactable_hook_focus(struct DjuiBase* base,
void (*on_focus_begin)(struct DjuiBase*),
void (*on_focus)(struct DjuiBase*, OSContPad*),
void (*on_focus_end)(struct DjuiBase*)) {
struct DjuiInteractable* interactable = base->interactable;
interactable->on_focus_begin = on_focus_begin;
interactable->on_focus = on_focus;
interactable->on_focus_end = on_focus_end;
}
void djui_interactable_hook_click(struct DjuiBase* base,
void (*on_click)(struct DjuiBase*)) {
struct DjuiInteractable* interactable = base->interactable;
interactable->on_click = on_click;
}
void djui_interactable_hook_value_change(struct DjuiBase* base,
void (*on_value_change)(struct DjuiBase*)) {
struct DjuiInteractable* interactable = base->interactable;
interactable->on_value_change = on_value_change;
}
void djui_interactable_hook_bind(struct DjuiBase* base,
void (*on_bind)(struct DjuiBase*)) {
struct DjuiInteractable* interactable = base->interactable;
interactable->on_bind = on_bind;
}
void djui_interactable_hook_key(struct DjuiBase* base,
bool (*on_key_down)(struct DjuiBase*, int),
void (*on_key_up)(struct DjuiBase*, int)) {
struct DjuiInteractable *interactable = base->interactable;
interactable->on_key_down = on_key_down;
interactable->on_key_up = on_key_up;
}
void djui_interactable_hook_text_input(struct DjuiBase *base,
void (*on_text_input)(struct DjuiBase*, char*)) {
struct DjuiInteractable *interactable = base->interactable;
interactable->on_text_input = on_text_input;
}
void djui_interactable_hook_enabled_change(struct DjuiBase *base,
void (*on_enabled_change)(struct DjuiBase*)) {
struct DjuiInteractable *interactable = base->interactable;
interactable->on_enabled_change = on_enabled_change;
}
void djui_interactable_create(struct DjuiBase* base) {
if (base->interactable != NULL) {
free(base->interactable);
}
struct DjuiInteractable* interactable = calloc(1, sizeof(struct DjuiInteractable));
memset(interactable, 0, sizeof(struct DjuiInteractable));
base->interactable = interactable;
}