Merge branch 'more-ui' into 'master'

More UI

See merge request KartKrew/Kart!1280
This commit is contained in:
Oni 2023-06-13 06:33:42 +00:00
commit b6e2609873
23 changed files with 965 additions and 272 deletions

View file

@ -249,12 +249,6 @@ void D_ProcessEvents(void)
HandleGamepadDeviceEvents(ev); HandleGamepadDeviceEvents(ev);
if (gameaction == ga_nothing && gamestate == GS_TITLESCREEN)
{
if (cht_Responder(ev))
continue;
}
if (demo.savemode == DSM_TITLEENTRY) if (demo.savemode == DSM_TITLEENTRY)
{ {
if (G_DemoTitleResponder(ev)) if (G_DemoTitleResponder(ev))
@ -295,14 +289,6 @@ void D_ProcessEvents(void)
if (eaten) if (eaten)
continue; // menu ate the event continue; // menu ate the event
// Demo input:
/*
if (demo.playback)
if (M_DemoResponder(ev))
continue; // demo ate the event
*/
G_Responder(ev); G_Responder(ev);
} }

View file

@ -2585,6 +2585,13 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
//PARAMCHECK(1); //PARAMCHECK(1);
ty = UC_ADDON + offset; ty = UC_ADDON + offset;
} }
else if (fastcmp(params[0], "PASSWORD"))
{
PARAMCHECK(1);
ty = UC_PASSWORD;
stringvar = Z_StrDup(params[1]);
re = -1;
}
else if ((offset=0) || fastcmp(params[0], "AND") else if ((offset=0) || fastcmp(params[0], "AND")
|| (++offset && fastcmp(params[0], "COMMA"))) || (++offset && fastcmp(params[0], "COMMA")))
{ {

View file

@ -4974,6 +4974,8 @@ void G_LoadGameData(void)
gamedata->challengegrid[i] = READUINT16(save.p); gamedata->challengegrid[i] = READUINT16(save.p);
} }
} }
M_SanitiseChallengeGrid();
} }
else else
{ {

View file

@ -4849,7 +4849,7 @@ K_drawMiniPing (void)
void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed) void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed)
{ {
V_DrawFixedPatch(x, y, FRACUNIT, flags, button[pressed], NULL); V_DrawFixedPatch(x, y, FRACUNIT, flags, button[(pressed == true) ? 1 : 0], NULL);
} }
void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic) void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic)

View file

@ -131,8 +131,9 @@ void M_HandlePauseMenuGametype(INT32 choice);
typedef enum typedef enum
{ {
MBF_UD_LR_FLIPPED = 1, // flip up-down and left-right axes MBF_UD_LR_FLIPPED = 1, // flip up-down and left-right axes
MBF_SOUNDLESS = 2, // do not play base menu sounds MBF_SOUNDLESS = 1<<1, // do not play base menu sounds
MBF_NOLOOPENTRIES = 1<<2, // do not loop M_NextOpt/M_PrevOpt
} menubehaviourflags_t; } menubehaviourflags_t;
struct menuitem_t struct menuitem_t
@ -424,6 +425,8 @@ extern menuitem_t MISC_ChallengesStatsDummyMenu[];
extern menu_t MISC_ChallengesDef; extern menu_t MISC_ChallengesDef;
extern menu_t MISC_StatisticsDef; extern menu_t MISC_StatisticsDef;
extern menu_t MISC_WrongWarpDef;
extern menuitem_t MISC_SoundTest[]; extern menuitem_t MISC_SoundTest[];
extern menu_t MISC_SoundTestDef; extern menu_t MISC_SoundTestDef;
@ -1035,6 +1038,7 @@ typedef enum
extras_statistics, extras_statistics,
extras_eggtv, extras_eggtv,
extras_stereo, extras_stereo,
extras_password,
} extras_e; } extras_e;
void M_InitExtras(INT32 choice); // init for the struct void M_InitExtras(INT32 choice); // init for the struct
@ -1098,6 +1102,7 @@ void M_AddonsRefresh(void);
void M_HandleAddons(INT32 choice); void M_HandleAddons(INT32 choice);
char *M_AddonsHeaderPath(void); char *M_AddonsHeaderPath(void);
extern consvar_t cv_dummyaddonsearch; extern consvar_t cv_dummyaddonsearch;
extern consvar_t cv_dummyextraspassword;
void M_Manual(INT32 choice); void M_Manual(INT32 choice);
void M_HandleImageDef(INT32 choice); void M_HandleImageDef(INT32 choice);
@ -1176,6 +1181,10 @@ void M_DrawAddons(void);
#define TILEFLIP_MAX 16 #define TILEFLIP_MAX 16
#define CHAOHOLD_MAX (3*TICRATE/2)
#define CHAOHOLD_BEGIN 7
#define CHAOHOLD_END 3
extern struct timeattackmenu_s { extern struct timeattackmenu_s {
tic_t ticker; // How long the menu's been open for tic_t ticker; // How long the menu's been open for
@ -1201,7 +1210,9 @@ extern struct challengesmenu_s {
boolean pending; boolean pending;
boolean requestnew; boolean requestnew;
boolean chaokeyadd; boolean chaokeyadd;
UINT8 chaokeyhold;
boolean requestflip; boolean requestflip;
@ -1228,6 +1239,23 @@ void M_Statistics(INT32 choice);
void M_DrawStatistics(void); void M_DrawStatistics(void);
boolean M_StatisticsInputs(INT32 ch); boolean M_StatisticsInputs(INT32 ch);
#define MAXWRONGPLAYER MAXSPLITSCREENPLAYERS
#define WRONGPLAYEROFFSCREEN 48
extern struct wrongwarp_s {
INT32 ticker;
tic_t delaytowrongplayer;
struct wrongplayer_s
{
UINT8 skin;
INT16 across;
boolean spinout;
} wrongplayers[MAXWRONGPLAYER];
} wrongwarp;
void M_WrongWarp(INT32 choice);
void M_DrawWrongWarp(void);
typedef enum typedef enum
{ {
stereospecial_none = 0, stereospecial_none = 0,

View file

@ -393,13 +393,13 @@ static void M_DrawMenuTyping(void)
x = (BASEVIDWIDTH - boxwidth)/2; x = (BASEVIDWIDTH - boxwidth)/2;
y = 80; y = 80;
if (menutyping.menutypingfade < 9) if (menutyping.menutypingfade < 9)
y += (9-menutyping.menutypingfade)*10; y += floor(pow(2, (double)(9 - menutyping.menutypingfade)));
else else
y += (9-menutyping.menutypingfade); y += (9-menutyping.menutypingfade);
if (currentMenu->menuitems[itemOn].text) if (currentMenu->menuitems[itemOn].text)
{ {
V_DrawThinString(x + 5, y - 2, highlightflags|V_ALLOWLOWERCASE, currentMenu->menuitems[itemOn].text); V_DrawThinString(x + 5, y - 2, highlightflags|V_6WIDTHSPACE|V_ALLOWLOWERCASE, currentMenu->menuitems[itemOn].text);
} }
M_DrawMenuTooltips(); M_DrawMenuTooltips();
@ -414,8 +414,12 @@ static void M_DrawMenuTyping(void)
V_DrawFill(x + 5 + boxwidth - 8, y + 4 + 5, 1, 8+6, 121); V_DrawFill(x + 5 + boxwidth - 8, y + 4 + 5, 1, 8+6, 121);
V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string); V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
if (skullAnimCounter < 4) if (skullAnimCounter < 4
&& menutyping.menutypingclose == false
&& menutyping.menutypingfade == (menutyping.keyboardtyping ? 9 : 18))
{
V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12 + 1, '_' | 0x80, false); V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12 + 1, '_' | 0x80, false);
}
const INT32 buttonwidth = ((boxwidth + 1)/NUMVIRTUALKEYSINROW); const INT32 buttonwidth = ((boxwidth + 1)/NUMVIRTUALKEYSINROW);
#define BUTTONHEIGHT (11) #define BUTTONHEIGHT (11)
@ -426,7 +430,12 @@ static void M_DrawMenuTyping(void)
if (menutyping.menutypingfade > 9) if (menutyping.menutypingfade > 9)
{ {
y += 36 + 80 + (9-menutyping.menutypingfade)*10; // double yoffs for animation y += 26;
if (menutyping.menutypingfade < 18)
{
y += floor(pow(2, (double)(18 - menutyping.menutypingfade))); // double yoffs for animation
}
INT32 tempkeyboardx = menutyping.keyboardx; INT32 tempkeyboardx = menutyping.keyboardx;
@ -575,14 +584,21 @@ static void M_DrawMenuTyping(void)
#undef BUTTONHEIGHT #undef BUTTONHEIGHT
y = 175;
if (menutyping.menutypingfade < 9)
{
y += 3 * (9 - menutyping.menutypingfade);
}
// Some contextual stuff // Some contextual stuff
if (menutyping.keyboardtyping) if (menutyping.keyboardtyping)
{ {
V_DrawThinString(returnx, 175, V_ALLOWLOWERCASE|V_6WIDTHSPACE|V_GRAYMAP, "Type using your keyboard. Press Enter to confirm & exit.\nUse your controller or any directional input to use the Virtual Keyboard.\n"); V_DrawThinString(returnx, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE|V_GRAYMAP, "Type using your keyboard. Press Enter to confirm & exit.\nUse your controller or any directional input to use the Virtual Keyboard.\n");
} }
else else
{ {
V_DrawThinString(x, 175, V_ALLOWLOWERCASE|V_6WIDTHSPACE|V_GRAYMAP, "Type using the Virtual Keyboard. Use the \'OK\' button to confirm & exit.\nPress any keyboard key not bound to a control to use it."); V_DrawThinString(returnx, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE|V_GRAYMAP, "Type using the Virtual Keyboard. Use the \'OK\' button to confirm & exit.\nPress any keyboard key not bound to a control to use it.");
} }
} }
@ -1390,16 +1406,14 @@ static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
} }
// returns false if the character couldn't be rendered // returns false if the character couldn't be rendered
static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, boolean charflip, boolean animate, INT32 addflags, UINT8 *colormap) static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, UINT8 spr2, UINT8 rotation, UINT32 frame, INT32 addflags, UINT8 *colormap)
{ {
UINT8 spr; UINT8 spr;
spritedef_t *sprdef; spritedef_t *sprdef;
spriteframe_t *sprframe; spriteframe_t *sprframe;
patch_t *sprpatch; patch_t *sprpatch;
UINT8 rotation = (charflip ? 1 : 7);
UINT32 frame = animate ? setup_animcounter : 0;
spr = P_GetSkinSprite2(&skins[skin], SPR2_STIN, NULL); spr = P_GetSkinSprite2(&skins[skin], spr2, NULL);
sprdef = &skins[skin].sprites[spr]; sprdef = &skins[skin].sprites[spr];
if (!sprdef->numframes) // No frames ?? if (!sprdef->numframes) // No frames ??
@ -1511,7 +1525,7 @@ static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y, boolean charflip
color = p->color; color = p->color;
colormap = R_GetTranslationColormap(p->skin, color, GTC_MENUCACHE); colormap = R_GetTranslationColormap(p->skin, color, GTC_MENUCACHE);
M_DrawCharacterSprite(x, y, p->skin, charflip, (p->mdepth == CSSTEP_READY), 0, colormap); M_DrawCharacterSprite(x, y, p->skin, SPR2_STIN, (charflip ? 1 : 7), ((p->mdepth == CSSTEP_READY) ? setup_animcounter : 0), 0, colormap);
} }
static void M_DrawCharSelectPreview(UINT8 num) static void M_DrawCharSelectPreview(UINT8 num)
@ -1880,7 +1894,7 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
if (skinnum >= 0) if (skinnum >= 0)
{ {
if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, colormap)) if (M_DrawCharacterSprite(x-22, y+119, skinnum, SPR2_STIN, 7, 0, 0, colormap))
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap); V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap);
} }
@ -1912,7 +1926,7 @@ static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
(K_FollowerUsable(fln) ? TC_DEFAULT : TC_BLINK), (K_FollowerUsable(fln) ? TC_DEFAULT : TC_BLINK),
col, GTC_MENUCACHE); col, GTC_MENUCACHE);
if (M_DrawCharacterSprite(x-22, y+119, skinnum, false, false, 0, ccolormap)) if (M_DrawCharacterSprite(x-22, y+119, skinnum, SPR2_STIN, 7, 0, 0, ccolormap))
{ {
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], ccolormap); V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], ccolormap);
} }
@ -2711,16 +2725,10 @@ void M_DrawTimeAttack(void)
// SPB Attack control hint + menu overlay // SPB Attack control hint + menu overlay
if (levellist.newgametype == GT_RACE && levellist.levelsearch.timeattack == true && M_SecretUnlocked(SECRET_SPBATTACK, true)) if (levellist.newgametype == GT_RACE && levellist.levelsearch.timeattack == true && M_SecretUnlocked(SECRET_SPBATTACK, true))
{ {
const UINT8 anim_duration = 16;
const UINT8 anim = (timeattackmenu.ticker % (anim_duration * 2)) < anim_duration;
INT32 buttonx = 162 + t; INT32 buttonx = 162 + t;
INT32 buttony = timeheight; INT32 buttony = timeheight;
if (anim) K_drawButtonAnim(buttonx + 35, buttony - 3, V_SNAPTOLEFT, kp_button_r, timeattackmenu.ticker);
V_DrawScaledPatch(buttonx + 35, buttony - 3, V_SNAPTOLEFT, W_CachePatchName("TLB_I", PU_CACHE));
else
V_DrawScaledPatch(buttonx + 35, buttony - 3, V_SNAPTOLEFT, W_CachePatchName("TLB_IB", PU_CACHE));
if ((timeattackmenu.spbflicker == 0 || timeattackmenu.ticker % 2) == (cv_dummyspbattack.value == 1)) if ((timeattackmenu.spbflicker == 0 || timeattackmenu.ticker % 2) == (cv_dummyspbattack.value == 1))
{ {
@ -4378,13 +4386,13 @@ void M_DrawPause(void)
if (smallroundpatch != NULL) if (smallroundpatch != NULL)
{ {
V_DrawMappedPatch( V_DrawMappedPatch(
24, 152, 24, 152 + offset/2,
0, 0,
smallroundpatch, smallroundpatch,
NULL); NULL);
} }
Y_RoundQueueDrawer(&standings, false, false); Y_RoundQueueDrawer(&standings, offset/2, false, false);
} }
} }
@ -5007,7 +5015,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
if (skin != -1) if (skin != -1)
{ {
colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE);
M_DrawCharacterSprite(x, y, skin, false, false, 0, colormap); M_DrawCharacterSprite(x, y, skin, SPR2_STIN, 7, 0, 0, colormap);
for (i = 0; i < skin; i++) for (i = 0; i < skin; i++)
{ {
@ -5065,7 +5073,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
if (skin == -1) if (skin == -1)
skin = 0; skin = 0;
colormap = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_BLACK, GTC_MENUCACHE); colormap = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_BLACK, GTC_MENUCACHE);
M_DrawCharacterSprite(x, y, skin, false, false, 0, colormap); M_DrawCharacterSprite(x, y, skin, SPR2_STIN, 7, 0, 0, colormap);
// Draw follower next to them // Draw follower next to them
if (fskin != -1) if (fskin != -1)
@ -5267,8 +5275,13 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
{ {
x = 8; x = 8;
y = BASEVIDHEIGHT-16; y = BASEVIDHEIGHT-16;
V_DrawGamemodeString(x, y - 32, V_ALLOWLOWERCASE, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_MENUCACHE), cv_alttitle.string); V_DrawGamemodeString(x, y - 33, V_ALLOWLOWERCASE, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_MENUCACHE), cv_alttitle.string);
V_DrawThinString(x, y, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Press (A)");
K_drawButtonAnim(x, y, 0, kp_button_a[1], challengesmenu.ticker);
x += SHORT(kp_button_a[1][0]->width);
V_DrawThinString(x, y + 1, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Toggle");
break; break;
} }
default: default:
@ -5333,7 +5346,9 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
void M_DrawChallenges(void) void M_DrawChallenges(void)
{ {
INT32 x = currentMenu->x, explodex, selectx; const UINT8 pid = 0;
INT32 x = currentMenu->x, explodex, selectx = 0, selecty = 0;
INT32 y; INT32 y;
INT16 i, j; INT16 i, j;
const char *str; const char *str;
@ -5410,6 +5425,7 @@ void M_DrawChallenges(void)
} }
selectx = explodex + (challengesmenu.hilix*challengesgridstep); selectx = explodex + (challengesmenu.hilix*challengesgridstep);
selecty = currentMenu->y + (challengesmenu.hiliy*challengesgridstep);
while (i >= 0 && x >= -(challengesgridstep*2)) while (i >= 0 && x >= -(challengesgridstep*2))
{ {
@ -5447,49 +5463,12 @@ void M_DrawChallenges(void)
challengesmenu.hilix, challengesmenu.hilix,
challengesmenu.hiliy, challengesmenu.hiliy,
selectx, selectx,
currentMenu->y + (challengesmenu.hiliy*challengesgridstep), selecty,
true); true);
M_DrawCharSelectExplosions(false, explodex, currentMenu->y); M_DrawCharSelectExplosions(false, explodex, currentMenu->y);
challengedesc: challengedesc:
// Chao Keys
{
patch_t *key = W_CachePatchName("UN_CHA00", PU_CACHE);
INT32 offs = challengesmenu.unlockcount[CC_CHAONOPE];
if (offs & 1)
offs = -offs;
offs /= 2;
if (gamedata->chaokeys > 9)
{
offs -= 6;
if (gamedata->chaokeys > 99)
offs -= 2; // as far as we can go
}
V_DrawFixedPatch((8+offs)*FRACUNIT, 5*FRACUNIT, FRACUNIT, 0, key, NULL);
V_DrawTimerString((27+offs), 9-challengesmenu.unlockcount[CC_CHAOANIM], 0, va("%u", gamedata->chaokeys));
offs = challengekeybarwidth;
if (gamedata->chaokeys < GDMAX_CHAOKEYS)
offs = ((gamedata->pendingkeyroundoffset * challengekeybarwidth)/GDCONVERT_ROUNDSTOKEY);
if (offs > 0)
V_DrawFill(1, 25, offs, 2, 0);
if (offs < challengekeybarwidth)
V_DrawFadeFill(1+offs, 25, challengekeybarwidth-offs, 2, 0, 31, challengetransparentstrength);
}
// Tally
{
str = va("%d/%d",
challengesmenu.unlockcount[CC_UNLOCKED] + challengesmenu.unlockcount[CC_TALLY],
challengesmenu.unlockcount[CC_TOTAL]
);
V_DrawRightAlignedTimerString(BASEVIDWIDTH-7, 9-challengesmenu.unlockcount[CC_ANIM], 0, str);
}
// Name bar // Name bar
{ {
y = 120; y = 120;
@ -5511,6 +5490,94 @@ challengedesc:
V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str); V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str);
} }
// Tally
{
str = va("%d/%d",
challengesmenu.unlockcount[CC_UNLOCKED] + challengesmenu.unlockcount[CC_TALLY],
challengesmenu.unlockcount[CC_TOTAL]
);
V_DrawRightAlignedTimerString(BASEVIDWIDTH-7, 9-challengesmenu.unlockcount[CC_ANIM], 0, str);
}
// Chao Keys
{
patch_t *key = W_CachePatchName("UN_CHA00", PU_CACHE);
INT32 offs = challengesmenu.unlockcount[CC_CHAONOPE];
if (offs & 1)
offs = -offs;
offs /= 2;
if (gamedata->chaokeys > 9)
{
offs -= 6;
if (gamedata->chaokeys > 99)
offs -= 2; // as far as we can go
}
fixed_t keyx = (8+offs)*FRACUNIT, keyy = 5*FRACUNIT;
const char *timerstr = va("%u", gamedata->chaokeys);
V_DrawTimerString((27+offs), 9-challengesmenu.unlockcount[CC_CHAOANIM], 0, timerstr);
K_drawButton(
(27 + offs + V_TimerStringWidth(timerstr, 0) + 2) << FRACBITS,
11 << FRACBITS,
0, kp_button_c[1],
M_MenuExtraHeld(pid)
);
offs = challengekeybarwidth;
if (gamedata->chaokeys < GDMAX_CHAOKEYS)
offs = ((gamedata->pendingkeyroundoffset * challengekeybarwidth)/GDCONVERT_ROUNDSTOKEY);
if (offs > 0)
V_DrawFill(1, 25, offs, 2, 0);
if (offs < challengekeybarwidth)
V_DrawFadeFill(1+offs, 25, challengekeybarwidth-offs, 2, 0, 31, challengetransparentstrength);
if (challengesmenu.chaokeyhold)
{
fixed_t keyholdrotation = 0, radius = challengesgridstep;
if (challengesmenu.chaokeyhold < CHAOHOLD_BEGIN)
{
radius = (challengesmenu.chaokeyhold*radius)*(FRACUNIT/CHAOHOLD_BEGIN);
keyx += challengesmenu.chaokeyhold*((selectx*FRACUNIT) - keyx)/CHAOHOLD_BEGIN;
keyy += challengesmenu.chaokeyhold*((selecty*FRACUNIT) - keyy)/CHAOHOLD_BEGIN;
}
else
{
if (challengesmenu.chaokeyhold < CHAOHOLD_MAX - CHAOHOLD_END)
{
radius <<= FRACBITS;
keyholdrotation = 360 * ((challengesmenu.chaokeyhold - CHAOHOLD_BEGIN))
* (FRACUNIT/(CHAOHOLD_MAX - (CHAOHOLD_BEGIN + CHAOHOLD_END)));
}
else
{
radius = ((CHAOHOLD_MAX - challengesmenu.chaokeyhold)*radius)*(FRACUNIT/CHAOHOLD_END);
}
keyx = selectx*FRACUNIT;
keyy = selecty*FRACUNIT;
}
if (radius)
{
angle_t ang = (FixedAngle(
keyholdrotation
) >> ANGLETOFINESHIFT) & FINEMASK;
keyx += FixedMul(radius, FINESINE(ang));
keyy -= FixedMul(radius, FINECOSINE(ang));
}
}
V_DrawFixedPatch(keyx, keyy, FRACUNIT, 0, key, NULL);
}
// Derived from M_DrawCharSelectPreview // Derived from M_DrawCharSelectPreview
x = 40; x = 40;
y = BASEVIDHEIGHT-16; y = BASEVIDHEIGHT-16;
@ -5854,9 +5921,150 @@ void M_DrawStatistics(void)
#undef STATSSTEP #undef STATSSTEP
static INT32 M_WrongWarpFallingHelper(INT32 y, INT32 falltime)
{
if (wrongwarp.ticker < falltime)
{
return y;
}
if (wrongwarp.ticker > falltime + 2*TICRATE)
{
return INT32_MAX;
}
if (wrongwarp.ticker < falltime + TICRATE)
{
y += + ((wrongwarp.ticker - falltime) & 1 ? 1 : -1);
return y;
}
y += floor(pow(1.5, (double)(wrongwarp.ticker - (falltime + TICRATE))));
return y;
}
static void M_DrawWrongPlayer(UINT8 i)
{
#define wrongpl wrongwarp.wrongplayers[i]
if (wrongpl.skin >= numskins)
return;
UINT8 *colormap = R_GetTranslationColormap(wrongpl.skin, skins[wrongpl.skin].prefcolor, GTC_MENUCACHE);
M_DrawCharacterSprite(
wrongpl.across,
160 - ((i & 1) ? 0 : 32),
wrongpl.skin,
wrongpl.spinout ? SPR2_SPIN : SPR2_SLWN,
wrongpl.spinout ? ((wrongpl.across/8) & 7) : 6,
(wrongwarp.ticker+i),
0, colormap
);
#undef wrongpl
}
void M_DrawWrongWarp(void)
{
INT32 titleoffset = 0, titlewidth, x, y;
const char *titletext = "WRONG GAME? WRONG GAME! ";
if (gamestate == GS_MENU)
{
patch_t *pat, *pat2;
INT32 animtimer, anim2 = 0;
pat = W_CachePatchName("TITLEBG1", PU_CACHE);
pat2 = W_CachePatchName("TITLEBG2", PU_CACHE);
animtimer = ((wrongwarp.ticker*5)/16) % SHORT(pat->width);
anim2 = SHORT(pat2->width) - (((wrongwarp.ticker*5)/16) % SHORT(pat2->width));
// SRB2Kart: F_DrawPatchCol is over-engineered; recoded to be less shitty and error-prone
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0);
x = -((INT32)animtimer);
y = 0;
while (x < BASEVIDWIDTH)
{
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, pat, NULL);
x += SHORT(pat->width);
}
x = -anim2;
y = BASEVIDHEIGHT - SHORT(pat2->height);
while (x < BASEVIDWIDTH)
{
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, pat2, NULL);
x += SHORT(pat2->width);
}
}
{
patch_t *ttcheckers = W_CachePatchName("TTCHECK", PU_CACHE);
y = FixedMul(40<<FRACBITS, FixedDiv(wrongwarp.ticker%70, 70));
V_DrawSciencePatch(0, -y, 0, ttcheckers, FRACUNIT);
V_DrawSciencePatch(280<<FRACBITS, -(40<<FRACBITS) + y, 0, ttcheckers, FRACUNIT);
y = M_WrongWarpFallingHelper(36, 7*TICRATE/4);
if (y != INT32_MAX)
{
patch_t *ttbanner = W_CachePatchName("TTKBANNR", PU_CACHE);
V_DrawSmallScaledPatch(84, y, 0, ttbanner);
}
y = M_WrongWarpFallingHelper(87, 4*TICRATE/3);
if (y != INT32_MAX)
{
patch_t *ttkart = W_CachePatchName("TTKART", PU_CACHE);
V_DrawSmallScaledPatch(84, y, 0, ttkart);
}
}
if (wrongwarp.ticker < 2*TICRATE/3)
return;
V_DrawFadeScreen(31, min((wrongwarp.ticker - 2*TICRATE/3), 5));
// SMK title screen recreation!?
if (wrongwarp.ticker >= 2*TICRATE)
{
// Done as four calls and not a loop for the sake of render order
M_DrawWrongPlayer(0);
M_DrawWrongPlayer(2);
M_DrawWrongPlayer(1);
M_DrawWrongPlayer(3);
}
y = 20;
x = BASEVIDWIDTH - 8;
if (wrongwarp.ticker < TICRATE)
{
INT32 adjust = floor(pow(2, (double)(TICRATE - wrongwarp.ticker)));
x += adjust/2;
y += adjust;
}
titlewidth = V_LSTitleHighStringWidth(titletext, 0);
titleoffset = (-wrongwarp.ticker) % titlewidth;
while (titleoffset < BASEVIDWIDTH)
{
V_DrawLSTitleHighString(titleoffset, y, 0, titletext);
titleoffset += titlewidth;
}
patch_t *bumper = W_CachePatchName((cv_alttitle.value ? "MTSJUMPR1" : "MTSBUMPR1"), PU_CACHE);
V_DrawScaledPatch(x-(SHORT(bumper->width)), (BASEVIDHEIGHT-8)-(SHORT(bumper->height)), 0, bumper);
}
void M_DrawSoundTest(void) void M_DrawSoundTest(void)
{ {
UINT8 pid = 0; // todo: Add ability for any splitscreen player to bring up the menu. const UINT8 pid = 0;
INT32 x, y, i, cursorx = 0; INT32 x, y, i, cursorx = 0;
INT32 titleoffset = 0, titlewidth; INT32 titleoffset = 0, titlewidth;

View file

@ -159,10 +159,11 @@ boolean M_NextOpt(void)
if (itemOn + 1 > currentMenu->numitems - 1) if (itemOn + 1 > currentMenu->numitems - 1)
{ {
// Prevent looparound here // Prevent looparound here
// If you're going to add any extra exceptions, DON'T. if (currentMenu->behaviourflags & MBF_NOLOOPENTRIES)
// Add a "don't loop" flag to the menu_t struct instead. {
if (currentMenu == &MISC_AddonsDef) itemOn = oldItemOn;
return false; return false;
}
itemOn = 0; itemOn = 0;
} }
else else
@ -186,10 +187,11 @@ boolean M_PrevOpt(void)
if (!itemOn) if (!itemOn)
{ {
// Prevent looparound here // Prevent looparound here
// If you're going to add any extra exceptions, DON'T. if (currentMenu->behaviourflags & MBF_NOLOOPENTRIES)
// Add a "don't loop" flag to the menu_t struct instead. {
if (currentMenu == &MISC_AddonsDef) itemOn = oldItemOn;
return false; return false;
}
itemOn = currentMenu->numitems - 1; itemOn = currentMenu->numitems - 1;
} }
else else
@ -370,18 +372,19 @@ boolean M_Responder(event_t *ev)
void M_PlayMenuJam(void) void M_PlayMenuJam(void)
{ {
menu_t *refMenu = (menuactive ? currentMenu : restoreMenu); menu_t *refMenu = (menuactive ? currentMenu : restoreMenu);
static boolean loserclubpermitted = false; static boolean musicstatepermitted = false;
boolean loserclub = (loserclubpermitted && (gamedata->musicflags & GDMUSIC_LOSERCLUB));
if (challengesmenu.pending) if (challengesmenu.pending)
{ {
S_StopMusic(); S_StopMusic();
S_StopMusicCredit(); S_StopMusicCredit();
loserclubpermitted = true; musicstatepermitted = true;
return; return;
} }
gdmusic_t override = musicstatepermitted ? gamedata->musicstate : 0;
if (Playing() || soundtest.playing) if (Playing() || soundtest.playing)
return; return;
@ -393,7 +396,7 @@ void M_PlayMenuJam(void)
S_StopMusicCredit(); S_StopMusicCredit();
return; return;
} }
else if (!loserclub) else if (override == 0)
{ {
if (NotCurrentlyPlaying(refMenu->music)) if (NotCurrentlyPlaying(refMenu->music))
{ {
@ -404,12 +407,21 @@ void M_PlayMenuJam(void)
} }
} }
if (loserclub) if (override != 0)
{ {
if (refMenu != NULL && NotCurrentlyPlaying("LOSERC")) // See also gdmusic_t
const char* overridetotrack[GDMUSIC_MAX-1] = {
"KEYGEN",
"LOSERC",
};
if (refMenu != NULL && NotCurrentlyPlaying(overridetotrack[override - 1]))
{ {
S_ChangeMusicInternal("LOSERC", true); S_ChangeMusicInternal(overridetotrack[override - 1], true);
S_ShowMusicCredit(); S_ShowMusicCredit();
if (override < GDMUSIC_KEEPONMENU)
gamedata->musicstate = GDMUSIC_NONE;
} }
return; return;
@ -487,6 +499,11 @@ menu_t *M_SpecificMenuRestore(menu_t *torestore)
// Ticker init // Ticker init
M_MPOptSelectInit(-1); M_MPOptSelectInit(-1);
} }
else if (torestore == &EXTRAS_MainDef)
{
// Disable or enable certain options
M_InitExtras(-1);
}
// One last catch. // One last catch.
M_SetupPlayMenu(-1); M_SetupPlayMenu(-1);
@ -790,12 +807,12 @@ boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt)
return false; return false;
} }
return (menucmd[pid].buttons & bt); return !!(menucmd[pid].buttons & bt);
} }
boolean M_MenuButtonHeld(UINT8 pid, UINT32 bt) boolean M_MenuButtonHeld(UINT8 pid, UINT32 bt)
{ {
return (menucmd[pid].buttons & bt); return !!(menucmd[pid].buttons & bt);
} }
// Returns true if we press the confirmation button // Returns true if we press the confirmation button
@ -1190,5 +1207,7 @@ void M_Init(void)
CV_RegisterVar(&cv_dummyaddonsearch); CV_RegisterVar(&cv_dummyaddonsearch);
CV_RegisterVar(&cv_dummyextraspassword);
M_UpdateMenuBGImage(true); M_UpdateMenuBGImage(true);
} }

View file

@ -68,9 +68,6 @@ static UINT8 cheatf_warp(void)
/*if (modifiedgame) /*if (modifiedgame)
return 0;*/ return 0;*/
if (menuactive && currentMenu != &MainDef)
return 0; // Only on the main menu!
// Unlock EVERYTHING. // Unlock EVERYTHING.
for (i = 0; i < MAXUNLOCKABLES; i++) for (i = 0; i < MAXUNLOCKABLES; i++)
{ {
@ -86,12 +83,40 @@ static UINT8 cheatf_warp(void)
if (success) if (success)
{ {
G_SetUsedCheats(); G_SetUsedCheats();
M_ClearMenus(true);
S_StartSound(0, sfx_kc42); S_StartSound(0, sfx_kc42);
M_StartMessage(M_GetText(
"TOURNAMENT MODE\n\
\nAll challenges temporarily unlocked.\n\
Saving is disabled - the game will\n\
return to normal on next launch.\n\
\n\
Press (B)\n"
), NULL, MM_NOTHING);
}
else
{
S_StartSound(0, sfx_s3k7b);
M_StartMessage(M_GetText(
"TOURNAMENT MODE\n\
\nThis is the correct password, but.\n\
you already have every challenge\n\
unlocked, so saving is still allowed!\n\
\n\
Press (B)\n"
), NULL, MM_NOTHING);
} }
// Refresh secrets menu existing. return 1;
M_ClearMenus(true); }
M_StartControlPanel();
static UINT8 cheatf_wrongwarp(void)
{
// Tee hee.
M_WrongWarp(0);
return 1; return 1;
} }
@ -103,50 +128,51 @@ static UINT8 cheatf_devmode(void)
if (modifiedgame) if (modifiedgame)
return 0; return 0;
if (menuactive && currentMenu != &MainDef)
return 0; // Only on the main menu!
S_StartSound(0, sfx_itemup);
// Just unlock all the things and turn on -debug and console devmode. // Just unlock all the things and turn on -debug and console devmode.
G_SetUsedCheats();
for (i = 0; i < MAXUNLOCKABLES; i++) for (i = 0; i < MAXUNLOCKABLES; i++)
{
if (!unlockables[i].conditionset)
continue;
gamedata->unlocked[i] = true; gamedata->unlocked[i] = true;
}
G_SetUsedCheats();
S_StartSound(0, sfx_kc42);
devparm = true; devparm = true;
cht_debug |= 0x8000; cht_debug |= 0x8000;
// Refresh secrets menu existing.
M_ClearMenus(true);
M_StartControlPanel();
return 1; return 1;
} }
#endif #endif
static cheatseq_t cheat_warp = { static cheatseq_t cheat_warp = {
0, cheatf_warp, NULL, cheatf_warp,
//{ SCRAMBLE('r'), SCRAMBLE('e'), SCRAMBLE('d'), SCRAMBLE('x'), SCRAMBLE('v'), SCRAMBLE('i'), 0xff } (UINT8[]){ SCRAMBLE('p'), SCRAMBLE('l'), SCRAMBLE('a'), SCRAMBLE('c'), SCRAMBLE('e'), SCRAMBLE('h'), SCRAMBLE('o'), SCRAMBLE('l'), SCRAMBLE('d'), SCRAMBLE('e'), SCRAMBLE('r'), 0xff }
(UINT8[]){ SCRAMBLE('b'), SCRAMBLE('a'), SCRAMBLE('n'), SCRAMBLE('a'), SCRAMBLE('n'), SCRAMBLE('a'), 0xff }
}; };
static cheatseq_t cheat_warp_joy = { static cheatseq_t cheat_wrongwarp = {
0, cheatf_warp, NULL, cheatf_wrongwarp,
/*{ SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_UPARROW), (UINT8[]){ SCRAMBLE('b'), SCRAMBLE('a'), SCRAMBLE('n'), SCRAMBLE('a'), SCRAMBLE('n'), SCRAMBLE('a'), 0xff }
SCRAMBLE(KEY_RIGHTARROW), SCRAMBLE(KEY_RIGHTARROW), SCRAMBLE(KEY_UPARROW),
SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_UPARROW),
SCRAMBLE(KEY_ENTER), 0xff }*/
(UINT8[]){ SCRAMBLE(KEY_LEFTARROW), SCRAMBLE(KEY_UPARROW), SCRAMBLE(KEY_RIGHTARROW),
SCRAMBLE(KEY_RIGHTARROW), SCRAMBLE(KEY_UPARROW), SCRAMBLE(KEY_LEFTARROW),
SCRAMBLE(KEY_DOWNARROW), SCRAMBLE(KEY_RIGHTARROW),
SCRAMBLE(KEY_ENTER), 0xff }
}; };
#ifdef DEVELOP #ifdef DEVELOP
static cheatseq_t cheat_devmode = { static cheatseq_t cheat_devmode = {
0, cheatf_devmode, NULL, cheatf_devmode,
(UINT8[]){ SCRAMBLE('d'), SCRAMBLE('e'), SCRAMBLE('v'), SCRAMBLE('m'), SCRAMBLE('o'), SCRAMBLE('d'), SCRAMBLE('e'), 0xff } (UINT8[]){ SCRAMBLE('d'), SCRAMBLE('e'), SCRAMBLE('v'), SCRAMBLE('m'), SCRAMBLE('o'), SCRAMBLE('d'), SCRAMBLE('e'), 0xff }
}; };
#endif #endif
cheatseq_t *cheatseqlist[] =
{
&cheat_warp,
&cheat_wrongwarp,
#ifdef DEVELOP
&cheat_devmode,
#endif
NULL
};
// ========================================================================== // ==========================================================================
// CHEAT SEQUENCE PACKAGE // CHEAT SEQUENCE PACKAGE
// ========================================================================== // ==========================================================================
@ -168,73 +194,64 @@ void cht_Init(void)
// Called in st_stuff module, which handles the input. // Called in st_stuff module, which handles the input.
// Returns a 1 if the cheat was successful, 0 if failed. // Returns a 1 if the cheat was successful, 0 if failed.
// //
static UINT8 cht_CheckCheat(cheatseq_t *cht, char key) static UINT8 cht_CheckCheat(cheatseq_t *cht, char key, boolean shouldend)
{ {
UINT8 rc = 0; UINT8 rc = 0; // end of sequence character
if (!cht->p) if (cht->p == NULL || *cht->p == 0xff)
cht->p = cht->sequence; // initialize if first time return rc;
if (*cht->p == 0) if (*cht->p == 0)
*(cht->p++) = key; *(cht->p++) = key;
else if (cheat_xlate_table[(UINT8)key] == *cht->p) else if (cheat_xlate_table[(UINT8)key] == *cht->p)
cht->p++; cht->p++;
else else
cht->p = cht->sequence; {
cht->p = NULL;
return rc;
}
if (*cht->p == 1) if (*cht->p == 1)
cht->p++; cht->p++;
else if (*cht->p == 0xff) // end of sequence character if (shouldend && *cht->p == 0xff) // end of sequence character
{
cht->p = cht->sequence;
rc = cht->func(); rc = cht->func();
}
return rc; return rc;
} }
boolean cht_Responder(event_t *ev) boolean cht_Interpret(const char *password)
{ {
UINT8 ret = 0, ch = 0; if (!password)
if (ev->type != ev_keydown)
return false; return false;
if (ev->data1 > 0xFF) UINT8 ret = 0;
{
// map some fake (joy) inputs into keys
// map joy inputs into keys
switch (ev->data1)
{
case KEY_JOY1:
case KEY_JOY1 + 2:
ch = KEY_ENTER;
break;
case KEY_HAT1:
ch = KEY_UPARROW;
break;
case KEY_HAT1 + 1:
ch = KEY_DOWNARROW;
break;
case KEY_HAT1 + 2:
ch = KEY_LEFTARROW;
break;
case KEY_HAT1 + 3:
ch = KEY_RIGHTARROW;
break;
default:
// no mapping
return false;
}
}
else
ch = (UINT8)ev->data1;
ret += cht_CheckCheat(&cheat_warp, (char)ch); size_t cheatseqid = 0;
ret += cht_CheckCheat(&cheat_warp_joy, (char)ch);
#ifdef DEVELOP const char *endofpassword = password;
ret += cht_CheckCheat(&cheat_devmode, (char)ch); while (*endofpassword && *(endofpassword+1))
#endif endofpassword++;
return (ret != 0);
cheatseqid = 0;
while (cheatseqlist[cheatseqid])
{
cheatseqlist[cheatseqid]->p = cheatseqlist[cheatseqid]->sequence;
cheatseqid++;
}
while (*password)
{
cheatseqid = 0;
while (cheatseqlist[cheatseqid])
{
ret += cht_CheckCheat(cheatseqlist[cheatseqid], *password, (password == endofpassword));
cheatseqid++;
}
password++;
}
return (ret > 0);
} }
// Console cheat commands rely on these a lot... // Console cheat commands rely on these a lot...

View file

@ -46,7 +46,7 @@ typedef enum {
// //
// Cheat sequences // Cheat sequences
// //
boolean cht_Responder(event_t *ev); boolean cht_Interpret(const char *password);
void cht_Init(void); void cht_Init(void);
// //

View file

@ -312,6 +312,88 @@ quickcheckagain:
} }
} }
void M_SanitiseChallengeGrid(void)
{
UINT8 seen[MAXUNLOCKABLES];
UINT16 empty[MAXUNLOCKABLES + (CHALLENGEGRIDHEIGHT-1)];
UINT16 i, j, numempty = 0;
if (gamedata->challengegrid == NULL)
return;
memset(seen, 0, sizeof(seen));
// Go through all spots to identify duplicates and absences.
for (j = 0; j < gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT; j++)
{
i = gamedata->challengegrid[j];
if (i >= MAXUNLOCKABLES || !unlockables[i].conditionset)
{
empty[numempty++] = j;
continue;
}
if (seen[i] != 5) // Arbitrary cap greater than 4
{
seen[i]++;
if (seen[i] == 1 || unlockables[i].majorunlock)
{
continue;
}
}
empty[numempty++] = j;
}
// Go through unlockables to identify if any haven't been seen.
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (!unlockables[i].conditionset)
{
continue;
}
if (unlockables[i].majorunlock && seen[i] != 4)
{
// Probably not enough spots to retrofit.
goto badgrid;
}
if (seen[i] != 0)
{
// Present on the challenge grid.
continue;
}
if (numempty != 0)
{
// Small ones can be slotted in easy.
j = empty[--numempty];
gamedata->challengegrid[j] = i;
}
// Nothing we can do to recover.
goto badgrid;
}
// Fill the remaining spots with empties.
while (numempty != 0)
{
j = empty[--numempty];
gamedata->challengegrid[j] = MAXUNLOCKABLES;
}
return;
badgrid:
// Just remove everything and let it get regenerated.
Z_Free(gamedata->challengegrid);
gamedata->challengegrid = NULL;
gamedata->challengegridwidth = 0;
}
void M_UpdateChallengeGridExtraData(challengegridextradata_t *extradata) void M_UpdateChallengeGridExtraData(challengegridextradata_t *extradata)
{ {
UINT16 i, j, num, id, tempid, work; UINT16 i, j, num, id, tempid, work;
@ -531,7 +613,7 @@ void M_ClearStats(void)
gamedata->eversavedreplay = false; gamedata->eversavedreplay = false;
gamedata->everseenspecial = false; gamedata->everseenspecial = false;
gamedata->evercrashed = false; gamedata->evercrashed = false;
gamedata->musicflags = 0; gamedata->musicstate = GDMUSIC_NONE;
gamedata->importprofilewins = false; gamedata->importprofilewins = false;
} }
@ -758,10 +840,13 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
case UC_CRASH: case UC_CRASH:
if (gamedata->evercrashed) if (gamedata->evercrashed)
{ {
gamedata->musicflags |= GDMUSIC_LOSERCLUB; if (gamedata->musicstate < GDMUSIC_LOSERCLUB)
gamedata->musicstate = GDMUSIC_LOSERCLUB;
return true; return true;
} }
return false; return false;
case UC_PASSWORD:
return (cn->stringvar == NULL);
// Just for string building // Just for string building
case UC_AND: case UC_AND:
@ -1081,7 +1166,7 @@ static const char *M_GetConditionString(condition_t *cn)
case UC_ALLSUPER: case UC_ALLSUPER:
case UC_ALLEMERALDS: case UC_ALLEMERALDS:
{ {
const char *chaostext, *speedtext = "", *orbetter = ""; const char *chaostext, *speedtext = "";
if (!gamedata->everseenspecial) if (!gamedata->everseenspecial)
return NULL; return NULL;
@ -1093,17 +1178,14 @@ static const char *M_GetConditionString(condition_t *cn)
else else
chaostext = "14"; chaostext = "14";
if (cn->requirement == KARTSPEED_NORMAL) /*if (cn->requirement == KARTSPEED_NORMAL) -- Emeralds can not be collected on Easy
{ {
speedtext = " on Normal difficulty"; speedtext = " on Normal difficulty";
//if (M_SecretUnlocked(SECRET_HARDSPEED, true))
orbetter = " or better";
} }
else if (cn->requirement == KARTSPEED_HARD) else*/
if (cn->requirement == KARTSPEED_HARD)
{ {
speedtext = " on Hard difficulty"; speedtext = " on Hard difficulty";
if (M_SecretUnlocked(SECRET_MASTERMODE, true))
orbetter = " or better";
} }
else if (cn->requirement == KARTGP_MASTER) else if (cn->requirement == KARTGP_MASTER)
{ {
@ -1113,7 +1195,7 @@ static const char *M_GetConditionString(condition_t *cn)
speedtext = " on ???"; speedtext = " on ???";
} }
return va("collect all %s Emeralds%s%s", chaostext, speedtext, orbetter); return va("GRAND PRIX: collect all %s Emeralds%s", chaostext, speedtext);
} }
case UC_TOTALMEDALS: // Requires number of emblems >= x case UC_TOTALMEDALS: // Requires number of emblems >= x
@ -1213,6 +1295,8 @@ static const char *M_GetConditionString(condition_t *cn)
if (gamedata->evercrashed) if (gamedata->evercrashed)
return "launch \"Dr. Robotnik's Ring Racers\" again after a game crash"; return "launch \"Dr. Robotnik's Ring Racers\" again after a game crash";
return NULL; return NULL;
case UC_PASSWORD:
return "enter a secret password";
case UC_AND: case UC_AND:
return "&"; return "&";
@ -1265,7 +1349,7 @@ static const char *M_GetConditionString(condition_t *cn)
if (cn->requirement == KARTSPEED_NORMAL) if (cn->requirement == KARTSPEED_NORMAL)
{ {
speedtext = "on Normal difficulty or better"; speedtext = "on Normal difficulty";
} }
else if (cn->requirement == KARTSPEED_HARD) else if (cn->requirement == KARTSPEED_HARD)
{ {
@ -1322,7 +1406,10 @@ static const char *M_GetConditionString(condition_t *cn)
{ {
if (cup->id != cn->requirement) if (cup->id != cn->requirement)
continue; continue;
return va("%s%s %s CUP", completetype, orbetter, cup->name); return va("%s%s %s CUP",
completetype, orbetter,
(M_CupLocked(cup) ? "???" : cup->name)
);
} }
return va("INVALID CUP CONDITION \"%d:%d\"", cn->type, cn->requirement); return va("INVALID CUP CONDITION \"%d:%d\"", cn->type, cn->requirement);
} }
@ -1528,7 +1615,7 @@ char *M_BuildConditionSetString(UINT16 unlockid)
static boolean M_CheckUnlockConditions(player_t *player) static boolean M_CheckUnlockConditions(player_t *player)
{ {
INT32 i; UINT32 i;
conditionset_t *c; conditionset_t *c;
boolean ret; boolean ret;
@ -1547,6 +1634,43 @@ static boolean M_CheckUnlockConditions(player_t *player)
return ret; return ret;
} }
boolean M_ConditionInterpret(const char *password)
{
UINT32 i, j;
conditionset_t *c;
condition_t *cn;
for (i = 0; i < MAXCONDITIONSETS; ++i)
{
c = &conditionSets[i];
if (!c->numconditions || gamedata->achieved[i])
continue;
for (j = 0; j < c->numconditions; ++j)
{
cn = &c->condition[j];
if (cn->type != UC_PASSWORD)
continue;
if (cn->stringvar == NULL)
continue;
if (stricmp(cn->stringvar, password))
continue;
// Remove the password for this session.
Z_Free(cn->stringvar);
cn->stringvar = NULL;
return true;
}
}
return false;
}
boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall) boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall)
{ {
UINT16 i = 0, response = 0, newkeys = 0; UINT16 i = 0, response = 0, newkeys = 0;
@ -1650,32 +1774,57 @@ boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall)
return false; return false;
} }
UINT16 M_GetNextAchievedUnlock(void) UINT16 M_GetNextAchievedUnlock(boolean canskipchaokeys)
{ {
UINT16 i; UINT16 i;
// Go through unlockables // Go through unlockables
for (i = 0; i < MAXUNLOCKABLES; ++i) for (i = 0; i < MAXUNLOCKABLES; ++i)
{ {
if (gamedata->unlocked[i] || !unlockables[i].conditionset) if (!unlockables[i].conditionset)
{ {
// Not worthy of consideration
continue; continue;
} }
if (gamedata->unlocked[i] == true) if (gamedata->unlocked[i] == true)
{ {
// Already unlocked, no need to engage
continue; continue;
} }
if (gamedata->unlockpending[i] == false) if (gamedata->unlockpending[i] == false)
{ {
// Not unlocked AND not pending, which means chao keys can be used on something
canskipchaokeys = false;
continue; continue;
} }
return i; return i;
} }
if (gamedata->keyspending != 0) if (canskipchaokeys == true)
{
// Okay, we're skipping chao keys - let's just insta-digest them.
if (gamedata->chaokeys + gamedata->keyspending < GDMAX_CHAOKEYS)
{
gamedata->chaokeys += gamedata->keyspending;
gamedata->pendingkeyroundoffset =
(gamedata->pendingkeyroundoffset + gamedata->pendingkeyrounds)
% GDCONVERT_ROUNDSTOKEY;
}
else
{
gamedata->chaokeys = GDMAX_CHAOKEYS;
gamedata->pendingkeyroundoffset = 0;
}
gamedata->keyspending = 0;
gamedata->pendingkeyrounds = 0;
}
else if (gamedata->keyspending != 0)
{ {
return PENDING_CHAOKEYS; return PENDING_CHAOKEYS;
} }

View file

@ -55,6 +55,8 @@ typedef enum
UC_REPLAY, // Save a replay UC_REPLAY, // Save a replay
UC_CRASH, // Hee ho ! UC_CRASH, // Hee ho !
UC_PASSWORD, // Type in something funny
// Just for string building // Just for string building
UC_AND, UC_AND,
UC_COMMA, UC_COMMA,
@ -218,7 +220,14 @@ typedef enum
#endif #endif
#define challengegridloops (gamedata->challengegridwidth >= CHALLENGEGRIDLOOPWIDTH) #define challengegridloops (gamedata->challengegridwidth >= CHALLENGEGRIDLOOPWIDTH)
#define GDMUSIC_LOSERCLUB 0x01 // See also M_PlayMenuJam
typedef enum {
GDMUSIC_NONE = 0,
GDMUSIC_KEYG,
GDMUSIC_KEEPONMENU, // Minimum to keep after leaving the Challenge Grid
GDMUSIC_LOSERCLUB = GDMUSIC_KEEPONMENU,
GDMUSIC_MAX
} gdmusic_t;
// This is the largest number of 9s that will fit in UINT32 and UINT16 respectively. // This is the largest number of 9s that will fit in UINT32 and UINT16 respectively.
#define GDMAX_RINGS 999999999 #define GDMAX_RINGS 999999999
@ -281,7 +290,7 @@ struct gamedata_t
boolean eversavedreplay; boolean eversavedreplay;
boolean everseenspecial; boolean everseenspecial;
boolean evercrashed; boolean evercrashed;
UINT8 musicflags; gdmusic_t musicstate;
// BACKWARDS COMPAT ASSIST // BACKWARDS COMPAT ASSIST
boolean importprofilewins; boolean importprofilewins;
@ -302,6 +311,7 @@ void M_NewGameDataStruct(void);
// Challenges menu stuff // Challenges menu stuff
void M_PopulateChallengeGrid(void); void M_PopulateChallengeGrid(void);
void M_SanitiseChallengeGrid(void);
struct challengegridextradata_t struct challengegridextradata_t
{ {
@ -332,11 +342,12 @@ void M_ClearStats(void);
boolean M_NotFreePlay(player_t *player); boolean M_NotFreePlay(player_t *player);
// Updating conditions and unlockables // Updating conditions and unlockables
boolean M_ConditionInterpret(const char *password);
boolean M_CheckCondition(condition_t *cn, player_t *player); boolean M_CheckCondition(condition_t *cn, player_t *player);
boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall); boolean M_UpdateUnlockablesAndExtraEmblems(boolean loud, boolean doall);
#define PENDING_CHAOKEYS (UINT16_MAX-1) #define PENDING_CHAOKEYS (UINT16_MAX-1)
UINT16 M_GetNextAchievedUnlock(void); UINT16 M_GetNextAchievedUnlock(boolean canskipchaokeys);
UINT16 M_CheckLevelEmblems(void); UINT16 M_CheckLevelEmblems(void);
UINT16 M_CompletionEmblems(void); UINT16 M_CompletionEmblems(void);

View file

@ -4,6 +4,7 @@ target_sources(SRB2SDL2 PRIVATE
extras-challenges.c extras-challenges.c
extras-egg-tv.cpp extras-egg-tv.cpp
extras-statistics.c extras-statistics.c
extras-wrong.c
main-1.c main-1.c
main-profile-select.c main-profile-select.c
options-1.c options-1.c

View file

@ -3,6 +3,7 @@
#include "../k_menu.h" #include "../k_menu.h"
#include "../m_cond.h" #include "../m_cond.h"
#include "../m_cheat.h"
#include "../s_sound.h" #include "../s_sound.h"
menuitem_t EXTRAS_Main[] = menuitem_t EXTRAS_Main[] =
@ -14,7 +15,7 @@ menuitem_t EXTRAS_Main[] =
{IT_STRING | IT_CALL, NULL, NULL, {IT_STRING | IT_CALL, NULL, NULL,
NULL, {.routine = M_Addons}, 0, 0}, NULL, {.routine = M_Addons}, 0, 0},
{IT_STRING | IT_CALL, "Tutorial", "Help Dr. Eggman and Tails test out their new Ring Racers.", {IT_STRING | IT_CALL, "Tutorial", "Help Dr. Robotnik and Tails test out their new Ring Racers.",
NULL, {.routine = M_LevelSelectInit}, 0, GT_TUTORIAL}, NULL, {.routine = M_LevelSelectInit}, 0, GT_TUTORIAL},
{IT_STRING | IT_CALL, "Challenges", "View the requirements for some of the secret content you can unlock!", {IT_STRING | IT_CALL, "Challenges", "View the requirements for some of the secret content you can unlock!",
@ -28,6 +29,9 @@ menuitem_t EXTRAS_Main[] =
{IT_STRING | IT_CALL, NULL, NULL, {IT_STRING | IT_CALL, NULL, NULL,
NULL, {.routine = M_SoundTest}, 0, 0}, NULL, {.routine = M_SoundTest}, 0, 0},
{IT_STRING | IT_CVAR | IT_CV_STRING, "Password", "If you don't know any passwords, come back later!",
NULL, {.cvar = &cv_dummyextraspassword}, 0, 0},
}; };
// the extras menu essentially reuses the options menu stuff // the extras menu essentially reuses the options menu stuff
@ -53,18 +57,10 @@ menu_t EXTRAS_MainDef = {
struct extrasmenu_s extrasmenu; struct extrasmenu_s extrasmenu;
consvar_t cv_dummyextraspassword = CVAR_INIT ("dummyextraspassword", "", CV_HIDDEN, NULL, NULL);
void M_InitExtras(INT32 choice) void M_InitExtras(INT32 choice)
{ {
(void)choice;
extrasmenu.ticker = 0;
extrasmenu.offset = 0;
extrasmenu.extx = 0;
extrasmenu.exty = 0;
extrasmenu.textx = 0;
extrasmenu.texty = 0;
// Addons // Addons
if (M_SecretUnlocked(SECRET_ADDONS, true)) if (M_SecretUnlocked(SECRET_ADDONS, true))
{ {
@ -127,6 +123,17 @@ void M_InitExtras(INT32 choice)
EXTRAS_Main[extras_stereo].text = EXTRAS_Main[extras_stereo].tooltip = "???"; EXTRAS_Main[extras_stereo].text = EXTRAS_Main[extras_stereo].tooltip = "???";
} }
if (choice == -1)
return;
extrasmenu.ticker = 0;
extrasmenu.offset = 0;
extrasmenu.extx = 0;
extrasmenu.exty = 0;
extrasmenu.textx = 0;
extrasmenu.texty = 0;
M_SetupNextMenu(&EXTRAS_MainDef, false); M_SetupNextMenu(&EXTRAS_MainDef, false);
} }
@ -163,6 +170,23 @@ void M_ExtrasTick(void)
extrasmenu.textx = 160; extrasmenu.textx = 160;
extrasmenu.texty = 50; extrasmenu.texty = 50;
} }
if (menutyping.active == false && cv_dummyextraspassword.string[0] != '\0')
{
if (M_ConditionInterpret(cv_dummyextraspassword.string) == true)
{
if (M_UpdateUnlockablesAndExtraEmblems(true, true))
{
M_Challenges(0);
}
}
else if (cht_Interpret(cv_dummyextraspassword.string) == true && menuactive == true)
{
M_InitExtras(-1);
}
CV_StealthSet(&cv_dummyextraspassword, "");
}
} }
boolean M_ExtrasInputs(INT32 ch) boolean M_ExtrasInputs(INT32 ch)

View file

@ -23,7 +23,7 @@ menu_t MISC_AddonsDef = {
MISC_AddonsMenu, MISC_AddonsMenu,
50, 28, 50, 28,
0, 0, 0, 0,
0, MBF_NOLOOPENTRIES,
"EXTRAS", "EXTRAS",
0, 0, 0, 0,
M_DrawAddons, M_DrawAddons,
@ -341,6 +341,9 @@ void M_HandleAddons(INT32 choice)
// Secret menu! // Secret menu!
//MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); //MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
// I could guard it, but let's just always do this.
M_InitExtras(-1);
if (currentMenu->prevMenu) if (currentMenu->prevMenu)
M_SetupNextMenu(M_InterruptMenuWithChallenges(currentMenu->prevMenu), false); M_SetupNextMenu(M_InterruptMenuWithChallenges(currentMenu->prevMenu), false);
else else

View file

@ -8,6 +8,8 @@
#include "../r_skins.h" #include "../r_skins.h"
#include "../s_sound.h" #include "../s_sound.h"
//#define CHAOKEYDEBUG
menuitem_t MISC_ChallengesStatsDummyMenu[] = menuitem_t MISC_ChallengesStatsDummyMenu[] =
{ {
{IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0}, {IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0},
@ -212,7 +214,7 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
M_UpdateUnlockablesAndExtraEmblems(false, true); M_UpdateUnlockablesAndExtraEmblems(false, true);
newunlock = M_GetNextAchievedUnlock(); newunlock = M_GetNextAchievedUnlock(true);
if ((challengesmenu.pending = (newunlock != MAXUNLOCKABLES))) if ((challengesmenu.pending = (newunlock != MAXUNLOCKABLES)))
{ {
@ -226,6 +228,7 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
challengesmenu.requestflip = false; challengesmenu.requestflip = false;
challengesmenu.requestnew = false; challengesmenu.requestnew = false;
challengesmenu.chaokeyadd = false; challengesmenu.chaokeyadd = false;
challengesmenu.chaokeyhold = 0;
challengesmenu.currentunlock = MAXUNLOCKABLES; challengesmenu.currentunlock = MAXUNLOCKABLES;
challengesmenu.unlockcondition = NULL; challengesmenu.unlockcondition = NULL;
@ -284,6 +287,39 @@ void M_Challenges(INT32 choice)
M_SetupNextMenu(&MISC_ChallengesDef, false); M_SetupNextMenu(&MISC_ChallengesDef, false);
} }
static boolean M_CanKeyHiliTile(void)
{
// No keys to do it with?
if (gamedata->chaokeys == 0)
return false;
// No tile data?
if (challengesmenu.extradata == NULL)
return false;
// No selected tile?
if (challengesmenu.currentunlock >= MAXUNLOCKABLES)
return false;
// Already unlocked?
if (gamedata->unlocked[challengesmenu.currentunlock] == true)
return false;
// Marked as unskippable?
if (unlockables[challengesmenu.currentunlock].majorunlock == true)
return false;
UINT16 i = (challengesmenu.hilix * CHALLENGEGRIDHEIGHT) + challengesmenu.hiliy;
// Not a hinted tile OR a fresh board.
if (!(challengesmenu.extradata[i].flags & CHE_HINT)
&& (challengesmenu.unlockcount[CC_UNLOCKED] + challengesmenu.unlockcount[CC_TALLY] > 0))
return false;
// All good!
return true;
}
void M_ChallengesTick(void) void M_ChallengesTick(void)
{ {
const UINT8 pid = 0; const UINT8 pid = 0;
@ -328,7 +364,40 @@ void M_ChallengesTick(void)
} }
} }
if (challengesmenu.pending && challengesmenu.fade < 5) if (challengesmenu.chaokeyhold)
{
if (M_MenuExtraHeld(pid) && M_CanKeyHiliTile())
{
// Not pressed just this frame?
if (!M_MenuExtraPressed(pid))
{
challengesmenu.chaokeyhold++;
if (challengesmenu.chaokeyhold > CHAOHOLD_MAX)
{
#ifndef CHAOKEYDEBUG
gamedata->chaokeys--;
#endif
challengesmenu.chaokeyhold = 0;
challengesmenu.unlockcount[CC_CHAOANIM]++;
S_StartSound(NULL, sfx_chchng);
challengesmenu.pending = true;
//M_ChallengesAutoFocus(challengesmenu.currentunlock, false);
challengesmenu.unlockanim = UNLOCKTIME-1;
}
}
}
else
{
challengesmenu.chaokeyhold = 0;
challengesmenu.unlockcount[CC_CHAONOPE] = 6;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2
}
}
if ((challengesmenu.pending || challengesmenu.chaokeyhold) && challengesmenu.fade < 5)
{ {
// Fade increase. // Fade increase.
challengesmenu.fade++; challengesmenu.fade++;
@ -384,6 +453,9 @@ void M_ChallengesTick(void)
gamedata->keyspending--; gamedata->keyspending--;
gamedata->chaokeys++; gamedata->chaokeys++;
challengesmenu.unlockcount[CC_CHAOANIM]++; challengesmenu.unlockcount[CC_CHAOANIM]++;
if (gamedata->musicstate < GDMUSIC_KEYG)
gamedata->musicstate = GDMUSIC_KEYG;
} }
} }
} }
@ -392,7 +464,7 @@ void M_ChallengesTick(void)
{ {
// The menu apparatus is requesting a new unlock. // The menu apparatus is requesting a new unlock.
challengesmenu.requestnew = false; challengesmenu.requestnew = false;
if ((newunlock = M_GetNextAchievedUnlock()) != MAXUNLOCKABLES) if ((newunlock = M_GetNextAchievedUnlock(false)) != MAXUNLOCKABLES)
{ {
// We got one! // We got one!
M_ChallengesAutoFocus(newunlock, false); M_ChallengesAutoFocus(newunlock, false);
@ -486,7 +558,7 @@ void M_ChallengesTick(void)
} }
} }
} }
else else if (!challengesmenu.chaokeyhold)
{ {
// Tick down the tally. (currently not visible) // Tick down the tally. (currently not visible)
@ -517,52 +589,30 @@ boolean M_ChallengesInputs(INT32 ch)
const boolean move = (menucmd[pid].dpad_ud != 0 || menucmd[pid].dpad_lr != 0); const boolean move = (menucmd[pid].dpad_ud != 0 || menucmd[pid].dpad_lr != 0);
(void) ch; (void) ch;
if (challengesmenu.fade || challengesmenu.chaokeyadd) if (challengesmenu.fade || challengesmenu.chaokeyadd || challengesmenu.chaokeyhold)
{ {
; ;
} }
else if (M_MenuExtraPressed(pid) else if (M_MenuExtraPressed(pid))
&& challengesmenu.extradata)
{ {
i = (challengesmenu.hilix * CHALLENGEGRIDHEIGHT) + challengesmenu.hiliy; if (M_CanKeyHiliTile())
if (challengesmenu.currentunlock < MAXUNLOCKABLES
&& !gamedata->unlocked[challengesmenu.currentunlock]
&& !unlockables[challengesmenu.currentunlock].majorunlock
&& ((challengesmenu.extradata[i].flags & CHE_HINT)
|| (challengesmenu.unlockcount[CC_UNLOCKED] + challengesmenu.unlockcount[CC_TALLY] == 0))
&& gamedata->chaokeys > 0)
{ {
gamedata->chaokeys--; challengesmenu.chaokeyhold = 1;
challengesmenu.unlockcount[CC_CHAOANIM]++;
S_StartSound(NULL, sfx_chchng);
challengesmenu.pending = true;
M_ChallengesAutoFocus(challengesmenu.currentunlock, false);
} }
else else
{ {
challengesmenu.unlockcount[CC_CHAONOPE] = 6; challengesmenu.unlockcount[CC_CHAONOPE] = 6;
S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2 S_StartSound(NULL, sfx_s3k7b); //sfx_s3kb2
#if 0 // debugging
if (challengesmenu.currentunlock < MAXUNLOCKABLES)
{
if (gamedata->unlocked[challengesmenu.currentunlock] && challengesmenu.unlockanim >= UNLOCKTIME)
{
if (challengesmenu.unlockcount[CC_TALLY] > 0)
challengesmenu.unlockcount[CC_TALLY]--;
else
challengesmenu.unlockcount[CC_UNLOCKED]--;
}
Z_Free(gamedata->challengegrid); #ifdef CHAOKEYDEBUG
gamedata->challengegrid = NULL; if (challengesmenu.currentunlock < MAXUNLOCKABLES && challengesmenu.unlockanim >= UNLOCKTIME && gamedata->unlocked[challengesmenu.currentunlock] == true)
gamedata->challengegridwidth = 0; {
M_PopulateChallengeGrid(); gamedata->unlocked[challengesmenu.currentunlock] = gamedata->unlockpending[challengesmenu.currentunlock] = false;
M_UpdateChallengeGridExtraData(challengesmenu.extradata);
challengesmenu.pending = true; if (challengesmenu.unlockcount[CC_TALLY] > 0)
M_ChallengesAutoFocus(challengesmenu.currentunlock, true); challengesmenu.unlockcount[CC_TALLY]--;
else
challengesmenu.unlockcount[CC_UNLOCKED]--;
} }
#endif #endif
} }

153
src/menus/extras-wrong.c Normal file
View file

@ -0,0 +1,153 @@
/// \file menus/extras-wrong.c
/// \brief Wrongwarp
#include "../k_menu.h"
#include "../s_sound.h"
#include "../m_random.h"
#include "../r_skins.h"
struct wrongwarp_s wrongwarp;
static menuitem_t MISC_WrongWarpMenu[] =
{
{IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0},
};
void M_WrongWarp(INT32 choice)
{
(void)choice;
wrongwarp.ticker = 0;
M_SetupNextMenu(&MISC_WrongWarpDef, false);
// Done here to avoid immediate music credit
S_ChangeMusicInternal("YEAWAY", true);
}
static void M_WrongWarpTick(void)
{
static boolean firsteggman = true;
static boolean antitailgate = false;
UINT8 i, j;
wrongwarp.ticker++;
if (wrongwarp.ticker < 2*TICRATE)
return;
if (wrongwarp.ticker == 2*TICRATE)
{
S_ShowMusicCredit();
for (i = 0; i < MAXWRONGPLAYER; i++)
wrongwarp.wrongplayers[i].skin = MAXSKINS;
firsteggman = true;
}
// SMK title screen recreation!?
for (i = 0; i < MAXWRONGPLAYER; i++)
{
if (wrongwarp.wrongplayers[i].skin == MAXSKINS)
continue;
wrongwarp.wrongplayers[i].across += 5;
if (wrongwarp.wrongplayers[i].across < BASEVIDWIDTH + WRONGPLAYEROFFSCREEN)
continue;
wrongwarp.wrongplayers[i].skin = MAXSKINS;
}
if (wrongwarp.delaytowrongplayer)
{
wrongwarp.delaytowrongplayer--;
return;
}
wrongwarp.delaytowrongplayer = M_RandomRange(TICRATE/3, 2*TICRATE/3);
if (wrongwarp.ticker == 2*TICRATE)
return;
UINT32 rskin = 0;
if (firsteggman == true)
{
// Eggman always leads the pack. It's not Sonic's game anymore...
firsteggman = false;
i = 0;
wrongwarp.wrongplayers[i].spinout = false;
}
else
{
rskin = R_GetLocalRandomSkin();
for (i = 0; i < MAXWRONGPLAYER; i++)
{
// Already in use.
if (wrongwarp.wrongplayers[i].skin == rskin)
return;
// Prevent tailgating.
if (antitailgate == !!(i & 1))
continue;
// Slot isn't free.
if (wrongwarp.wrongplayers[i].skin != MAXSKINS)
continue;
break;
}
// No free slots.
if (i == MAXWRONGPLAYER)
return;
// Check to see if any later entry uses the skin too
for (j = i+1; j < MAXWRONGPLAYER; j++)
{
if (wrongwarp.wrongplayers[j].skin != rskin)
continue;
return;
}
wrongwarp.wrongplayers[i].spinout = M_RandomChance(FRACUNIT/11);
}
// Add the new character!
wrongwarp.wrongplayers[i].skin = rskin;
wrongwarp.wrongplayers[i].across = -WRONGPLAYEROFFSCREEN;
antitailgate = !!(i & 1);
}
static boolean M_WrongWarpInputs(INT32 ch)
{
(void)ch;
if (wrongwarp.ticker < TICRATE/2)
return true;
return false;
}
menu_t MISC_WrongWarpDef = {
sizeof (MISC_WrongWarpMenu)/sizeof (menuitem_t),
NULL,
0,
MISC_WrongWarpMenu,
0, 0,
0, 0,
MBF_SOUNDLESS,
".",
98, 0,
M_DrawWrongWarp,
M_WrongWarpTick,
NULL,
NULL,
M_WrongWarpInputs,
};

View file

@ -347,7 +347,7 @@ boolean M_LevelListFromGametype(INT16 gt)
templevelsearch.cup = &dummy_lostandfound; templevelsearch.cup = &dummy_lostandfound;
templevelsearch.checklocked = true; templevelsearch.checklocked = true;
if (M_GetFirstLevelInList(&temp, &levellist.levelsearch) != NEXTMAP_INVALID) if (M_GetFirstLevelInList(&temp, &templevelsearch) != NEXTMAP_INVALID)
{ {
foundany = true; foundany = true;
GRID_INSERTCUP; GRID_INSERTCUP;

View file

@ -7813,7 +7813,7 @@ static void P_InitGametype(void)
// Started a game? Move on to the next jam when you go back to the title screen // Started a game? Move on to the next jam when you go back to the title screen
CV_SetValue(&cv_menujam_update, 1); CV_SetValue(&cv_menujam_update, 1);
gamedata->musicflags = 0; gamedata->musicstate = GDMUSIC_NONE;
} }
struct minimapinfo minimapinfo; struct minimapinfo minimapinfo;

View file

@ -363,8 +363,12 @@ void S_StopSoundByID(void *origin, sfxenum_t sfx_id)
// Sounds without origin can have multiple sources, they shouldn't // Sounds without origin can have multiple sources, they shouldn't
// be stopped by new sounds. // be stopped by new sounds.
// (The above comment predates this codebase using git and cannot be BLAME'd)
// ...yeah, but if it's being stopped by ID, it's clearly an intentful effect. ~toast 090623
#if 0
if (!origin) if (!origin)
return; return;
#endif
#ifdef HW3SOUND #ifdef HW3SOUND
if (hws_mode != HWS_DEFAULT_MODE) if (hws_mode != HWS_DEFAULT_MODE)
{ {

View file

@ -1102,6 +1102,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"typri2", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SA2 final boss-type typewriting {"typri2", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SA2 final boss-type typewriting
{"eggspr", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Sonic Unleashed Trap Spring {"eggspr", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Sonic Unleashed Trap Spring
{"achiev", false, 204, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Achievement"}, {"achiev", false, 204, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Achievement"},
{"gpmetr", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // End of a "Tutorial Teleport"
{"endwrp", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // End of a "Tutorial Teleport" {"endwrp", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // End of a "Tutorial Teleport"
// SRB2Kart - Drop target sounds // SRB2Kart - Drop target sounds

View file

@ -1169,6 +1169,7 @@ typedef enum
sfx_typri2, sfx_typri2,
sfx_eggspr, sfx_eggspr,
sfx_achiev, sfx_achiev,
sfx_gpmetr,
sfx_endwrp, sfx_endwrp,
// SRB2Kart - Drop target sounds // SRB2Kart - Drop target sounds

View file

@ -449,7 +449,7 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
patch_t *resbar = W_CachePatchName("R_RESBAR", PU_PATCH); // Results bars for players patch_t *resbar = W_CachePatchName("R_RESBAR", PU_PATCH); // Results bars for players
if (drawping || data.rankingsmode != 0) if (drawping || standings->rankingsmode != 0)
{ {
inwardshim = 8; inwardshim = 8;
} }
@ -604,15 +604,15 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
); );
} }
} }
else if (data.rankingsmode != 0) else if (standings->rankingsmode != 0)
{ {
char *increasenum = NULL; char *increasenum = NULL;
if (data.increase[pnum] != INT16_MIN) if (standings->increase[pnum] != INT16_MIN)
{ {
increasenum = va( increasenum = va(
"(%d)", "(%d)",
data.increase[pnum] standings->increase[pnum]
); );
} }
@ -660,9 +660,9 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
// Y_RoundQueueDrawer // Y_RoundQueueDrawer
// //
// Handles drawing the bottom-of-screen progression. // Handles drawing the bottom-of-screen progression.
// Currently requires intermission y_data to be active, but abstraction is feasible. // Currently requires intermission y_data for animation only.
// //
void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean widescreen) void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen)
{ {
if (roundqueue.size == 0) if (roundqueue.size == 0)
{ {
@ -766,7 +766,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
INT32 widthofroundqueue = 24*(workingqueuesize - 1); INT32 widthofroundqueue = 24*(workingqueuesize - 1);
INT32 x = (BASEVIDWIDTH - widthofroundqueue) / 2; INT32 x = (BASEVIDWIDTH - widthofroundqueue) / 2;
INT32 y; INT32 y, basey = 167 + offset;
INT32 spacetospecial = 0; INT32 spacetospecial = 0;
@ -829,13 +829,13 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
if (upwa == true) if (upwa == true)
{ {
xiter -= 24; xiter -= 24;
V_DrawMappedPatch(xiter, 167, baseflags, queuebg_upwa, greymap); V_DrawMappedPatch(xiter, basey, baseflags, queuebg_upwa, greymap);
} }
while (xiter > -bufferspace) while (xiter > -bufferspace)
{ {
xiter -= 24; xiter -= 24;
V_DrawMappedPatch(xiter, 167, baseflags, queuebg_flat, greymap); V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap);
} }
for (i = 0; i < workingqueuesize; i++) for (i = 0; i < workingqueuesize; i++)
@ -846,9 +846,9 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
upwa ^= true; upwa ^= true;
if (upwa == false) if (upwa == false)
{ {
y = 171; y = basey + 4;
V_DrawMappedPatch(x, 167, baseflags, queuebg_down, greymap); V_DrawMappedPatch(x, basey, baseflags, queuebg_down, greymap);
if (i+1 != workingqueuesize) // no more line? if (i+1 != workingqueuesize) // no more line?
{ {
@ -857,17 +857,17 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
} }
else else
{ {
y = 179; y = basey + 12;
if (i+1 != workingqueuesize) // no more line? if (i+1 != workingqueuesize) // no more line?
{ {
V_DrawMappedPatch(x, 167, baseflags, queuebg_upwa, greymap); V_DrawMappedPatch(x, basey, baseflags, queuebg_upwa, greymap);
choose_line = line_upwa; choose_line = line_upwa;
} }
else else
{ {
V_DrawMappedPatch(x, 167, baseflags, queuebg_flat, greymap); V_DrawMappedPatch(x, basey, baseflags, queuebg_flat, greymap);
} }
} }
@ -936,7 +936,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
// Draw the line to the right of the dot // Draw the line to the right of the dot
V_DrawMappedPatch( V_DrawMappedPatch(
x - 1, 178, x - 1, basey + 11,
baseflags, baseflags,
choose_line[BPP_SHADOW], choose_line[BPP_SHADOW],
NULL NULL
@ -969,7 +969,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
else else
{ {
V_DrawMappedPatch( V_DrawMappedPatch(
x - 1, 179, x - 1, basey + 12,
baseflags, baseflags,
choose_line[BPP_DONE], choose_line[BPP_DONE],
colormap colormap
@ -988,7 +988,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
} }
V_DrawMappedPatch( V_DrawMappedPatch(
x - 1, 179, x - 1, basey + 12,
baseflags, baseflags,
choose_line[lineisfull ? BPP_DONE : BPP_AHEAD], choose_line[lineisfull ? BPP_DONE : BPP_AHEAD],
lineisfull ? colormap : NULL lineisfull ? colormap : NULL
@ -1006,7 +1006,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
while (xiter < BASEVIDWIDTH + bufferspace) while (xiter < BASEVIDWIDTH + bufferspace)
{ {
xiter += 24; xiter += 24;
V_DrawMappedPatch(xiter, 167, baseflags, queuebg_flat, greymap); V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap);
} }
// Handle special entry on the end // Handle special entry on the end
@ -1068,7 +1068,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
} }
// Special background bump // Special background bump
V_DrawMappedPatch(x2 - 13, 167, baseflags, queuebg_prize, greymap); V_DrawMappedPatch(x2 - 13, basey, baseflags, queuebg_prize, greymap);
// Draw the final line // Draw the final line
const fixed_t barstart = x + 6; const fixed_t barstart = x + 6;
@ -1095,14 +1095,14 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
while (xiter < fillend) while (xiter < fillend)
{ {
V_DrawMappedPatch( V_DrawMappedPatch(
xiter - 1, 177, xiter - 1, basey + 10,
baseflags, baseflags,
line_flat[BPP_SHADOW], line_flat[BPP_SHADOW],
NULL NULL
); );
V_DrawMappedPatch( V_DrawMappedPatch(
xiter - 1, 179, xiter - 1, basey + 12,
baseflags, baseflags,
line_flat[BPP_DONE], line_flat[BPP_DONE],
colormap colormap
@ -1128,14 +1128,14 @@ void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean wides
while (xiter < barend) while (xiter < barend)
{ {
V_DrawMappedPatch( V_DrawMappedPatch(
xiter - 1, 177, xiter - 1, basey + 10,
baseflags, baseflags,
line_flat[BPP_SHADOW], line_flat[BPP_SHADOW],
NULL NULL
); );
V_DrawMappedPatch( V_DrawMappedPatch(
xiter - 1, 179, xiter - 1, basey + 12,
baseflags, baseflags,
line_flat[lineisfull ? BPP_DONE : BPP_AHEAD], line_flat[lineisfull ? BPP_DONE : BPP_AHEAD],
lineisfull ? colormap : NULL lineisfull ? colormap : NULL
@ -1391,7 +1391,7 @@ skiptallydrawer:
goto finalcounter; goto finalcounter;
// Returns early if there's no roundqueue entries to draw // Returns early if there's no roundqueue entries to draw
Y_RoundQueueDrawer(&data, true, false); Y_RoundQueueDrawer(&data, 0, true, false);
if (netgame) if (netgame)
{ {
@ -1498,6 +1498,35 @@ void Y_Ticker(void)
return; return;
} }
// Animation sounds for roundqueue, see Y_RoundQueueDrawer
if (roundqueue.size != 0
&& roundqueue.position != 0
&& (timer - 1) <= 2*TICRATE)
{
const INT32 through = ((2*TICRATE) - (timer - 1));
if (data.showrank == true
&& roundqueue.position == roundqueue.size-1)
{
// Handle special entry on the end
if (through == data.linemeter && timer > 2)
{
S_StopSoundByID(NULL, sfx_gpmetr);
S_StartSound(NULL, sfx_kc50);
}
else if (through == 0)
{
S_StartSound(NULL, sfx_gpmetr);
}
}
else if (through == 9
&& roundqueue.position < roundqueue.size)
{
// Impactful landing
S_StartSound(NULL, sfx_kc50);
}
}
if (intertic < TICRATE || endtic != -1) if (intertic < TICRATE || endtic != -1)
{ {
return; return;

View file

@ -49,7 +49,7 @@ void Y_Ticker(void);
// Specific sub-drawers // Specific sub-drawers
void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset); void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset);
void Y_RoundQueueDrawer(y_data_t *standings, boolean doanimations, boolean widescreen); void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen);
void Y_StartIntermission(void); void Y_StartIntermission(void);
void Y_EndIntermission(void); void Y_EndIntermission(void);