Merge branch 'level_select' into 'master'

Revamped Level Select (platter view)

Seriously revamped every level select instance in the game to use a more attractive and easier to navigate platter view, including:

* Game CLEAR! save (Title/Pause menus)
* Secrets Menu (including custom ones)
* Record Attack/NiGHTS Mode
* Server creation (Online/2P)
* MP Pause level select (Online/2P)

As a result, the layout of the last three above menus has been changed to varying degrees of difference.

Also, bonus feature: using level select (or MAP MAPxx without -force) in Co-op multiplayer won't reset your score, will keep any lives you have above the startinglives variable, and will not take away all your emeralds. The -force thing prevents both warping directly to special stages to rack up the emeralds AND ensures there IS a way to start a new game.

Check out <root>/!LatestSRB2Files/srb2win_branch_levelselect.exe with the latest patch.dta to see more.

Also, LF2_WIDEICON lets you do this. https://gfycat.com/MenacingClearAngora

See merge request !68
This commit is contained in:
Rob 2017-04-04 12:23:18 -04:00
commit 8fdbc5669c
16 changed files with 1162 additions and 421 deletions

View File

@ -1551,34 +1551,27 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if (var == &cv_chooseskin)
{
// Special case for the chooseskin variable, used only directly from the menu
if (increment > 0) // Going up!
newvalue = var->value - 1;
do
{
newvalue = var->value - 1;
do
if (increment > 0) // Going up!
{
newvalue++;
if (newvalue == MAXSKINS)
newvalue = 0;
} while (var->PossibleValue[newvalue].strvalue == NULL);
var->value = newvalue + 1;
var->string = var->PossibleValue[newvalue].strvalue;
var->func();
return;
}
else if (increment < 0) // Going down!
{
newvalue = var->value - 1;
do
}
else if (increment < 0) // Going down!
{
newvalue--;
if (newvalue == -1)
newvalue = MAXSKINS-1;
} while (var->PossibleValue[newvalue].strvalue == NULL);
var->value = newvalue + 1;
var->string = var->PossibleValue[newvalue].strvalue;
var->func();
return;
}
}
} while (var->PossibleValue[newvalue].strvalue == NULL);
var->value = newvalue + 1;
var->string = var->PossibleValue[newvalue].strvalue;
var->func();
return;
}
#ifdef PARANOIA
if (currentindice == -1)

View File

@ -1532,10 +1532,13 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
// The supplied data are assumed to be good.
I_Assert(delay >= 0 && delay <= 2);
if (mapnum != -1)
CV_SetValue(&cv_nextmap, mapnum);
CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d ultmode=%d resetplayers=%d delay=%d skipprecutscene=%d\n",
mapnum, newgametype, pultmode, resetplayers, delay, skipprecutscene);
if (netgame || multiplayer)
if ((netgame || multiplayer) && !((gametype == newgametype) && (newgametype == GT_COOP)))
FLS = false;
if (delay != 2)
@ -1701,9 +1704,19 @@ static void Command_Map_f(void)
}
}
// Prevent warping to locked levels
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
// lives hell.
if (!dedicated && M_MapLocked(newmapnum))
{
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
return;
}
// don't use a gametype the map doesn't support
if (cv_debug || COM_CheckParm("-force") || cv_skipmapcheck.value)
; // The player wants us to trek on anyway. Do so.
fromlevelselect = false; // The player wants us to trek on anyway. Do so.
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
// Alternatively, bail if the map header is completely missing anyway.
else if (!mapheaderinfo[newmapnum-1]
@ -1722,19 +1735,10 @@ static void Command_Map_f(void)
CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring);
return;
}
else
fromlevelselect = ((netgame || multiplayer) && ((gametype == newgametype) && (newgametype == GT_COOP)));
// Prevent warping to locked levels
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
// lives hell.
if (!dedicated && M_MapLocked(newmapnum))
{
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
return;
}
fromlevelselect = false;
D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, false);
D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect);
}
/** Receives a map command and changes the map.
@ -1800,17 +1804,14 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
if (demoplayback && !timingdemo)
precache = false;
if (resetplayer)
{
if (!FLS || (netgame || multiplayer))
emeralds = 0;
}
if (resetplayer && !FLS)
emeralds = 0;
#ifdef HAVE_BLUA
LUAh_MapChange();
#endif
G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene);
G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene, FLS);
if (demoplayback && !timingdemo)
precache = true;
CON_ToggleOff();

View File

@ -1211,6 +1211,12 @@ static void readlevelheader(MYFILE *f, INT32 num)
{
deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2,
sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num));
strlcpy(mapheaderinfo[num-1]->selectheading, word2, sizeof(mapheaderinfo[num-1]->selectheading)); // not deh_ so only complains once
}
else if (fastcmp(word, "SELECTHEADING"))
{
deh_strlcpy(mapheaderinfo[num-1]->selectheading, word2,
sizeof(mapheaderinfo[num-1]->selectheading), va("Level header %d: selectheading", num));
}
else if (fastcmp(word, "SCRIPTNAME"))
{
@ -1418,6 +1424,13 @@ static void readlevelheader(MYFILE *f, INT32 num)
else
mapheaderinfo[num-1]->menuflags &= ~LF2_NOVISITNEEDED;
}
else if (fastcmp(word, "WIDEICON"))
{
if (i || word2[0] == 'T' || word2[0] == 'Y')
mapheaderinfo[num-1]->menuflags |= LF2_WIDEICON;
else
mapheaderinfo[num-1]->menuflags &= ~LF2_WIDEICON;
}
else
deh_warning("Level header %d: unknown word '%s'", num, word);
}
@ -7305,6 +7318,7 @@ struct {
{"LF2_RECORDATTACK",LF2_RECORDATTACK},
{"LF2_NIGHTSATTACK",LF2_NIGHTSATTACK},
{"LF2_NOVISITNEEDED",LF2_NOVISITNEEDED},
{"LF2_WIDEICON",LF2_WIDEICON},
// NiGHTS grades
{"GRADE_F",GRADE_F},
@ -7673,6 +7687,7 @@ struct {
{"V_70TRANS",V_70TRANS},
{"V_80TRANS",V_80TRANS},
{"V_90TRANS",V_90TRANS},
{"V_STATIC",V_STATIC},
{"V_HUDTRANSHALF",V_HUDTRANSHALF},
{"V_HUDTRANS",V_HUDTRANS},
{"V_HUDTRANSDOUBLE",V_HUDTRANSDOUBLE},

View File

@ -241,6 +241,8 @@ typedef struct
UINT8 levelflags; ///< LF_flags: merged eight booleans into one UINT8 for space, see below
UINT8 menuflags; ///< LF2_flags: options that affect record attack / nights mode menus
char selectheading[22]; ///< Level select heading. Allows for controllable grouping.
// Freed animals stuff.
UINT8 numFlickies; ///< Internal. For freed flicky support.
mobjtype_t *flickies; ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful.
@ -267,6 +269,7 @@ typedef struct
#define LF2_RECORDATTACK 4 ///< Show this map in Time Attack
#define LF2_NIGHTSATTACK 8 ///< Show this map in NiGHTS mode menu
#define LF2_NOVISITNEEDED 16 ///< Available in time attack/nights mode without visiting the level
#define LF2_WIDEICON 32 ///< If you're in a circumstance where it fits, use a wide map icon
extern mapheader_t* mapheaderinfo[NUMMAPS];
@ -311,7 +314,7 @@ enum GameType
NUMGAMETYPES
};
// If you alter this list, update gametype_cons_t in m_menu.c
// If you alter this list, update dehacked.c, and gametype_cons_t and MISC_ChangeGameTypeMenu in m_menu.c
extern tic_t totalplaytime;

View File

@ -697,8 +697,7 @@ void G_SetNightsRecords(void)
free(gpath);
// If the mare count changed, this will update the score display
CV_AddValue(&cv_nextmap, 1);
CV_AddValue(&cv_nextmap, -1);
Nextmap_OnChange();
}
// for consistency among messages: this modifies the game and removes savemoddata.
@ -3531,7 +3530,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b
// This is the map command interpretation something like Command_Map_f
//
// called at: map cmd execution, doloadgame, doplaydemo
void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean skipprecutscene)
void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS)
{
INT32 i;
@ -3561,7 +3560,8 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
if (netgame || multiplayer)
{
players[i].lives = cv_startinglives.value;
if (!FLS || (players[i].lives < cv_startinglives.value))
players[i].lives = cv_startinglives.value;
players[i].continues = 0;
}
else if (pultmode)
@ -3575,13 +3575,16 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
players[i].continues = 1;
}
if (!((netgame || multiplayer) && (FLS)))
players[i].score = 0;
// The latter two should clear by themselves, but just in case
players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS);
// Clear cheatcodes too, just in case.
players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS);
players[i].score = players[i].xtralife = 0;
players[i].xtralife = 0;
}
// Reset unlockable triggers
@ -5132,7 +5135,7 @@ void G_DoPlayDemo(char *defdemoname)
memset(playeringame,0,sizeof(playeringame));
playeringame[0] = true;
P_SetRandSeed(randseed);
G_InitNew(false, G_BuildMapName(gamemap), true, true);
G_InitNew(false, G_BuildMapName(gamemap), true, true, false);
// Set skin
SetPlayerSkin(0, skin);

View File

@ -89,7 +89,7 @@ void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo);
void G_DoReborn(INT32 playernum);
void G_PlayerReborn(INT32 player);
void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer,
boolean skipprecutscene);
boolean skipprecutscene, boolean FLS);
char *G_BuildMapTitle(INT32 mapnum);
// XMOD spawning

View File

@ -152,7 +152,9 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale);
float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale);
if (alphalevel >= 10 && alphalevel < 13)
if (alphalevel == 12)
alphalevel = 0;
else if (alphalevel >= 10 && alphalevel < 13)
return;
// make patch ready in hardware cache
@ -252,7 +254,9 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale);
float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale);
if (alphalevel >= 10 && alphalevel < 13)
if (alphalevel == 12)
alphalevel = 0;
else if (alphalevel >= 10 && alphalevel < 13)
return;
// make patch ready in hardware cache

File diff suppressed because it is too large Load Diff

View File

@ -66,7 +66,7 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp
// Called by linux_x/i_video_xshm.c
void M_QuitResponse(INT32 ch);
// Determines whether to show a level in the list
// Determines whether to show a level in the list (platter version does not need to be exposed)
boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
@ -182,6 +182,28 @@ typedef struct
UINT8 next;
} description_t;
// level select platter
typedef struct
{
char header[22+5]; // mapheader_t lvltttl max length + " ZONE"
INT32 maplist[3];
char mapnames[3][17+1];
boolean mapavailable[4]; // mapavailable[3] == wide or not
} levelselectrow_t;
typedef struct
{
UINT8 numrows;
levelselectrow_t *rows;
} levelselect_t;
// experimental level select end
// descriptions for gametype select screen
typedef struct
{
char notes[441];
} gtdesc_t;
// mode descriptions for video mode menu
typedef struct
{
@ -222,6 +244,9 @@ void M_ForceSaveSlotSelected(INT32 sslot);
void M_CheatActivationResponder(INT32 ch);
// Level select updating
void Nextmap_OnChange(void);
// Screenshot menu updating
void Moviemode_mode_Onchange(void);
void Screenshot_option_Onchange(void);
@ -263,14 +288,14 @@ void Screenshot_option_Onchange(void);
NULL\
}
#define MAPICONMENUSTYLE(header, source, prev)\
#define MAPPLATTERMENUSTYLE(header, source)\
{\
header,\
sizeof (source)/sizeof (menuitem_t),\
prev,\
&MainDef,\
source,\
M_DrawServerMenu,\
27,40,\
M_DrawLevelPlatterMenu,\
0,0,\
0,\
NULL\
}

View File

@ -199,6 +199,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
const INT16 num = (INT16)(i-1);
DEH_WriteUndoline("LEVELNAME", mapheaderinfo[num]->lvlttl, UNDO_NONE);
mapheaderinfo[num]->lvlttl[0] = '\0';
DEH_WriteUndoline("SELECTHEADING", mapheaderinfo[num]->selectheading, UNDO_NONE);
mapheaderinfo[num]->selectheading[0] = '\0';
DEH_WriteUndoline("SUBTITLE", mapheaderinfo[num]->subttl, UNDO_NONE);
mapheaderinfo[num]->subttl[0] = '\0';
DEH_WriteUndoline("ACT", va("%d", mapheaderinfo[num]->actnum), UNDO_NONE);

View File

@ -909,7 +909,7 @@ static inline boolean I_SkipFrame(void)
case GS_LEVEL:
if (!paused)
return false;
case GS_TIMEATTACK:
//case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible
case GS_WAITINGPLAYERS:
return skip; // Skip odd frames
default:

View File

@ -1311,7 +1311,7 @@ static inline boolean I_SkipFrame(void)
case GS_LEVEL:
if (!paused)
return false;
case GS_TIMEATTACK:
//case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible
case GS_WAITINGPLAYERS:
return skip; // Skip odd frames
default:

View File

@ -325,6 +325,13 @@ static inline UINT8 transmappedpdraw(const UINT8 *dest, const UINT8 *source, fix
{
return *(v_translevel + (((*(v_colormap + source[ofs>>FRACBITS]))<<8)&0xff00) + (*dest&0xff));
}
static inline UINT8 staticpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
{
UINT8 val = source[ofs>>FRACBITS];
(void)dest;
if (val < 7) return val;
return M_RandomKey(7+1)+(val-7);//M_RandomByte();
}
// Draws a patch scaled to arbitrary size.
void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t *patch, const UINT8 *colormap)
@ -356,22 +363,30 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t
patchdrawfunc = standardpdraw;
v_translevel = NULL;
if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)) == 12) // static
{
if (alphalevel == 13)
alphalevel = hudminusalpha[cv_translucenthud.value];
else if (alphalevel == 14)
alphalevel = 10 - cv_translucenthud.value;
else if (alphalevel == 15)
alphalevel = hudplusalpha[cv_translucenthud.value];
if (alphalevel >= 10)
return; // invis
alphalevel = 0;
patchdrawfunc = staticpdraw;
}
if (alphalevel)
else
{
v_translevel = transtables + ((alphalevel-1)<<FF_TRANSSHIFT);
patchdrawfunc = translucentpdraw;
if (alphalevel)
{
if (alphalevel == 13)
alphalevel = hudminusalpha[cv_translucenthud.value];
else if (alphalevel == 14)
alphalevel = 10 - cv_translucenthud.value;
else if (alphalevel == 15)
alphalevel = hudplusalpha[cv_translucenthud.value];
if (alphalevel >= 10)
return; // invis
}
if (alphalevel)
{
v_translevel = transtables + ((alphalevel-1)<<FF_TRANSSHIFT);
patchdrawfunc = translucentpdraw;
}
}
v_colormap = NULL;

View File

@ -90,6 +90,7 @@ extern RGBA_t *pLocalPalette;
#define V_70TRANS 0x00070000
#define V_80TRANS 0x00080000 // used to be V_8020TRANS
#define V_90TRANS 0x00090000
#define V_STATIC 0x000C0000 // ogl unsupported kthnxbai
#define V_HUDTRANSHALF 0x000D0000
#define V_HUDTRANS 0x000E0000 // draw the hud translucent
#define V_HUDTRANSDOUBLE 0x000F0000

View File

@ -322,7 +322,7 @@ static inline boolean I_SkipFrame(void)
case GS_LEVEL:
if (!paused)
return false;
case GS_TIMEATTACK:
//case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible
#ifndef CLIENT_LOADINGSCREEN
case GS_WAITINGPLAYERS:
#endif

View File

@ -900,8 +900,7 @@ static void Y_UpdateRecordReplays(void)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
// Update timeattack menu's replay availability.
CV_AddValue(&cv_nextmap, 1);
CV_AddValue(&cv_nextmap, -1);
Nextmap_OnChange();
}
//