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
This commit is contained in:
James R 2024-02-21 21:18:02 -08:00
parent 0766e8823b
commit 9523fd8652
3 changed files with 156 additions and 3 deletions

View file

@ -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.

View file

@ -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';
}
}
{
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(x + (drawnpatch ? 13 : 1), y + 12, vflags, buf);
V_DrawThinString(x + (drawnpatch ? 13 : 1), y + 22, vflags, buf2);
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)

View file

@ -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;
}