From 9523fd86524dd75e165c0c93d09a32cb305058a8 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 21 Feb 2024 21:18:02 -0800 Subject: [PATCH] Menus/Controls: add Bind Ben - Hold right to scroll binding text into Bind Ben - Release right to cancel - When the text is done scrolling, Bind Ben begins chewing - Release right to have Bind Ben swallow - After Bind Ben swallows the binding text, he does a cute pose and the control is cleared - Press up or down to skip the swallow and pose animations and clear the control immediately - Press C to have Bind Ben quickly eat the binding text and clear the control --- src/k_menu.h | 3 + src/k_menudraw.c | 111 ++++++++++++++++++++- src/menus/options-profiles-edit-controls.c | 45 +++++++++ 3 files changed, 156 insertions(+), 3 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 5218f2cd9..e7ce4cebd 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -966,6 +966,7 @@ struct modedesc_t #define MAXCOLUMNMODES 12 //max modes displayed in one column #define MAXMODEDESCS (MAXCOLUMNMODES*3) #define M_OPTIONS_OFSTIME 5 +#define M_OPTIONS_BINDBEN_QUICK 106 // Keep track of some options properties extern struct optionsmenu_s { @@ -998,6 +999,8 @@ extern struct optionsmenu_s { INT16 controlscroll; // scrolling for the control menu.... UINT8 bindcontrol; // 0: not binding, 1: binding control #1, 2: binding control #2 INT16 bindtimer; // Timer until binding is cancelled (5s) + UINT16 bindben; // Hold right timer + UINT8 bindben_swallow; // (bool) control is about to be cleared; (int) swallow/pose animation timer INT16 trycontroller; // Starts at 3*TICRATE, holding B lowers this, when at 0, cancel controller try mode. diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 72baa8aed..375d1c61c 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4785,6 +4785,74 @@ static const char *M_GetDPadPatchName(SINT8 ud, SINT8 lr) } } +static void M_DrawBindBen(INT32 x, INT32 y, INT32 scroll_remaining) +{ + // optionsmenu.bindben_swallow + const int pose_time = 30; + const int swallow_time = pose_time + 14; + + // Lid closed + int state = 'A'; + int frame = 0; + + if (optionsmenu.bindben_swallow > 100) + { + // Quick swallow (C button) + state = 'C'; + + int t = 106 - optionsmenu.bindben_swallow; + if (t < 3) + frame = 0; + else + frame = t - 3; + } + else if (scroll_remaining <= 0) + { + // Chewing (text done scrolling) + state = 'B'; + frame = I_GetTime() / 2 % 4; + + // When state changes from 'lid open' to 'chewing', + // play chomp sound. + if (!optionsmenu.bindben_swallow) + S_StartSound(NULL, sfx_monch); + + // Ready to swallow when button is released. + optionsmenu.bindben_swallow = swallow_time + 1; + } + else if (optionsmenu.bindben) + { + // Lid open (text scrolling) + frame = 1; + } + else if (optionsmenu.bindben_swallow) + { + if (optionsmenu.bindben_swallow > pose_time) + { + // Swallow + state = 'C'; + + int t = swallow_time - optionsmenu.bindben_swallow; + if (t < 8) + frame = 0; + else + frame = 1 + (t - 8) / 2 % 3; + } + else + { + // Pose + state = 'D'; + + int t = pose_time - optionsmenu.bindben_swallow; + if (t < 10) + frame = 0; + else + frame = 1 + (t - 10) / 4 % 5; + } + } + + V_DrawMappedPatch(x-30, y, 0, W_CachePatchName(va("PR_BIN%c%c", state, '1' + frame), PU_CACHE), aquamap); +} // the control stuff. // Dear god. @@ -5008,6 +5076,9 @@ void M_DrawProfileControls(void) } } + INT32 bindx = x; + INT32 benx = 142; + INT32 beny = y - 8; if (i == itemOn) { // Extend yellow wedge down behind @@ -5016,12 +5087,46 @@ void M_DrawProfileControls(void) { for (j=24; j < 34; j++) V_DrawFill(0, (y)+j, 128+j, 1, 73); + benx += 10; + beny += 10; + } + + // Scroll text into Bind Ben. + bindx += optionsmenu.bindben * 3; + + if (buf2[0]) + { + // Bind Ben: suck characters off + // the end of the first line onto + // the beginning of the second + // line. + UINT16 n = strlen(buf); + UINT16 t = min(optionsmenu.bindben, n); + memmove(&buf2[t], buf2, t + 1); + memcpy(buf2, &buf[n - t], t); + buf[n - t] = '\0'; } } - // don't shift the text if we didn't draw a patch. - V_DrawThinString(x + (drawnpatch ? 13 : 1), y + 12, vflags, buf); - V_DrawThinString(x + (drawnpatch ? 13 : 1), y + 22, vflags, buf2); + { + cliprect_t clip; + V_SaveClipRect(&clip); // preserve cliprect for tooltip + + // Clip text as it scrolls into Bind Ben. + V_SetClipRect(0, 0, (benx-14)*FRACUNIT, 200*FRACUNIT, 0); + + if (i != itemOn || !optionsmenu.bindben_swallow) + { + // don't shift the text if we didn't draw a patch. + V_DrawThinString(bindx + (drawnpatch ? 13 : 1), y + 12, vflags, buf); + V_DrawThinString(bindx + (drawnpatch ? 13 : 1), y + 22, vflags, buf2); + } + + V_RestoreClipRect(&clip); + } + + if (i == itemOn) + M_DrawBindBen(benx, beny, (benx-14) - bindx); // controller dest coords: if (itemOn == i && gc > 0 && gc <= gc_start) diff --git a/src/menus/options-profiles-edit-controls.c b/src/menus/options-profiles-edit-controls.c index 54d6c8c66..7353d4341 100644 --- a/src/menus/options-profiles-edit-controls.c +++ b/src/menus/options-profiles-edit-controls.c @@ -155,6 +155,7 @@ static boolean M_ClearCurrentControl(void) void M_HandleProfileControls(void) { + const UINT8 pid = 0; UINT8 maxscroll = currentMenu->numitems - 5; M_OptionsTick(); @@ -184,6 +185,35 @@ void M_HandleProfileControls(void) } } + else if (currentMenu->menuitems[itemOn].mvar1) // check if we're on a valid menu option... + { + // Hold right to begin clearing the control. + // + // If bindben timer increases enough, bindben_swallow + // will be set. + // This is a commitment to clear the control. + // You can keep holding right to postpone the clear + // but once you let go, you are locked out of + // pressing it again until the animation finishes. + if (menucmd[pid].dpad_lr > 0 && (optionsmenu.bindben || !optionsmenu.bindben_swallow)) + { + optionsmenu.bindben++; + } + else + { + optionsmenu.bindben = 0; + + if (optionsmenu.bindben_swallow) + { + optionsmenu.bindben_swallow--; + + if (optionsmenu.bindben_swallow == 100) // special countdown for the "quick" animation + optionsmenu.bindben_swallow = 0; + else if (!optionsmenu.bindben_swallow) // long animation, clears control when done + M_ClearCurrentControl(); + } + } + } } void M_ProfileTryController(INT32 choice) @@ -287,6 +317,8 @@ boolean M_ProfileControlsInputs(INT32 ch) { if (M_ClearCurrentControl()) S_StartSound(NULL, sfx_monch); + optionsmenu.bindben = 0; + optionsmenu.bindben_swallow = M_OPTIONS_BINDBEN_QUICK; M_SetMenuDelay(pid); return true; } @@ -297,6 +329,19 @@ boolean M_ProfileControlsInputs(INT32 ch) return true; } + if (menucmd[pid].dpad_ud) + { + if (optionsmenu.bindben_swallow) + { + // Control would be cleared, but we're + // interrupting the animation so clear it + // immediately. + M_ClearCurrentControl(); + } + optionsmenu.bindben = 0; + optionsmenu.bindben_swallow = 0; + } + return false; }