diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..b5c43d017 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,63 @@ +version: 2 +jobs: + build: + working_directory: /root/SRB2 + docker: + - image: debian:jessie + environment: + CC: ccache gcc -m32 + PKG_CONFIG_LIBDIR: /usr/lib/i386-linux-gnu/pkgconfig + LIBGME_CFLAGS: -I/usr/include + LIBGME_LDFLAGS: -lgme + CCACHE_COMPRESS: true + WFLAGS: -Wno-unsuffixed-float-constants + GCC49: true + #- image: ubuntu:trusty + # environment: + # CC: ccache gcc -m32 + # PKG_CONFIG_LIBDIR: /usr/lib/i386-linux-gnu/pkgconfig + # LIBGME_CFLAGS: -I/usr/include + # LIBGME_LDFLAGS: -lgme + # CCACHE_COMPRESS: true + # WFLAGS: -Wno-unsuffixed-float-constants + # GCC48: true + steps: + - run: + name: Add i386 arch + command: dpkg --add-architecture i386 + - run: + name: Update APT listing + command: apt-get -qq update + - run: + name: Support S3 upload + command: apt-get -qq -y install ca-certificates + - restore_cache: + keys: + - v1-SRB2-APT + - run: + name: Install SDK + command: apt-get -qq -y install git build-essential nasm libpng12-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 gettext ccache wget gcc-multilib upx + - save_cache: + key: v1-SRB2-APT + paths: + - /var/cache/apt/archives + - checkout + - run: + name: Clean build + command: make -C src LINUX=1 clean + - restore_cache: + keys: + - v1-SRB2-{{ .Branch }}-{{ checksum "objs/Linux/SDL/Release/depend.dep" }} + - run: + name: Compile + command: make -C src LINUX=1 ERRORMODE=1 -k + - store_artifacts: + path: /root/SRB2/bin/Linux/Release/ + destination: bin + - save_cache: + key: v1-SRB2-{{ .Branch }}-{{ checksum "objs/Linux/SDL/Release/depend.dep" }} + paths: + - /root/.ccache + + + diff --git a/README.md b/README.md index eb06156b4..d16071454 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build status](https://ci.appveyor.com/api/projects/status/399d4hcw9yy7hg2y?svg=true)](https://ci.appveyor.com/project/STJr/srb2) [![Build status](https://travis-ci.org/STJr/SRB2.svg?branch=master)](https://travis-ci.org/STJr/SRB2) +[![CircleCI](https://circleci.com/gh/STJr/SRB2/tree/master.svg?style=svg)](https://circleci.com/gh/STJr/SRB2/tree/master) [Sonic Robo Blast 2](https://srb2.org/) is a 3D Sonic the Hedgehog fangame based on a modified version of [Doom Legacy](http://doomlegacy.sourceforge.net/). diff --git a/src/b_bot.c b/src/b_bot.c index 0f2c80d55..dc65c9c16 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -212,7 +212,7 @@ boolean B_CheckRespawn(player_t *player) // Check if Sonic is busy first. // If he's doing any of these things, he probably doesn't want to see us. - if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_NIGHTSMODE) + if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_BOUNCING) || (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK) || (sonic->player->powers[pw_carry])) return false; diff --git a/src/command.c b/src/command.c index 6534fed3d..d72f845ad 100644 --- a/src/command.c +++ b/src/command.c @@ -1165,7 +1165,7 @@ found: if (var == &cv_forceskin) { var->value = R_SkinAvailable(var->string); - if (!R_SkinUnlock(var->value)) + if (!R_SkinUsable(-1, var->value)) var->value = -1; } else @@ -1361,6 +1361,16 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth) return; } + if (var == &cv_forceskin) + { + INT32 skin = R_SkinAvailable(value); + if ((stricmp(value, "None")) && ((skin == -1) || !R_SkinUsable(-1, skin))) + { + CONS_Printf("Please provide a valid skin name (\"None\" disables).\n"); + return; + } + } + // Only add to netcmd buffer if in a netgame, otherwise, just change it. if (netgame || multiplayer) { @@ -1478,7 +1488,7 @@ void CV_AddValue(consvar_t *var, INT32 increment) else if (newvalue >= numskins) newvalue = -1; } while ((oldvalue != newvalue) - && !(R_SkinUnlock(newvalue))); + && !(R_SkinUsable(-1, newvalue))); } else newvalue = var->value + increment; @@ -1551,34 +1561,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) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 574cce7ab..2db27a693 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -528,6 +528,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->skincolor = players[i].skincolor; rsp->skin = LONG(players[i].skin); + rsp->availabilities = LONG(players[i].availabilities); // Just in case Lua does something like // modify these at runtime rsp->camerascale = (fixed_t)LONG(players[i].camerascale); @@ -551,7 +552,6 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->playerspinheight = (fixed_t)LONG(players[i].spinheight); rsp->speed = (fixed_t)LONG(players[i].speed); - rsp->jumping = players[i].jumping; rsp->secondjump = players[i].secondjump; rsp->fly1 = players[i].fly1; rsp->glidetime = (tic_t)LONG(players[i].glidetime); @@ -657,6 +657,7 @@ static void resynch_read_player(resynch_pak *rsp) players[i].skincolor = rsp->skincolor; players[i].skin = LONG(rsp->skin); + players[i].availabilities = LONG(rsp->availabilities); // Just in case Lua does something like // modify these at runtime players[i].camerascale = (fixed_t)LONG(rsp->camerascale); @@ -680,7 +681,6 @@ static void resynch_read_player(resynch_pak *rsp) players[i].spinheight = (fixed_t)LONG(rsp->playerspinheight); players[i].speed = (fixed_t)LONG(rsp->speed); - players[i].jumping = rsp->jumping; players[i].secondjump = rsp->secondjump; players[i].fly1 = rsp->fly1; players[i].glidetime = (tic_t)LONG(rsp->glidetime); @@ -1364,6 +1364,7 @@ static boolean SV_SendServerConfig(INT32 node) // which is nice and easy for us to detect memset(netbuffer->u.servercfg.playerskins, 0xFF, sizeof(netbuffer->u.servercfg.playerskins)); memset(netbuffer->u.servercfg.playercolor, 0xFF, sizeof(netbuffer->u.servercfg.playercolor)); + memset(netbuffer->u.servercfg.playeravailabilities, 0xFF, sizeof(netbuffer->u.servercfg.playeravailabilities)); for (i = 0; i < MAXPLAYERS; i++) { @@ -1371,6 +1372,7 @@ static boolean SV_SendServerConfig(INT32 node) continue; netbuffer->u.servercfg.playerskins[i] = (UINT8)players[i].skin; netbuffer->u.servercfg.playercolor[i] = (UINT8)players[i].skincolor; + netbuffer->u.servercfg.playeravailabilities[i] = (UINT32)LONG(players[i].availabilities); } memcpy(netbuffer->u.servercfg.server_context, server_context, 8); @@ -2308,12 +2310,7 @@ static void ResetNode(INT32 node); void CL_ClearPlayer(INT32 playernum) { if (players[playernum].mo) - { - // Don't leave a NiGHTS ghost! - if ((players[playernum].pflags & PF_NIGHTSMODE) && players[playernum].mo->tracer) - P_RemoveMobj(players[playernum].mo->tracer); P_RemoveMobj(players[playernum].mo); - } memset(&players[playernum], 0, sizeof (player_t)); } @@ -3497,10 +3494,12 @@ static void HandlePacketFromAwayNode(SINT8 node) for (j = 0; j < MAXPLAYERS; j++) { if (netbuffer->u.servercfg.playerskins[j] == 0xFF - && netbuffer->u.servercfg.playercolor[j] == 0xFF) + && netbuffer->u.servercfg.playercolor[j] == 0xFF + && netbuffer->u.servercfg.playeravailabilities[j] == 0xFFFFFFFF) continue; // not in game playeringame[j] = true; + players[j].availabilities = (UINT32)LONG(netbuffer->u.servercfg.playeravailabilities[j]); SetPlayerSkinByNum(j, (INT32)netbuffer->u.servercfg.playerskins[j]); players[j].skincolor = netbuffer->u.servercfg.playercolor[j]; } diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 382329539..1ca82fdc5 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -172,6 +172,7 @@ typedef struct UINT8 skincolor; INT32 skin; + UINT32 availabilities; // Just in case Lua does something like // modify these at runtime fixed_t camerascale; @@ -195,7 +196,6 @@ typedef struct fixed_t playerspinheight; fixed_t speed; - UINT8 jumping; UINT8 secondjump; UINT8 fly1; tic_t glidetime; @@ -284,6 +284,7 @@ typedef struct // 0xFF == not in game; else player skin num UINT8 playerskins[MAXPLAYERS]; UINT8 playercolor[MAXPLAYERS]; + UINT32 playeravailabilities[MAXPLAYERS]; UINT8 gametype; UINT8 modifiedgame; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 6519a5d0e..a79c62082 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -998,7 +998,7 @@ UINT8 CanChangeSkin(INT32 playernum) return true; // Force skin in effect. - if (client && (cv_forceskin.value != -1) && !(adminplayer == playernum && serverplayer == -1)) + if ((cv_forceskin.value != -1) || (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0')) return false; // Can change skin in intermission and whatnot. @@ -1096,6 +1096,8 @@ static void SendNameAndColor(void) if (!Playing()) return; + players[consoleplayer].availabilities = R_GetSkinAvailabilities(); + // If you're not in a netgame, merely update the skin, color, and name. if (!netgame) { @@ -1114,7 +1116,7 @@ static void SendNameAndColor(void) SetPlayerSkinByNum(consoleplayer, 0); CV_StealthSet(&cv_skin, skins[0].name); } - else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUnlock(foundskin)) + else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin)) { boolean notsame; @@ -1161,7 +1163,7 @@ static void SendNameAndColor(void) // check if player has the skin loaded (cv_skin may have // the name of a skin that was available in the previous game) cv_skin.value = R_SkinAvailable(cv_skin.string); - if ((cv_skin.value < 0) || !R_SkinUnlock(cv_skin.value)) + if ((cv_skin.value < 0) || !R_SkinUsable(consoleplayer, cv_skin.value)) { CV_StealthSet(&cv_skin, DEFAULTSKIN); cv_skin.value = 0; @@ -1169,6 +1171,7 @@ static void SendNameAndColor(void) // Finally write out the complete packet and send it off. WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME); + WRITEUINT32(p, (UINT32)players[consoleplayer].availabilities); WRITEUINT8(p, (UINT8)cv_playercolor.value); WRITEUINT8(p, (UINT8)cv_skin.value); SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf); @@ -1211,6 +1214,8 @@ static void SendNameAndColor2(void) if (!Playing()) return; + players[secondplaya].availabilities = R_GetSkinAvailabilities(); + // If you're not in a netgame, merely update the skin, color, and name. if (botingame) { @@ -1239,7 +1244,7 @@ static void SendNameAndColor2(void) SetPlayerSkinByNum(secondplaya, forcedskin); CV_StealthSet(&cv_skin2, skins[forcedskin].name); } - else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUnlock(foundskin)) + else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUsable(secondplaya, foundskin)) { boolean notsame; @@ -1294,6 +1299,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) #endif READSTRINGN(*cp, name, MAXPLAYERNAME); + p->availabilities = READUINT32(*cp); color = READUINT8(*cp); skin = READUINT8(*cp); @@ -1310,6 +1316,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (server && (p != &players[consoleplayer] && p != &players[secondarydisplayplayer])) { boolean kick = false; + INT32 s; // team colors if (G_GametypeHasTeams()) @@ -1324,6 +1331,16 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (!p->skincolor) kick = true; + // availabilities + for (s = 0; s < MAXSKINS; s++) + { + if (!skins[s].availability && (p->availabilities & (1 << s))) + { + kick = true; + break; + } + } + if (kick) { XBOXSTATIC UINT8 buf[2]; @@ -1519,10 +1536,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) @@ -1688,9 +1708,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] @@ -1709,19 +1739,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. @@ -1787,17 +1808,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(); @@ -3966,13 +3984,6 @@ static void Command_Archivetest_f(void) */ static void ForceSkin_OnChange(void) { - if ((server || adminplayer == consoleplayer) && ((cv_forceskin.value == -1 && stricmp(cv_forceskin.string, "None")) || !(R_SkinUnlock(cv_forceskin.value)))) - { - CONS_Printf("Please provide a valid skin name (\"None\" disables).\n"); - CV_SetValue(&cv_forceskin, -1); - return; - } - // NOT in SP, silly! if (!(netgame || multiplayer)) return; @@ -3981,7 +3992,7 @@ static void ForceSkin_OnChange(void) CONS_Printf("The server has lifted the forced skin restrictions.\n"); else { - CONS_Printf("The server is restricting all players to skin \"%s\".\n",skins[cv_forceskin.value].realname); + CONS_Printf("The server is restricting all players to skin \"%s\".\n",skins[cv_forceskin.value].name); ForceAllSkins(cv_forceskin.value); } } @@ -4019,7 +4030,8 @@ static void Skin_OnChange(void) if (!Playing()) return; // do whatever you want - if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player. + if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player. + && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y { CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name); return; @@ -4062,8 +4074,7 @@ static void Color_OnChange(void) if (!Playing()) return; // do whatever you want - if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player. - && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CONTINUING)) + if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player. { CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name); return; diff --git a/src/d_player.h b/src/d_player.h index 1c57e6167..4e4a53a08 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -32,19 +32,21 @@ // Extra abilities/settings for skins (combinable stuff) typedef enum { - SF_SUPER = 1, // Can turn super in singleplayer/co-op mode. - SF_SUPERANIMS = 1<<1, // If super, use the super sonic animations - SF_SUPERSPIN = 1<<2, // Should spin frames be played while super? - SF_HIRES = 1<<3, // Draw the sprite 2x as small? + SF_SUPER = 1, // Can turn super in singleplayer/co-op mode? + SF_NOSUPERSPIN = 1<<1, // Should spin frames be played while super? + SF_NOSPINDASHDUST = 1<<2, // Spawn dust particles when charging a spindash? + SF_HIRES = 1<<3, // Draw the sprite at different size? SF_NOSKID = 1<<4, // No skid particles etc SF_NOSPEEDADJUST = 1<<5, // Skin-specific version of disablespeedadjust SF_RUNONWATER = 1<<6, // Run on top of water FOFs? - SF_NOJUMPSPIN = 1<<7, // SPR2_JUMP defaults to SPR2_SPRG instead of SPR2_SPIN, falling states used, and player height is full when jumping? + SF_NOJUMPSPIN = 1<<7, // SPR2_JUMP defaults to SPR2_SPRG instead of SPR2_ROLL, falling states used, and player height is full when jumping? SF_NOJUMPDAMAGE = 1<<8, // Don't damage enemies, etc whilst jumping? SF_STOMPDAMAGE = 1<<9, // Always damage enemies, etc by landing on them, no matter your vunerability? SF_MARIODAMAGE = SF_NOJUMPDAMAGE|SF_STOMPDAMAGE, // The Mario method of being able to damage enemies, etc. SF_MACHINE = 1<<10, // Beep boop. Are you a robot? - SF_NOSPINDASHDUST = 1<<11, // Don't spawn dust particles when charging a spindash + SF_DASHMODE = 1<<11, // Sonic Advance 2 style top speed increase? + SF_FASTEDGE = 1<<12, // Faster edge teeter? + SF_MULTIABILITY = 1<<13, // Revenge of Final Demo. // free up to and including 1<<31 } skinflags_t; @@ -65,7 +67,7 @@ typedef enum CA_JUMPBOOST, CA_AIRDRILL, CA_JUMPTHOK, - CA_DASHMODE, + CA_BOUNCE, CA_TWINSPIN } charability_t; @@ -74,7 +76,7 @@ typedef enum { CA2_NONE=0, CA2_SPINDASH, - CA2_MULTIABILITY, + CA2_GUNSLINGER, CA2_MELEE } charability2_t; @@ -118,10 +120,8 @@ typedef enum // Did you get a time-over? PF_TIMEOVER = 1<<10, - // Ready for Super? - PF_SUPERREADY = 1<<11, - // Character action status + PF_STARTJUMP = 1<<11, PF_JUMPED = 1<<12, PF_SPINNING = 1<<13, PF_STARTDASH = 1<<14, @@ -133,12 +133,11 @@ typedef enum // Sliding (usually in water) like Labyrinth/Oil Ocean PF_SLIDING = 1<<17, - /*** NIGHTS STUFF ***/ - // Is the player in NiGHTS mode? - PF_NIGHTSMODE = 1<<18, - PF_TRANSFERTOCLOSEST = 1<<19, + // Bouncing + PF_BOUNCING = 1<<18, - // Spill rings after falling + /*** NIGHTS STUFF ***/ + PF_TRANSFERTOCLOSEST = 1<<19, PF_NIGHTSFALL = 1<<20, PF_DRILLING = 1<<21, PF_SKIDDOWN = 1<<22, @@ -157,10 +156,10 @@ typedef enum // Used shield ability PF_SHIELDABILITY = 1<<28, - // Force jump damage? - PF_FORCEJUMPDAMAGE = 1<<29 + // Jump damage? + PF_NOJUMPDAMAGE = 1<<29, - // free up to and including 1<<31 + // up to 1<<31 is free } pflags_t; typedef enum @@ -171,7 +170,7 @@ typedef enum PA_EDGE, PA_WALK, PA_RUN, - PA_PEEL, + PA_DASH, PA_PAIN, PA_ROLL, PA_JUMP, @@ -223,6 +222,10 @@ typedef enum CR_GENERIC, // Tails carry. CR_PLAYER, + // NiGHTS mode. Not technically a CARRYING, but doesn't stack with any of the others, so might as well go here. + CR_NIGHTSMODE, + // Old Brak sucks hard, but this gimmick could be used for something better, so we might as well continue supporting it. + CR_BRAKGOOP, // Specific level gimmicks. CR_ZOOMTUBE, CR_ROPEHANG, @@ -262,9 +265,7 @@ typedef enum pw_nights_helper, pw_nights_linkfreeze, - //for linedef exec 427 - pw_nocontrol, - pw_ingoop, // In goop + pw_nocontrol, //for linedef exec 427 NUMPOWERS } powertype_t; @@ -340,6 +341,7 @@ typedef struct player_s UINT8 skincolor; INT32 skin; + UINT32 availabilities; UINT32 score; // player score fixed_t dashspeed; // dashing speed @@ -377,7 +379,6 @@ typedef struct player_s UINT8 gotcontinue; // Got continue from this stage? fixed_t speed; // Player's speed (distance formula of MOMX and MOMY values) - UINT8 jumping; // Holding down jump button UINT8 secondjump; // Jump counter UINT8 fly1; // Tails flying diff --git a/src/dehacked.c b/src/dehacked.c index e4ba03847..8628cc89c 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -341,22 +341,20 @@ static void readAnimTex(MYFILE *f, INT32 num) } */ -static boolean findFreeSlot(INT32 *num, UINT16 wadnum) +static boolean findFreeSlot(INT32 *num) { // Send the character select entry to a free slot. - while (*num < 32 && (!(PlayerMenu[*num].status & IT_DISABLED) || description[*num].wadnum == wadnum)) // Will kill hidden characters from other files, but that's okay. + while (*num < 32 && (description[*num].used)) *num = *num+1; // No more free slots. :( if (*num >= 32) return false; - PlayerMenu[*num].status = IT_CALL; - description[*num].wadnum = wadnum; description[*num].picname[0] = '\0'; // Redesign your logo. (See M_DrawSetupChoosePlayerMenu in m_menu.c...) // Found one! ^_^ - return true; + return (description[*num].used = true); } // Reads a player. @@ -386,7 +384,7 @@ static void readPlayer(MYFILE *f, INT32 num) { char *playertext = NULL; - if (!slotfound && (slotfound = findFreeSlot(&num, f->wad)) == false) + if (!slotfound && (slotfound = findFreeSlot(&num)) == false) goto done; for (i = 0; i < MAXLINELEN-3; i++) @@ -435,19 +433,13 @@ static void readPlayer(MYFILE *f, INT32 num) if (fastcmp(word, "PICNAME")) { - if (!slotfound && (slotfound = findFreeSlot(&num, f->wad)) == false) + if (!slotfound && (slotfound = findFreeSlot(&num)) == false) goto done; strncpy(description[num].picname, word2, 8); } else if (fastcmp(word, "STATUS")) { - // Limit the status to only IT_DISABLED and IT_CALL - if (i) - i = IT_CALL; - else - i = IT_DISABLED; - /* You MAY disable previous entries if you so desire... But try to enable something that's already enabled and you will be sent to a free slot. @@ -455,14 +447,15 @@ static void readPlayer(MYFILE *f, INT32 num) Because of this, you are allowed to edit any previous entries you like, but only if you signal that you are purposely doing so by disabling and then reenabling the slot. */ - if (i != IT_DISABLED && !slotfound && (slotfound = findFreeSlot(&num, f->wad)) == false) + if (i && !slotfound && (slotfound = findFreeSlot(&num)) == false) goto done; - PlayerMenu[num].status = (INT16)i; + + description[num].used = (!!i); } else if (fastcmp(word, "SKINNAME")) { // Send to free slot. - if (!slotfound && (slotfound = findFreeSlot(&num, f->wad)) == false) + if (!slotfound && (slotfound = findFreeSlot(&num)) == false) goto done; strlcpy(description[num].skinname, word2, sizeof description[num].skinname); @@ -1076,6 +1069,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")) { @@ -1283,6 +1282,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); } @@ -2091,6 +2097,8 @@ static void reademblemdata(MYFILE *f, INT32 num) emblemlocations[num-1].type = ET_NGRADE; else if (fastcmp(word2, "NTIME")) emblemlocations[num-1].type = ET_NTIME; + else if (fastcmp(word2, "MAP")) + emblemlocations[num-1].type = ET_MAP; else emblemlocations[num-1].type = (UINT8)value; } @@ -3590,12 +3598,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_PLAY_WAIT", "S_PLAY_WALK", "S_PLAY_RUN", - "S_PLAY_PEEL", + "S_PLAY_DASH", "S_PLAY_PAIN", + "S_PLAY_STUN", "S_PLAY_DEAD", "S_PLAY_DRWN", - "S_PLAY_SPIN", - "S_PLAY_DASH", + "S_PLAY_ROLL", "S_PLAY_GASP", "S_PLAY_JUMP", "S_PLAY_SPRING", @@ -3603,6 +3611,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_PLAY_EDGE", "S_PLAY_RIDE", + // CA2_SPINDASH + "S_PLAY_SPINDASH", + // CA_FLY/SWIM "S_PLAY_FLY", "S_PLAY_SWIM", @@ -3613,30 +3624,25 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_PLAY_CLING", "S_PLAY_CLIMB", + // CA_FLOAT/CA_SLOWFALL + "S_PLAY_FLOAT", + "S_PLAY_FLOAT_RUN", + + // CA_BOUNCE + "S_PLAY_BOUNCE", + "S_PLAY_BOUNCE_LANDING", + + // CA2_GUNSLINGER + "S_PLAY_FIRE", + "S_PLAY_FIRE_FINISH", + // CA_TWINSPIN "S_PLAY_TWINSPIN", // CA2_MELEE "S_PLAY_MELEE", "S_PLAY_MELEE_FINISH", - - // SF_SUPERANIMS - "S_PLAY_SUPER_STND", - "S_PLAY_SUPER_WALK", - "S_PLAY_SUPER_RUN", - "S_PLAY_SUPER_PEEL", - "S_PLAY_SUPER_PAIN", - "S_PLAY_SUPER_STUN", - "S_PLAY_SUPER_DEAD", - "S_PLAY_SUPER_DRWN", - "S_PLAY_SUPER_SPIN", - "S_PLAY_SUPER_GASP", - "S_PLAY_SUPER_JUMP", - "S_PLAY_SUPER_SPRING", - "S_PLAY_SUPER_FALL", - "S_PLAY_SUPER_EDGE", - "S_PLAY_SUPER_RIDE", - "S_PLAY_SUPER_FLOAT", + "S_PLAY_MELEE_LANDING", // SF_SUPER "S_PLAY_SUPERTRANS1", @@ -3675,7 +3681,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_PLAY_NIGHTS_STAND", "S_PLAY_NIGHTS_FLOAT", - "S_PLAY_NIGHTS_PAIN", + "S_PLAY_NIGHTS_STUN", "S_PLAY_NIGHTS_PULL", "S_PLAY_NIGHTS_ATTACK", @@ -5600,14 +5606,18 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FOUR2", "S_FIVE2", + "S_LOCKON1", + "S_LOCKON2", + // Tag Sign - "S_TTAG1", + "S_TTAG", // Got Flag Sign - "S_GOTFLAG1", - "S_GOTFLAG2", - "S_GOTFLAG3", - "S_GOTFLAG4", + "S_GOTFLAG", + "S_GOTREDFLAG", + "S_GOTBLUEFLAG", + + "S_CORK", // Red Ring "S_RRNG1", @@ -6398,9 +6408,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_SCORE", // score logo "MT_DROWNNUMBERS", // Drowning Timer "MT_GOTEMERALD", // Chaos Emerald (intangible) + "MT_LOCKON", // Target "MT_TAG", // Tag Sign "MT_GOTFLAG", // Got Flag sign - "MT_GOTFLAG2", // Got Flag sign // Ambient Sounds "MT_AWATERA", // Ambient Water Sound 1 @@ -6414,6 +6424,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_RANDOMAMBIENT", "MT_RANDOMAMBIENT2", + "MT_CORK", + // Ring Weapons "MT_REDRING", "MT_BOUNCERING", @@ -6637,10 +6649,8 @@ static const char *const PLAYERFLAG_LIST[] = { // Did you get a time-over? "TIMEOVER", - // Ready for Super? - "SUPERREADY", - // Character action status + "STARTJUMP", "JUMPED", "SPINNING", "STARTDASH", @@ -6652,12 +6662,11 @@ static const char *const PLAYERFLAG_LIST[] = { // Sliding (usually in water) like Labyrinth/Oil Ocean "SLIDING", - /*** NIGHTS STUFF ***/ - // Is the player in NiGHTS mode? - "NIGHTSMODE", - "TRANSFERTOCLOSEST", + // Bouncing + "BOUNCING", - // Spill rings after falling + /*** NIGHTS STUFF ***/ + "TRANSFERTOCLOSEST", "NIGHTSFALL", "DRILLING", "SKIDDOWN", @@ -6671,7 +6680,7 @@ static const char *const PLAYERFLAG_LIST[] = { "ANALOGMODE", // Analog mode? "CANCARRY", // Can carry? "SHIELDABILITY", // Thokked with shield ability - "FORCEJUMPDAMAGE", // Force jump damage + "NOJUMPDAMAGE", // No jump damage NULL // stop loop here. }; @@ -6820,8 +6829,7 @@ static const char *const POWERS_LIST[] = { "NIGHTS_LINKFREEZE", //for linedef exec 427 - "NOCONTROL", - "INGOOP" // In goop + "NOCONTROL" }; static const char *const HUDITEMS_LIST[] = { @@ -6913,14 +6921,15 @@ struct { // Frame settings {"FF_FRAMEMASK",FF_FRAMEMASK}, - {"FF_VERTICALFLIP",FF_VERTICALFLIP}, - {"FF_PAPERSPRITE",FF_PAPERSPRITE}, + {"FF_SPR2SUPER",FF_SPR2SUPER}, {"FF_SPR2ENDSTATE",FF_SPR2ENDSTATE}, {"FF_SPR2MIDSTART",FF_SPR2MIDSTART}, {"FF_ANIMATE",FF_ANIMATE}, {"FF_RANDOMANIM",FF_RANDOMANIM}, {"FF_GLOBALANIM",FF_GLOBALANIM}, {"FF_FULLBRIGHT",FF_FULLBRIGHT}, + {"FF_VERTICALFLIP",FF_VERTICALFLIP}, + {"FF_PAPERSPRITE",FF_PAPERSPRITE}, {"FF_TRANSMASK",FF_TRANSMASK}, {"FF_TRANSSHIFT",FF_TRANSSHIFT}, // new preshifted translucency (used in source) @@ -6983,6 +6992,7 @@ struct { {"LF2_RECORDATTACK",LF2_RECORDATTACK}, {"LF2_NIGHTSATTACK",LF2_NIGHTSATTACK}, {"LF2_NOVISITNEEDED",LF2_NOVISITNEEDED}, + {"LF2_WIDEICON",LF2_WIDEICON}, // NiGHTS grades {"GRADE_F",GRADE_F}, @@ -7044,6 +7054,8 @@ struct { {"CR_NONE",CR_NONE}, {"CR_GENERIC",CR_GENERIC}, {"CR_PLAYER",CR_PLAYER}, + {"CR_NIGHTSMODE",CR_NIGHTSMODE}, + {"CR_BRAKGOOP",CR_BRAKGOOP}, {"CR_ZOOMTUBE",CR_ZOOMTUBE}, {"CR_ROPEHANG",CR_ROPEHANG}, {"CR_MACESPIN",CR_MACESPIN}, @@ -7059,8 +7071,8 @@ struct { // Character flags (skinflags_t) {"SF_SUPER",SF_SUPER}, - {"SF_SUPERANIMS",SF_SUPERANIMS}, - {"SF_SUPERSPIN",SF_SUPERSPIN}, + {"SF_NOSUPERSPIN",SF_NOSUPERSPIN}, + {"SF_NOSPINDASHDUST",SF_NOSPINDASHDUST}, {"SF_HIRES",SF_HIRES}, {"SF_NOSKID",SF_NOSKID}, {"SF_NOSPEEDADJUST",SF_NOSPEEDADJUST}, @@ -7070,7 +7082,9 @@ struct { {"SF_STOMPDAMAGE",SF_STOMPDAMAGE}, {"SF_MARIODAMAGE",SF_MARIODAMAGE}, {"SF_MACHINE",SF_MACHINE}, - {"SF_NOSPINDASHDUST",SF_NOSPINDASHDUST}, + {"SF_DASHMODE",SF_DASHMODE}, + {"SF_FASTEDGE",SF_FASTEDGE}, + {"SF_MULTIABILITY",SF_MULTIABILITY}, // Character abilities! // Primary @@ -7088,12 +7102,12 @@ struct { {"CA_JUMPBOOST",CA_JUMPBOOST}, {"CA_AIRDRILL",CA_AIRDRILL}, {"CA_JUMPTHOK",CA_JUMPTHOK}, - {"CA_DASHMODE",CA_DASHMODE}, + {"CA_BOUNCE",CA_BOUNCE}, {"CA_TWINSPIN",CA_TWINSPIN}, // Secondary {"CA2_NONE",CA2_NONE}, // now slot 0! {"CA2_SPINDASH",CA2_SPINDASH}, - {"CA2_MULTIABILITY",CA2_MULTIABILITY}, + {"CA2_GUNSLINGER",CA2_GUNSLINGER}, {"CA2_MELEE",CA2_MELEE}, // Sound flags @@ -7104,7 +7118,12 @@ struct { {"SF_X8AWAYSOUND",SF_X8AWAYSOUND}, {"SF_NOINTERRUPT",SF_NOINTERRUPT}, {"SF_X2AWAYSOUND",SF_X2AWAYSOUND}, - + + // Map emblem var flags + {"ME_ALLEMERALDS",ME_ALLEMERALDS}, + {"ME_ULTIMATE",ME_ULTIMATE}, + {"ME_PERFECT",ME_PERFECT}, + #ifdef HAVE_BLUA // p_local.h constants {"FLOATSPEED",FLOATSPEED}, @@ -7157,7 +7176,7 @@ struct { {"PA_EDGE",PA_EDGE}, {"PA_WALK",PA_WALK}, {"PA_RUN",PA_RUN}, - {"PA_PEEL",PA_PEEL}, + {"PA_DASH",PA_DASH}, {"PA_PAIN",PA_PAIN}, {"PA_ROLL",PA_ROLL}, {"PA_JUMP",PA_JUMP}, @@ -7346,6 +7365,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}, diff --git a/src/doomdef.h b/src/doomdef.h index 3e3dbfff3..be7513f3e 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -222,7 +222,7 @@ extern FILE *logstream; // NOTE: it needs more than this to increase the number of players... #define MAXPLAYERS 32 -#define MAXSKINS MAXPLAYERS +#define MAXSKINS 32 #define PLAYERSMASK (MAXPLAYERS-1) #define MAXPLAYERNAME 21 @@ -539,4 +539,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Experimental attempts at preventing MF_PAPERCOLLISION objects from getting stuck in walls. //#define PAPER_COLLISIONCORRECTION +/// Hudname padding. +#define SKINNAMEPADDING + #endif // __DOOMDEF__ diff --git a/src/doomstat.h b/src/doomstat.h index f1b7d2169..7ee0382b2 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -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; diff --git a/src/g_game.c b/src/g_game.c index 1eef85ada..8bd71d123 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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. @@ -1015,9 +1014,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics) if (cv_analog.value || twodlevel || (player->mo && (player->mo->flags2 & MF2_TWOD)) || (!demoplayback && (player->climbing - || (player->pflags & PF_NIGHTSMODE) - || (player->pflags & PF_SLIDING) - || (player->pflags & PF_FORCESTRAFE)))) // Analog + || (player->powers[pw_carry] == CR_NIGHTSMODE) + || (player->pflags & (PF_SLIDING|PF_FORCESTRAFE))))) // Analog forcestrafe = true; if (forcestrafe) // Analog { @@ -1120,7 +1118,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics) cmd->buttons |= BT_USE; // Camera Controls - if (cv_debug || cv_analog.value || demoplayback || objectplacing || player->pflags & PF_NIGHTSMODE) + if (cv_debug || cv_analog.value || demoplayback || objectplacing || player->powers[pw_carry] == CR_NIGHTSMODE) { if (PLAYER1INPUTDOWN(gc_camleft)) cmd->buttons |= BT_CAMLEFT; @@ -1306,9 +1304,8 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics) if (cv_analog2.value || twodlevel || (player->mo && (player->mo->flags2 & MF2_TWOD)) || player->climbing - || (player->pflags & PF_NIGHTSMODE) - || (player->pflags & PF_SLIDING) - || (player->pflags & PF_FORCESTRAFE)) // Analog + || (player->powers[pw_carry] == CR_NIGHTSMODE) + || (player->pflags & (PF_SLIDING|PF_FORCESTRAFE))) // Analog forcestrafe = true; if (forcestrafe) // Analog { @@ -1408,7 +1405,7 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics) cmd->buttons |= BT_USE; // Camera Controls - if (cv_debug || cv_analog2.value || player->pflags & PF_NIGHTSMODE) + if (cv_debug || cv_analog2.value || player->powers[pw_carry] == CR_NIGHTSMODE) { if (PLAYER2INPUTDOWN(gc_camleft)) cmd->buttons |= BT_CAMLEFT; @@ -1566,11 +1563,6 @@ static void Analog_OnChange(void) // cameras are not initialized at this point - if (leveltime > 1) - CV_SetValue(&cv_cam_dist, 128); - if (cv_analog.value || demoplayback) - CV_SetValue(&cv_cam_dist, 192); - if (!cv_chasecam.value && cv_analog.value) { CV_SetValue(&cv_analog, 0); return; @@ -1591,11 +1583,6 @@ static void Analog2_OnChange(void) // cameras are not initialized at this point - if (leveltime > 1) - CV_SetValue(&cv_cam2_dist, 128); - if (cv_analog2.value) - CV_SetValue(&cv_cam2_dist, 192); - if (!cv_chasecam2.value && cv_analog2.value) { CV_SetValue(&cv_analog2, 0); return; @@ -2082,6 +2069,7 @@ void G_PlayerReborn(INT32 player) UINT8 mare; UINT8 skincolor; INT32 skin; + UINT32 availabilities; tic_t jointime; boolean spectator; INT16 bot; @@ -2106,6 +2094,7 @@ void G_PlayerReborn(INT32 player) skincolor = players[player].skincolor; skin = players[player].skin; + availabilities = players[player].availabilities; camerascale = players[player].camerascale; shieldscale = players[player].shieldscale; charability = players[player].charability; @@ -2151,6 +2140,7 @@ void G_PlayerReborn(INT32 player) // save player config truth reborn p->skincolor = skincolor; p->skin = skin; + p->availabilities = availabilities; p->camerascale = camerascale; p->shieldscale = shieldscale; p->charability = charability; @@ -3541,7 +3531,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; @@ -3571,7 +3561,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) @@ -3585,13 +3576,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 @@ -3920,12 +3914,8 @@ void G_WriteGhostTic(mobj_t *ghost) if (!(demoflags & DF_GHOST)) return; // No ghost data to write. - if (ghost->player && ghost->player->pflags & PF_NIGHTSMODE && ghost->tracer) - { - // We're talking about the NiGHTS thing, not the normal platforming thing! + if (ghost->player && ghost->player->powers[pw_carry] == CR_NIGHTSMODE) // We're talking about the NiGHTS thing, not the normal platforming thing! ziptic |= GZT_NIGHTS; - ghost = ghost->tracer; - } ziptic_p = demo_p++; // the ziptic, written at the end of this function @@ -4107,11 +4097,9 @@ void G_ConsGhostTic(void) demo_p++; if (ziptic & GZT_SPR2) demo_p++; - if(ziptic & GZT_NIGHTS) { - if (!testmo->player || !(testmo->player->pflags & PF_NIGHTSMODE) || !testmo->tracer) + if (ziptic & GZT_NIGHTS) { + if (!testmo->player || !(testmo->player->powers[pw_carry] == CR_NIGHTSMODE)) nightsfail = true; - else - testmo = testmo->tracer; } if (ziptic & GZT_EXTRA) @@ -5142,7 +5130,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); diff --git a/src/g_game.h b/src/g_game.h index 6d4125517..bfde7698a 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -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 diff --git a/src/g_input.c b/src/g_input.c index b004384c0..a538df06c 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -25,10 +25,10 @@ static CV_PossibleValue_t mousesens_cons_t[] = {{1, "MIN"}, {MAXMOUSESENSITIVITY static CV_PossibleValue_t onecontrolperkey_cons_t[] = {{1, "One"}, {2, "Several"}, {0, NULL}}; // mouse values are used once -consvar_t cv_mousesens = {"mousesens", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mousesens2 = {"mousesens2", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mouseysens = {"mouseysens", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mouseysens2 = {"mouseysens2", "35", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_mousesens = {"mousesens", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_mousesens2 = {"mousesens2", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_mouseysens = {"mouseysens", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_mouseysens2 = {"mouseysens2", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_controlperkey = {"controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; INT32 mousex, mousey; @@ -1154,10 +1154,8 @@ void G_Controldefault(void) #else void G_Controldefault(void) { - gamecontrol[gc_forward ][0] = KEY_UPARROW; - gamecontrol[gc_forward ][1] = 'w'; - gamecontrol[gc_backward ][0] = KEY_DOWNARROW; - gamecontrol[gc_backward ][1] = 's'; + gamecontrol[gc_forward ][0] = 'w'; + gamecontrol[gc_backward ][0] = 's'; gamecontrol[gc_strafeleft ][0] = 'a'; gamecontrol[gc_straferight][0] = 'd'; gamecontrol[gc_turnleft ][0] = KEY_LEFTARROW; @@ -1178,19 +1176,18 @@ void G_Controldefault(void) gamecontrol[gc_fire ][1] = KEY_MOUSE1+0; gamecontrol[gc_firenormal ][0] = 'c'; gamecontrol[gc_tossflag ][0] = '\''; - gamecontrol[gc_use ][0] = 'x'; + gamecontrol[gc_use ][0] = KEY_LSHIFT; gamecontrol[gc_camtoggle ][0] = 'v'; gamecontrol[gc_camleft ][0] = '['; gamecontrol[gc_camright ][0] = ']'; gamecontrol[gc_camreset ][0] = 'r'; - gamecontrol[gc_lookup ][0] = KEY_PGUP; - gamecontrol[gc_lookdown ][0] = KEY_PGDN; + gamecontrol[gc_lookup ][0] = KEY_UPARROW; + gamecontrol[gc_lookdown ][0] = KEY_DOWNARROW; gamecontrol[gc_centerview ][0] = KEY_END; gamecontrol[gc_talkkey ][0] = 't'; gamecontrol[gc_teamkey ][0] = 'y'; gamecontrol[gc_scores ][0] = KEY_TAB; - gamecontrol[gc_jump ][0] = 'z'; - gamecontrol[gc_jump ][1] = KEY_MOUSE1+1; + gamecontrol[gc_jump ][0] = KEY_SPACE; gamecontrol[gc_console ][0] = KEY_CONSOLE; gamecontrol[gc_pause ][0] = KEY_PAUSE; #ifdef WMINPUT diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index f23753ee5..a00bf3aeb 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -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 diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index cdd778caa..a49a788e6 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -404,7 +404,7 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_SPLA &lspr[NOLIGHT], // SPR_SMOK &lspr[NOLIGHT], // SPR_BUBL - &lspr[SUPERSPARK_L], // SPR_WZAP + &lspr[RINGLIGHT_L], // SPR_WZAP &lspr[SUPERSPARK_L], // SPR_TFOG &lspr[NIGHTSLIGHT_L], // SPR_SEED // Sonic CD flower seed &lspr[NOLIGHT], // SPR_PRTL @@ -412,9 +412,12 @@ light_t *t_lspr[NUMSPRITES] = // Game Indicators &lspr[NOLIGHT], // SPR_SCOR &lspr[NOLIGHT], // SPR_DRWN + &lspr[NOLIGHT], // SPR_LCKN &lspr[NOLIGHT], // SPR_TTAG &lspr[NOLIGHT], // SPR_GFLG + &lspr[NOLIGHT], // SPR_CORK + // Ring Weapons &lspr[RINGLIGHT_L], // SPR_RRNG &lspr[RINGLIGHT_L], // SPR_RNGB diff --git a/src/hu_stuff.c b/src/hu_stuff.c index de6561337..f6275631c 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1226,9 +1226,9 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I } else { - if (players[tab[i].num].powers[pw_super]) + if (players[tab[i].num].powers[pw_super] && players[tab[i].num].mo && (players[tab[i].num].mo->state < &states[S_PLAY_SUPER_TRANS] || players[tab[i].num].mo->state > &states[S_PLAY_SUPER_TRANS9])) { - colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE); + colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); V_DrawSmallMappedPatch (x, y-4, 0, superprefix[players[tab[i].num].skin], colormap); } else diff --git a/src/info.c b/src/info.c index 6b3019781..7d6e1fede 100644 --- a/src/info.c +++ b/src/info.c @@ -301,9 +301,12 @@ char sprnames[NUMSPRITES + 1][5] = // Game Indicators "SCOR", // Score logo "DRWN", // Drowning Timer + "LCKN", // Target "TTAG", // Tag Sign "GFLG", // Got Flag sign + "CORK", + // Ring Weapons "RRNG", // Red Ring "RNGB", // Bounce Ring @@ -390,12 +393,12 @@ char spr2names[NUMPLAYERSPRITES][5] = "WAIT", "WALK", "RUN_", - "PEEL", + "DASH", "PAIN", + "STUN", "DEAD", "DRWN", - "SPIN", - "DASH", + "ROLL", "GASP", "JUMP", "SPNG", @@ -403,8 +406,7 @@ char spr2names[NUMPLAYERSPRITES][5] = "EDGE", "RIDE", - "SIGN", - "LIFE", + "SPIN", "FLY_", "SWIM", @@ -414,32 +416,24 @@ char spr2names[NUMPLAYERSPRITES][5] = "CLNG", "CLMB", + "FLT_", + "FRUN", + + "BNCE", + "BLND", + + "FIRE", + "TWIN", "MLEE", + "MLEL", "TRNS", - "SSTD", - "SWLK", - "SRUN", - "SPEE", - "SPAN", - "SSTN", - "SDTH", - "SDRN", - "SSPN", - "SGSP", - "SJMP", - "SSPG", - "SFAL", - "SEDG", - "SRID", - "SFLT", - "NTRN", "NSTD", "NFLT", - "NPAN", + "NSTN", "NPUL", "NATK", @@ -469,7 +463,10 @@ char spr2names[NUMPLAYERSPRITES][5] = "DRL9", "DRLA", "DRLB", - "DRLC" + "DRLC", + + "SIGN", + "LIFE" }; enum playersprite free_spr2 = SPR2_FIRSTFREESLOT; @@ -503,19 +500,22 @@ state_t states[NUMSTATES] = {SPR_PLAY, SPR2_WAIT|FF_ANIMATE, -1, {NULL}, 0, 16, S_NULL}, // S_PLAY_WAIT {SPR_PLAY, SPR2_WALK, 4, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_WALK {SPR_PLAY, SPR2_RUN , 2, {NULL}, 0, 0, S_PLAY_RUN}, // S_PLAY_RUN - {SPR_PLAY, SPR2_PEEL, 2, {NULL}, 0, 0, S_PLAY_PEEL}, // S_PLAY_PEEL + {SPR_PLAY, SPR2_DASH, 2, {NULL}, 0, 0, S_PLAY_DASH}, // S_PLAY_DASH {SPR_PLAY, SPR2_PAIN|FF_ANIMATE, 350, {NULL}, 0, 4, S_PLAY_FALL}, // S_PLAY_PAIN + {SPR_PLAY, SPR2_STUN|FF_ANIMATE, 350, {NULL}, 0, 4, S_PLAY_FALL}, // S_PLAY_STUN {SPR_PLAY, SPR2_DEAD|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_DEAD {SPR_PLAY, SPR2_DRWN|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_DRWN - {SPR_PLAY, SPR2_SPIN, 1, {NULL}, 0, 0, S_PLAY_SPIN}, // S_PLAY_SPIN - {SPR_PLAY, SPR2_DASH, 2, {NULL}, 0, 0, S_PLAY_DASH}, // S_PLAY_DASH + {SPR_PLAY, SPR2_ROLL, 1, {NULL}, 0, 0, S_PLAY_ROLL}, // S_PLAY_ROLL {SPR_PLAY, SPR2_GASP|FF_ANIMATE, 14, {NULL}, 0, 4, S_PLAY_WALK}, // S_PLAY_GASP {SPR_PLAY, SPR2_JUMP, 1, {NULL}, 0, 0, S_PLAY_JUMP}, // S_PLAY_JUMP {SPR_PLAY, SPR2_SPNG, 2, {NULL}, 0, 0, S_PLAY_SPRING}, // S_PLAY_SPRING {SPR_PLAY, SPR2_FALL, 2, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_FALL - {SPR_PLAY, SPR2_EDGE|FF_ANIMATE, -1, {NULL}, 0, 12, S_NULL}, // S_PLAY_EDGE + {SPR_PLAY, SPR2_EDGE, 12, {NULL}, 0, 0, S_PLAY_EDGE}, // S_PLAY_EDGE {SPR_PLAY, SPR2_RIDE, 4, {NULL}, 0, 0, S_PLAY_RIDE}, // S_PLAY_RIDE + // CA2_SPINDASH + {SPR_PLAY, SPR2_SPIN, 2, {NULL}, 0, 0, S_PLAY_SPINDASH}, // S_PLAY_SPINDASH + // CA_FLY/CA_SWIM {SPR_PLAY, SPR2_FLY , 2, {NULL}, 0, 0, S_PLAY_FLY}, // S_PLAY_FLY {SPR_PLAY, SPR2_SWIM, 2, {NULL}, 0, 0, S_PLAY_SWIM}, // S_PLAY_SWIM @@ -526,45 +526,40 @@ state_t states[NUMSTATES] = {SPR_PLAY, SPR2_CLNG|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_CLING {SPR_PLAY, SPR2_CLMB, 5, {NULL}, 0, 0, S_PLAY_CLIMB}, // S_PLAY_CLIMB + // CA_FLOAT/CA_SLOWFALL + {SPR_PLAY, SPR2_FLT , 7, {NULL}, 0, 0, S_PLAY_FLOAT}, // S_PLAY_FLOAT + {SPR_PLAY, SPR2_FRUN, 7, {NULL}, 0, 0, S_PLAY_FLOAT_RUN}, // S_PLAY_FLOAT_RUN + + // CA_BOUNCE + {SPR_PLAY, SPR2_BNCE|FF_ANIMATE, -1, {NULL}, 0, 0, S_NULL}, // S_PLAY_BOUNCE + {SPR_PLAY, SPR2_BLND|FF_SPR2ENDSTATE, 2, {NULL}, S_PLAY_BOUNCE, 0, S_PLAY_BOUNCE_LANDING}, // S_PLAY_BOUNCE_LANDING + + // CA2_GUNSLINGER + {SPR_PLAY, SPR2_FIRE|FF_SPR2ENDSTATE, 2, {NULL}, S_PLAY_FIRE_FINISH, 0, S_PLAY_FIRE}, // S_PLAY_FIRE + {SPR_PLAY, SPR2_FIRE, 15, {NULL}, S_PLAY_STND, 0, S_PLAY_STND}, // S_PLAY_FIRE_FINISH + // CA_TWINSPIN {SPR_PLAY, SPR2_TWIN|FF_SPR2ENDSTATE, 1, {NULL}, S_PLAY_JUMP, 0, S_PLAY_TWINSPIN}, // S_PLAY_TWINSPIN // CA2_MELEE {SPR_PLAY, SPR2_MLEE|FF_SPR2ENDSTATE, 1, {NULL}, S_PLAY_MELEE_FINISH, 0, S_PLAY_MELEE}, // S_PLAY_MELEE - {SPR_PLAY, SPR2_MLEE, 20, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_MELEE_FINISH - - // SF_SUPERANIMS - {SPR_PLAY, SPR2_SSTD|FF_ANIMATE, -1, {NULL}, 0, 7, S_NULL}, // S_PLAY_SUPER_STND - {SPR_PLAY, SPR2_SWLK, 7, {NULL}, 0, 0, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_WALK - {SPR_PLAY, SPR2_SRUN, 7, {NULL}, 0, 0, S_PLAY_SUPER_RUN}, // S_PLAY_SUPER_RUN - {SPR_PLAY, SPR2_SPEE, 7, {NULL}, 0, 0, S_PLAY_SUPER_PEEL}, // S_PLAY_SUPER_PEEL - {SPR_PLAY, SPR2_SPAN|FF_ANIMATE, 350, {NULL}, 0, 4, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_PAIN - {SPR_PLAY, SPR2_SSTN|FF_ANIMATE, 350, {NULL}, 0, 4, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_STUN - {SPR_PLAY, SPR2_SDTH|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_SUPER_DEAD - {SPR_PLAY, SPR2_SDRN|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_SUPER_DRWN - {SPR_PLAY, SPR2_SSPN, 1, {NULL}, 0, 0, S_PLAY_SUPER_SPIN}, // S_PLAY_SUPER_SPIN - {SPR_PLAY, SPR2_SGSP|FF_ANIMATE, 14, {NULL}, 0, 4, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_GASP - {SPR_PLAY, SPR2_SJMP, 1, {NULL}, 0, 0, S_PLAY_SUPER_JUMP}, // S_PLAY_SUPER_JUMP - {SPR_PLAY, SPR2_SSPG, 2, {NULL}, 0, 0, S_PLAY_SUPER_SPRING}, // S_PLAY_SUPER_SPRING - {SPR_PLAY, SPR2_SFAL, 2, {NULL}, 0, 0, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_FALL - {SPR_PLAY, SPR2_SEDG|FF_ANIMATE, -1, {NULL}, 0, 12, S_NULL}, // S_PLAY_SUPER_EDGE - {SPR_PLAY, SPR2_SRID, 4, {NULL}, 0, 0, S_PLAY_SUPER_RIDE}, // S_PLAY_SUPER_RIDE - {SPR_PLAY, SPR2_SFLT, 7, {NULL}, 0, 0, S_PLAY_SUPER_FLOAT}, // S_PLAY_SUPER_FLOAT + {SPR_PLAY, SPR2_MLEE, 70, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_MELEE_FINISH + {SPR_PLAY, SPR2_MLEL, 35, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_MELEE_LANDING // SF_SUPER - {SPR_PLAY, SPR2_TRNS, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS - {SPR_PLAY, SPR2_TRNS, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS3}, // S_PLAY_SUPER_TRANS2 - {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS4}, // S_PLAY_SUPER_TRANS3 - {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS5}, // S_PLAY_SUPER_TRANS4 - {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS6}, // S_PLAY_SUPER_TRANS5 - {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS7}, // S_PLAY_SUPER_TRANS6 - {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS8}, // S_PLAY_SUPER_TRANS7 - {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS9}, // S_PLAY_SUPER_TRANS8 - {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 16, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_SUPER_TRANS9 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS3}, // S_PLAY_SUPER_TRANS2 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS4}, // S_PLAY_SUPER_TRANS3 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS5}, // S_PLAY_SUPER_TRANS4 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS6}, // S_PLAY_SUPER_TRANS5 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS7}, // S_PLAY_SUPER_TRANS6 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS8}, // S_PLAY_SUPER_TRANS7 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS9}, // S_PLAY_SUPER_TRANS8 + {SPR_PLAY, SPR2_TRNS|FF_SPR2SUPER|FF_FULLBRIGHT, 16, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_SUPER_TRANS9 {SPR_NULL, 0, -1, {NULL}, 0, 0, S_OBJPLACE_DUMMY}, //S_OBJPLACE_DUMMY - // 1-Up Box Sprites (uses player sprite) + // 1-Up box sprites (uses player sprite) {SPR_PLAY, SPR2_LIFE, 2, {NULL}, 0, 16, S_PLAY_BOX2}, // S_PLAY_BOX1 {SPR_NULL, 0, 1, {NULL}, 0, 0, S_PLAY_BOX1}, // S_PLAY_BOX2 {SPR_PLAY, SPR2_LIFE, 4, {NULL}, 0, 4, S_PLAY_ICON2}, // S_PLAY_ICON1 @@ -575,50 +570,50 @@ state_t states[NUMSTATES] = {SPR_PLAY, SPR2_SIGN, 1, {NULL}, 0, 24, S_PLAY_SIGN}, // S_PLAY_SIGN // NiGHTS Player, transforming - {SPR_PLAY, SPR2_NTRN, 4, {A_Scream}, 0, 0, S_PLAY_NIGHTS_TRANS2}, // S_PLAY_NIGHTS_TRANS - {SPR_PLAY, SPR2_NTRN, 4, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS3}, // S_PLAY_NIGHTS_TRANS2 - {SPR_PLAY, SPR2_NTRN|FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS4}, // S_PLAY_NIGHTS_TRANS3 - {SPR_PLAY, SPR2_NTRN, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS5}, // S_PLAY_NIGHTS_TRANS4 - {SPR_PLAY, SPR2_NTRN, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS6}, // S_PLAY_NIGHTS_TRANS5 - {SPR_PLAY, SPR2_NTRN, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS7}, // S_PLAY_NIGHTS_TRANS6 - {SPR_PLAY, SPR2_NTRN, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS8}, // S_PLAY_NIGHTS_TRANS7 - {SPR_PLAY, SPR2_NTRN, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS9}, // S_PLAY_NIGHTS_TRANS8 - {SPR_PLAY, SPR2_NTRN, 16, {NULL}, 0, 0, S_PLAY_NIGHTS_FLOAT}, // S_PLAY_NIGHTS_TRANS9 + {SPR_PLAY, SPR2_TRNS, 4, {A_Scream}, 0, 0, S_PLAY_NIGHTS_TRANS2}, // S_PLAY_NIGHTS_TRANS + {SPR_PLAY, SPR2_TRNS, 4, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS3}, // S_PLAY_NIGHTS_TRANS2 + {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS4}, // S_PLAY_NIGHTS_TRANS3 + {SPR_PLAY, SPR2_TRNS, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS5}, // S_PLAY_NIGHTS_TRANS4 + {SPR_PLAY, SPR2_TRNS, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS6}, // S_PLAY_NIGHTS_TRANS5 + {SPR_PLAY, SPR2_TRNS, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS7}, // S_PLAY_NIGHTS_TRANS6 + {SPR_PLAY, SPR2_TRNS, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS8}, // S_PLAY_NIGHTS_TRANS7 + {SPR_PLAY, SPR2_TRNS, 3, {NULL}, 0, 0, S_PLAY_NIGHTS_TRANS9}, // S_PLAY_NIGHTS_TRANS8 + {SPR_PLAY, SPR2_TRNS, 16, {NULL}, 0, 0, S_PLAY_NIGHTS_FLOAT}, // S_PLAY_NIGHTS_TRANS9 - // NiGHTS Player, Stand, Floating, Pain, Pull and Attack - {SPR_PLAY, SPR2_NSTD, 7, {NULL}, 0, 0, S_PLAY_NIGHTS_STAND}, // S_PLAY_NIGHTS_STAND - {SPR_PLAY, SPR2_NFLT, 7, {NULL}, 0, 0, S_PLAY_NIGHTS_FLOAT}, // S_PLAY_NIGHTS_FLOAT - {SPR_PLAY, SPR2_NPAN, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_PAIN}, // S_PLAY_NIGHTS_PAIN - {SPR_PLAY, SPR2_NPUL, 1, {NULL}, 0, 0, S_PLAY_NIGHTS_PULL}, // S_PLAY_NIGHTS_PULL + // NiGHTS Player, stand, float, pain, pull and attack + {SPR_PLAY, SPR2_NSTD, 7, {NULL}, 0, 0, S_PLAY_NIGHTS_STAND}, // S_PLAY_NIGHTS_STAND + {SPR_PLAY, SPR2_NFLT, 7, {NULL}, 0, 0, S_PLAY_NIGHTS_FLOAT}, // S_PLAY_NIGHTS_FLOAT + {SPR_PLAY, SPR2_NSTN, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_STUN}, // S_PLAY_NIGHTS_STUN + {SPR_PLAY, SPR2_NPUL, 1, {NULL}, 0, 0, S_PLAY_NIGHTS_PULL}, // S_PLAY_NIGHTS_PULL {SPR_PLAY, SPR2_NATK, 1, {NULL}, 0, 0, S_PLAY_NIGHTS_ATTACK}, // S_PLAY_NIGHTS_ATTACK - // NiGHTS Player, Flying and Drilling - {SPR_PLAY, SPR2_NGT0, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY0}, // S_PLAY_NIGHTS_FLY0 - {SPR_PLAY, SPR2_DRL0, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL0}, // S_PLAY_NIGHTS_DRILL0 - {SPR_PLAY, SPR2_NGT1, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY1}, // S_PLAY_NIGHTS_FLY1 - {SPR_PLAY, SPR2_DRL1, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL1}, // S_PLAY_NIGHTS_DRILL1 - {SPR_PLAY, SPR2_NGT2, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY2}, // S_PLAY_NIGHTS_FLY2 - {SPR_PLAY, SPR2_DRL2, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL2}, // S_PLAY_NIGHTS_DRILL2 - {SPR_PLAY, SPR2_NGT3, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY3}, // S_PLAY_NIGHTS_FLY3 - {SPR_PLAY, SPR2_DRL3, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL3}, // S_PLAY_NIGHTS_DRILL3 - {SPR_PLAY, SPR2_NGT4, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY4}, // S_PLAY_NIGHTS_FLY4 - {SPR_PLAY, SPR2_DRL4, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL4}, // S_PLAY_NIGHTS_DRILL4 - {SPR_PLAY, SPR2_NGT5, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY5}, // S_PLAY_NIGHTS_FLY5 - {SPR_PLAY, SPR2_DRL5, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL5}, // S_PLAY_NIGHTS_DRILL5 - {SPR_PLAY, SPR2_NGT6, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY6}, // S_PLAY_NIGHTS_FLY6 - {SPR_PLAY, SPR2_DRL6, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL6}, // S_PLAY_NIGHTS_DRILL6 - {SPR_PLAY, SPR2_NGT7, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY7}, // S_PLAY_NIGHTS_FLY7 - {SPR_PLAY, SPR2_DRL7, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL7}, // S_PLAY_NIGHTS_DRILL7 - {SPR_PLAY, SPR2_NGT8, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY8}, // S_PLAY_NIGHTS_FLY8 - {SPR_PLAY, SPR2_DRL8, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL8}, // S_PLAY_NIGHTS_DRILL8 - {SPR_PLAY, SPR2_NGT9, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY9}, // S_PLAY_NIGHTS_FLY9 - {SPR_PLAY, SPR2_DRL9, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL9}, // S_PLAY_NIGHTS_DRILL9 - {SPR_PLAY, SPR2_NGTA, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLYA}, // S_PLAY_NIGHTS_FLYA - {SPR_PLAY, SPR2_DRLA, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILLA}, // S_PLAY_NIGHTS_DRILLA - {SPR_PLAY, SPR2_NGTB, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLYB}, // S_PLAY_NIGHTS_FLYB - {SPR_PLAY, SPR2_DRLB, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILLB}, // S_PLAY_NIGHTS_DRILLB - {SPR_PLAY, SPR2_NGTC, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLYC}, // S_PLAY_NIGHTS_FLYC - {SPR_PLAY, SPR2_DRLC, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILLC}, // S_PLAY_NIGHTS_DRILLC + // NiGHTS Player, flying and drilling + {SPR_PLAY, SPR2_NGT0, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY0}, // S_PLAY_NIGHTS_FLY0 + {SPR_PLAY, SPR2_DRL0, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL0}, // S_PLAY_NIGHTS_DRILL0 + {SPR_PLAY, SPR2_NGT1, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY1}, // S_PLAY_NIGHTS_FLY1 + {SPR_PLAY, SPR2_DRL1, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL1}, // S_PLAY_NIGHTS_DRILL1 + {SPR_PLAY, SPR2_NGT2, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY2}, // S_PLAY_NIGHTS_FLY2 + {SPR_PLAY, SPR2_DRL2, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL2}, // S_PLAY_NIGHTS_DRILL2 + {SPR_PLAY, SPR2_NGT3, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY3}, // S_PLAY_NIGHTS_FLY3 + {SPR_PLAY, SPR2_DRL3, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL3}, // S_PLAY_NIGHTS_DRILL3 + {SPR_PLAY, SPR2_NGT4, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY4}, // S_PLAY_NIGHTS_FLY4 + {SPR_PLAY, SPR2_DRL4, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL4}, // S_PLAY_NIGHTS_DRILL4 + {SPR_PLAY, SPR2_NGT5, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY5}, // S_PLAY_NIGHTS_FLY5 + {SPR_PLAY, SPR2_DRL5, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL5}, // S_PLAY_NIGHTS_DRILL5 + {SPR_PLAY, SPR2_NGT6, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY6}, // S_PLAY_NIGHTS_FLY6 + {SPR_PLAY, SPR2_DRL6, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL6}, // S_PLAY_NIGHTS_DRILL6 + {SPR_PLAY, SPR2_NGT7, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY7}, // S_PLAY_NIGHTS_FLY7 + {SPR_PLAY, SPR2_DRL7, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL7}, // S_PLAY_NIGHTS_DRILL7 + {SPR_PLAY, SPR2_NGT8, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY8}, // S_PLAY_NIGHTS_FLY8 + {SPR_PLAY, SPR2_DRL8, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL8}, // S_PLAY_NIGHTS_DRILL8 + {SPR_PLAY, SPR2_NGT9, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLY9}, // S_PLAY_NIGHTS_FLY9 + {SPR_PLAY, SPR2_DRL9, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILL9}, // S_PLAY_NIGHTS_DRILL9 + {SPR_PLAY, SPR2_NGTA, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLYA}, // S_PLAY_NIGHTS_FLYA + {SPR_PLAY, SPR2_DRLA, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILLA}, // S_PLAY_NIGHTS_DRILLA + {SPR_PLAY, SPR2_NGTB, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLYB}, // S_PLAY_NIGHTS_FLYB + {SPR_PLAY, SPR2_DRLB, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILLB}, // S_PLAY_NIGHTS_DRILLB + {SPR_PLAY, SPR2_NGTC, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_FLYC}, // S_PLAY_NIGHTS_FLYC + {SPR_PLAY, SPR2_DRLC, 2, {NULL}, 0, 0, S_PLAY_NIGHTS_DRILLC}, // S_PLAY_NIGHTS_DRILLC // Blue Crawla {SPR_POSS, 0, 5, {A_Look}, 0, 0, S_POSS_STND}, // S_POSS_STND @@ -2535,13 +2530,17 @@ state_t states[NUMSTATES] = {SPR_DRWN, 10, 40, {NULL}, 0, 0, S_NULL}, // S_FOUR2 {SPR_DRWN, 11, 40, {NULL}, 0, 0, S_NULL}, // S_FIVE2 - {SPR_TTAG, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_TTAG1 + {SPR_LCKN, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON1 + {SPR_LCKN, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON2 + + {SPR_TTAG, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_TTAG // CTF Sign - {SPR_GFLG, 0, 1, {NULL}, 0, 0, S_GOTFLAG2}, // S_GOTFLAG1 - {SPR_GFLG, 1, 1, {NULL}, 0, 0, S_NULL}, // S_GOTFLAG2 - {SPR_GFLG, 0, 1, {NULL}, 0, 0, S_GOTFLAG4}, // S_GOTFLAG3 - {SPR_GFLG, 2, 1, {NULL}, 0, 0, S_NULL}, // S_GOTFLAG4 + {SPR_GFLG, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_GOTFLAG + {SPR_GFLG, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_GOTREDFLAG + {SPR_GFLG, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_GOTBLUEFLAG + + {SPR_CORK, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CORK // Red Rings (thrown) {SPR_RRNG, FF_FULLBRIGHT, 1, {A_ThrownRing}, 0, 0, S_RRNG2}, // S_RRNG1 @@ -3062,7 +3061,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MT_THOK, // painchance sfx_None, // painsound S_NULL, // meleestate - S_PLAY_SPIN, // missilestate + S_PLAY_ROLL, // missilestate S_PLAY_DEAD, // deathstate S_PLAY_DRWN, // xdeathstate sfx_None, // deathsound @@ -4581,7 +4580,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 30*FRACUNIT, // speed 11*FRACUNIT, // radius 8*FRACUNIT, // height - 0, // display offset + 100, // display offset 100, // mass 0, // damage sfx_None, // activesound @@ -7127,7 +7126,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_ATTRACT_ICON1, // spawnstate 1, // spawnhealth S_NULL, // seestate - sfx_s3k41, // seesound + sfx_attrsg, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate @@ -12032,7 +12031,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // speed 8*FRACUNIT, // radius 8*FRACUNIT, // height - 0, // display offset + 113, // display offset 16, // mass 0, // damage sfx_None, // activesound @@ -12059,7 +12058,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // speed 8*FRACUNIT, // radius 16*FRACUNIT, // height - 0, // display offset + 112, // display offset 16, // mass 0, // damage sfx_None, // activesound @@ -12067,9 +12066,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_TAG + { // MT_LOCKON -1, // doomednum - S_TTAG1, // spawnstate + S_LOCKON1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -12086,7 +12085,34 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // speed 16*FRACUNIT, // radius 32*FRACUNIT, // height - 0, // display offset + 111, // display offset + 16, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_TAG + -1, // doomednum + S_TTAG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 8, // speed + 16*FRACUNIT, // radius + 32*FRACUNIT, // height + 111, // display offset 16, // mass 0, // damage sfx_None, // activesound @@ -12096,7 +12122,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_GOTFLAG -1, // doomednum - S_GOTFLAG1, // spawnstate + S_GOTFLAG, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -12113,34 +12139,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // speed 64*FRACUNIT, // radius 32*FRACUNIT, // height - 0, // display offset - 16, // mass - 0, // damage - sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags - S_NULL // raisestate - }, - - { // MT_GOTFLAG2 - -1, // doomednum - S_GOTFLAG3, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 8, // speed - 64*FRACUNIT, // radius - 32*FRACUNIT, // height - 0, // display offset + 111, // display offset 16, // mass 0, // damage sfx_None, // activesound @@ -12426,6 +12425,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_CORK + -1, // doomednum + S_CORK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_corkp, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_SMOKE1, // deathstate + S_NULL, // xdeathstate + sfx_corkh, // deathsound + 60*FRACUNIT, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // display offset + 100, // mass + 1, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_REDRING -1, // doomednum S_RRNG1, // spawnstate diff --git a/src/info.h b/src/info.h index 0cca7896e..4b21e98ec 100644 --- a/src/info.h +++ b/src/info.h @@ -450,7 +450,7 @@ typedef enum sprite SPR_ARMB, // Armageddon Shield Ring, Back SPR_WIND, // Whirlwind Shield Orb SPR_MAGN, // Attract Shield Orb - SPR_ELEM, // Elemental Shield Orb and Fire + SPR_ELEM, // Elemental Shield Orb SPR_FORC, // Force Shield Orb SPR_PITY, // Pity Shield Orb SPR_FIRS, // Flame Shield Orb @@ -507,9 +507,12 @@ typedef enum sprite // Game Indicators SPR_SCOR, // Score logo SPR_DRWN, // Drowning Timer + SPR_LCKN, // Target SPR_TTAG, // Tag Sign SPR_GFLG, // Got Flag sign + SPR_CORK, + // Ring Weapons SPR_RRNG, // Red Ring SPR_RNGB, // Bounce Ring @@ -594,21 +597,21 @@ typedef enum sprite NUMSPRITES } spritenum_t; -// Make sure to be conscious of FF_FRAMEMASK whenever you change this table. -// Currently, FF_FRAMEMASK is 0x1ff, or 511 - and NUMSPRITEFREESLOTS is 256. -// Since this is zero-based, there can be at most 256 different SPR2_'s without changing that. +// Make sure to be conscious of FF_FRAMEMASK and the fact sprite2 is stored as a UINT8 whenever you change this table. +// Currently, FF_FRAMEMASK is 0xff, or 255 - but the second half is used by FF_SPR2SUPER, so the limitation is 0x7f. +// Since this is zero-based, there can be at most 128 different SPR2_'s without changing that. enum playersprite { SPR2_STND = 0, SPR2_WAIT, SPR2_WALK, SPR2_RUN , - SPR2_PEEL, + SPR2_DASH, SPR2_PAIN, + SPR2_STUN, SPR2_DEAD, SPR2_DRWN, // drown - SPR2_SPIN, - SPR2_DASH, // spindash charge + SPR2_ROLL, SPR2_GASP, SPR2_JUMP, SPR2_SPNG, // spring @@ -616,8 +619,7 @@ enum playersprite SPR2_EDGE, SPR2_RIDE, - SPR2_SIGN, // end sign head - SPR2_LIFE, // life monitor icon + SPR2_SPIN, // spindash SPR2_FLY , SPR2_SWIM, @@ -627,36 +629,28 @@ enum playersprite SPR2_CLNG, // cling SPR2_CLMB, // climb + SPR2_FLT , // float + SPR2_FRUN, // float run + + SPR2_BNCE, // bounce + SPR2_BLND, // bounce landing + + SPR2_FIRE, // fire + SPR2_TWIN, // twinspin SPR2_MLEE, // melee + SPR2_MLEL, // melee land - SPR2_TRNS, // super transformation - SPR2_SSTD, // super stand - SPR2_SWLK, // super walk - SPR2_SRUN, // super run - SPR2_SPEE, // super peelout - SPR2_SPAN, // super pain - SPR2_SSTN, // super stun - SPR2_SDTH, // super death - SPR2_SDRN, // super drown - SPR2_SSPN, // super spin - SPR2_SGSP, // super gasp - SPR2_SJMP, // super jump - SPR2_SSPG, // super spring - SPR2_SFAL, // super fall - SPR2_SEDG, // super edge - SPR2_SRID, // super ride - SPR2_SFLT, // super float + SPR2_TRNS, // transformation - SPR2_NTRN, // NiGHTS transformation SPR2_NSTD, // NiGHTS stand SPR2_NFLT, // NiGHTS float - SPR2_NPAN, // NiGHTS pain + SPR2_NSTN, // NiGHTS stun SPR2_NPUL, // NiGHTS pull SPR2_NATK, // NiGHTS attack - // NiGHTS flight. + // NiGHTS flight SPR2_NGT0, SPR2_NGT1, SPR2_NGT2, @@ -671,7 +665,7 @@ enum playersprite SPR2_NGTB, SPR2_NGTC, - // NiGHTS drill. + // NiGHTS drill SPR2_DRL0, SPR2_DRL1, SPR2_DRL2, @@ -686,8 +680,11 @@ enum playersprite SPR2_DRLB, SPR2_DRLC, + SPR2_SIGN, // end sign head + SPR2_LIFE, // life monitor icon + SPR2_FIRSTFREESLOT, - SPR2_LASTFREESLOT = SPR2_FIRSTFREESLOT + NUMSPRITEFREESLOTS - 1, + SPR2_LASTFREESLOT = 0x7f, NUMPLAYERSPRITES }; @@ -713,19 +710,22 @@ typedef enum state S_PLAY_WAIT, S_PLAY_WALK, S_PLAY_RUN, - S_PLAY_PEEL, + S_PLAY_DASH, S_PLAY_PAIN, + S_PLAY_STUN, S_PLAY_DEAD, S_PLAY_DRWN, - S_PLAY_SPIN, - S_PLAY_DASH, + S_PLAY_ROLL, S_PLAY_GASP, - S_PLAY_JUMP, // spin jump + S_PLAY_JUMP, S_PLAY_SPRING, S_PLAY_FALL, S_PLAY_EDGE, S_PLAY_RIDE, + // CA2_SPINDASH + S_PLAY_SPINDASH, + // CA_FLY/SWIM S_PLAY_FLY, S_PLAY_SWIM, @@ -736,30 +736,25 @@ typedef enum state S_PLAY_CLING, S_PLAY_CLIMB, + // CA_FLOAT/CA_SLOWFALL + S_PLAY_FLOAT, + S_PLAY_FLOAT_RUN, + + // CA_BOUNCE + S_PLAY_BOUNCE, + S_PLAY_BOUNCE_LANDING, + + // CA2_GUNSLINGER + S_PLAY_FIRE, + S_PLAY_FIRE_FINISH, + // CA_TWINSPIN S_PLAY_TWINSPIN, // CA2_MELEE S_PLAY_MELEE, S_PLAY_MELEE_FINISH, - - // SF_SUPERANIMS - S_PLAY_SUPER_STND, - S_PLAY_SUPER_WALK, - S_PLAY_SUPER_RUN, - S_PLAY_SUPER_PEEL, - S_PLAY_SUPER_PAIN, - S_PLAY_SUPER_STUN, - S_PLAY_SUPER_DEAD, - S_PLAY_SUPER_DRWN, - S_PLAY_SUPER_SPIN, - S_PLAY_SUPER_GASP, - S_PLAY_SUPER_JUMP, // see note above - S_PLAY_SUPER_SPRING, - S_PLAY_SUPER_FALL, - S_PLAY_SUPER_EDGE, - S_PLAY_SUPER_RIDE, - S_PLAY_SUPER_FLOAT, + S_PLAY_MELEE_LANDING, // SF_SUPER S_PLAY_SUPER_TRANS, @@ -798,7 +793,7 @@ typedef enum state S_PLAY_NIGHTS_STAND, S_PLAY_NIGHTS_FLOAT, - S_PLAY_NIGHTS_PAIN, + S_PLAY_NIGHTS_STUN, S_PLAY_NIGHTS_PULL, S_PLAY_NIGHTS_ATTACK, @@ -2725,14 +2720,18 @@ typedef enum state S_FOUR2, S_FIVE2, + S_LOCKON1, + S_LOCKON2, + // Tag Sign - S_TTAG1, + S_TTAG, // Got Flag Sign - S_GOTFLAG1, - S_GOTFLAG2, - S_GOTFLAG3, - S_GOTFLAG4, + S_GOTFLAG, + S_GOTREDFLAG, + S_GOTBLUEFLAG, + + S_CORK, // Red Ring S_RRNG1, @@ -3542,9 +3541,9 @@ typedef enum mobj_type MT_SCORE, // score logo MT_DROWNNUMBERS, // Drowning Timer MT_GOTEMERALD, // Chaos Emerald (intangible) + MT_LOCKON, // Target MT_TAG, // Tag Sign MT_GOTFLAG, // Got Flag sign - MT_GOTFLAG2, // Got Flag sign // Ambient Sounds MT_AWATERA, // Ambient Water Sound 1 @@ -3558,6 +3557,8 @@ typedef enum mobj_type MT_RANDOMAMBIENT, MT_RANDOMAMBIENT2, + MT_CORK, + // Ring Weapons MT_REDRING, MT_BOUNCERING, diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 4d48521ca..71f6a7e65 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -445,10 +445,32 @@ static int lib_pIsValidSprite2(lua_State *L) INLEVEL if (!mobj) return LUA_ErrInvalid(L, "mobj_t"); - lua_pushboolean(L, (mobj->skin && (((skin_t *)mobj->skin)->sprites[spr2].numframes > 0))); + lua_pushboolean(L, (mobj->skin && (((skin_t *)mobj->skin)->sprites[spr2].numframes))); return 1; } +// P_SpawnLockOn doesn't exist either, but we want to expose making a local mobj without encouraging hacks. + +static int lib_pSpawnLockOn(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + mobj_t *lockon = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); + statenum_t state = luaL_checkinteger(L, 3); + NOHUD + INLEVEL + if (!lockon) + return LUA_ErrInvalid(L, "mobj_t"); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + if (player == &players[consoleplayer] || player == &players[secondarydisplayplayer] || player == &players[displayplayer]) // Only display it on your own view. + { + mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker + visual->target = lockon; + P_SetMobjStateNF(visual, state); + } + return 0; +} + static int lib_pSpawnMissile(lua_State *L) { mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -793,6 +815,17 @@ static int lib_pStealPlayerScore(lua_State *L) return 0; } +static int lib_pGetJumpFlags(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + NOHUD + INLEVEL + if (!player) + return LUA_ErrInvalid(L, "player_t"); + lua_pushinteger(L, P_GetJumpFlags(player)); + return 1; +} + static int lib_pPlayerInPain(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); @@ -1055,11 +1088,12 @@ static int lib_pLookForEnemies(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); boolean nonenemies = lua_opttrueboolean(L, 2); + boolean bullet = lua_optboolean(L, 3); NOHUD INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); - lua_pushboolean(L, P_LookForEnemies(player, nonenemies)); + LUA_PushUserdata(L, P_LookForEnemies(player, nonenemies, bullet), META_MOBJ); return 1; } @@ -2327,6 +2361,7 @@ static luaL_Reg lib[] = { {"P_SpawnMobj",lib_pSpawnMobj}, {"P_RemoveMobj",lib_pRemoveMobj}, {"P_IsValidSprite2", lib_pIsValidSprite2}, + {"P_SpawnLockOn", lib_pSpawnLockOn}, {"P_SpawnMissile",lib_pSpawnMissile}, {"P_SpawnXYZMissile",lib_pSpawnXYZMissile}, {"P_SpawnPointMissile",lib_pSpawnPointMissile}, @@ -2354,6 +2389,7 @@ static luaL_Reg lib[] = { {"P_GetPlayerControlDirection",lib_pGetPlayerControlDirection}, {"P_AddPlayerScore",lib_pAddPlayerScore}, {"P_StealPlayerScore",lib_pStealPlayerScore}, + {"P_GetJumpFlags",lib_pGetJumpFlags}, {"P_PlayerInPain",lib_pPlayerInPain}, {"P_DoPlayerPain",lib_pDoPlayerPain}, {"P_ResetPlayer",lib_pResetPlayer}, diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 93febf209..2fcccab66 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -511,7 +511,8 @@ static int mobj_set(lua_State *L) for (i = 0; i < numskins; i++) if (fastcmp(skins[i].name, skin)) { - mo->skin = &skins[i]; + if (!mo->player || R_SkinUsable(mo->player-players, i)) + mo->skin = &skins[i]; return 0; } return luaL_error(L, "mobj.skin '%s' not found!", skin); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 0900528ed..622309425 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -194,8 +194,6 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->gotcontinue); else if (fastcmp(field,"speed")) lua_pushfixed(L, plr->speed); - else if (fastcmp(field,"jumping")) - lua_pushboolean(L, plr->jumping); else if (fastcmp(field,"secondjump")) lua_pushinteger(L, plr->secondjump); else if (fastcmp(field,"fly1")) @@ -459,8 +457,6 @@ static int player_set(lua_State *L) plr->gotcontinue = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"speed")) plr->speed = luaL_checkfixed(L, 3); - else if (fastcmp(field,"jumping")) - plr->jumping = luaL_checkboolean(L, 3); else if (fastcmp(field,"secondjump")) plr->secondjump = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"fly1")) diff --git a/src/m_cheat.c b/src/m_cheat.c index ce9519799..8ae670662 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -942,7 +942,7 @@ boolean OP_FreezeObjectplace(void) if (!objectplacing) return false; - if ((maptol & TOL_NIGHTS) && (players[consoleplayer].pflags & PF_NIGHTSMODE)) + if ((maptol & TOL_NIGHTS) && (players[consoleplayer].powers[pw_carry] == CR_NIGHTSMODE)) return false; return true; @@ -1255,7 +1255,7 @@ void Command_ObjectPlace_f(void) { objectplacing = true; - if ((players[0].pflags & PF_NIGHTSMODE)) + if ((players[0].powers[pw_carry] == CR_NIGHTSMODE)) return; if (!COM_CheckParm("-silent")) @@ -1326,7 +1326,7 @@ void Command_ObjectPlace_f(void) // Don't touch the NiGHTS Objectplace stuff. // ... or if the mo mysteriously vanished. - if (!players[0].mo || (players[0].pflags & PF_NIGHTSMODE)) + if (!players[0].mo || (players[0].powers[pw_carry] == CR_NIGHTSMODE)) return; // If still in dummy state, get out of it. diff --git a/src/m_cond.c b/src/m_cond.c index 5e23d4080..7f977c15d 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -929,7 +929,7 @@ UINT8 M_CheckLevelEmblems(void) // Update Score, Time, Rings emblems for (i = 0; i < numemblems; ++i) { - if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].collected) + if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].type == ET_MAP || emblemlocations[i].collected) continue; levelnum = emblemlocations[i].level; @@ -963,6 +963,42 @@ UINT8 M_CheckLevelEmblems(void) return somethingUnlocked; } +UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough. +{ + INT32 i; + INT32 embtype; + INT16 levelnum; + UINT8 res; + UINT8 somethingUnlocked = 0; + UINT8 flags; + + for (i = 0; i < numemblems; ++i) + { + if (emblemlocations[i].type != ET_MAP || emblemlocations[i].collected) + continue; + + levelnum = emblemlocations[i].level; + embtype = emblemlocations[i].var; + flags = MV_BEATEN; + + if (embtype & ME_ALLEMERALDS) + flags |= MV_ALLEMERALDS; + + if (embtype & ME_ULTIMATE) + flags |= MV_ULTIMATE; + + if (embtype & ME_PERFECT) + flags |= MV_PERFECT; + + res = ((mapvisited[levelnum - 1] & flags) == flags); + + emblemlocations[i].collected = res; + if (res) + ++somethingUnlocked; + } + return somethingUnlocked; +} + // ------------------- // Quick unlock checks // ------------------- diff --git a/src/m_cond.h b/src/m_cond.h index e61ff1f79..94802f665 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -73,6 +73,12 @@ typedef struct #define ET_RINGS 4 #define ET_NGRADE 5 #define ET_NTIME 6 +#define ET_MAP 7 + +// Map emblem flags +#define ME_ALLEMERALDS 1 +#define ME_ULTIMATE 2 +#define ME_PERFECT 4 typedef struct { @@ -153,6 +159,7 @@ void M_CheckUnlockConditions(void); UINT8 M_UpdateUnlockablesAndExtraEmblems(void); void M_SilentUpdateUnlockablesAndEmblems(void); UINT8 M_CheckLevelEmblems(void); +UINT8 M_CompletionEmblems(void); // Checking unlockable status UINT8 M_AnySecretUnlocked(void); diff --git a/src/m_menu.c b/src/m_menu.c index f682cd1b5..fb8aeedad 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -53,6 +53,7 @@ #include "byteptr.h" #include "st_stuff.h" #include "i_sound.h" +#include "fastcmp.h" // Condition Sets #include "m_cond.h" @@ -108,41 +109,45 @@ typedef enum const char *quitmsg[NUM_QUITMESSAGES]; // Stuff for customizing the player select screen Tails 09-22-2003 +// A rare case. +// External files modify this menu, so we can't call it static. +// And I'm too lazy to go through and rename it everywhere. ARRGH! description_t description[32] = { - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0}, - {"???", "", "", 0, 0, 0} + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0}, + {false, "???", "", "", 0, 0} }; +static INT16 char_on = 0; static char *char_notes = NULL; static fixed_t char_scroll = 0; @@ -170,7 +175,6 @@ static saveinfo_t savegameinfo[MAXSAVEGAMES]; // Extra info about the save games INT16 startmap; // Mario, NiGHTS, or just a plain old normal game? static INT16 itemOn = 1; // menu item skull is on, Hack by Tails 09-18-2002 -static boolean lastdirection = true; // toaster - Only You Can Prevent Hacks - true is for forward, false is for backwards static INT16 skullAnimCounter = 10; // skull animation counter static boolean setupcontrols_secondaryplayer; @@ -187,6 +191,7 @@ static INT32 vidm_column_size; // PROTOTYPES // +static void M_GoBack(INT32 choice); static void M_StopMessage(INT32 choice); #ifndef NONET @@ -204,6 +209,27 @@ menu_t MessageDef; menu_t SPauseDef; +// Level Select +static levelselect_t levelselect = {0, NULL}; +static UINT8 levelselectselect[4]; +static patch_t *levselp[2][3]; +static INT32 lsoffs[2]; + +#define lsrow levelselectselect[0] +#define lscol levelselectselect[1] +#define lstic levelselectselect[2] +#define lshli levelselectselect[3] + +#define lshseperation 101 +#define lsbasevseperation 62 +#define lsheadingheight 16 +#define getheadingoffset(row) (levelselect.rows[row].header[0] ? lsheadingheight : 0) +#define lsvseperation(row) lsbasevseperation + getheadingoffset(row) +#define lswide(row) levelselect.rows[row].mapavailable[3] + +#define lsbasex 19 +#define lsbasey 59+lsheadingheight + // Sky Room static void M_CustomLevelSelect(INT32 choice); static void M_CustomWarp(INT32 choice); @@ -223,6 +249,7 @@ static void M_Options(INT32 choice); static void M_SelectableClearMenus(INT32 choice); static void M_Retry(INT32 choice); static void M_EndGame(INT32 choice); +static void M_GameTypeChange(INT32 choice); static void M_MapChange(INT32 choice); static void M_ChangeLevel(INT32 choice); static void M_ConfirmSpectate(INT32 choice); @@ -237,7 +264,9 @@ menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef; // Single Player static void M_LoadGame(INT32 choice); +static void M_TimeAttackLevelSelect(INT32 choice); static void M_TimeAttack(INT32 choice); +static void M_NightsAttackLevelSelect(INT32 choice); static void M_NightsAttack(INT32 choice); static void M_Statistics(INT32 choice); static void M_ReplayTimeAttack(INT32 choice); @@ -246,6 +275,7 @@ static void M_ChooseNightsAttack(INT32 choice); static void M_ModeAttackRetry(INT32 choice); static void M_ModeAttackEndGame(INT32 choice); static void M_SetGuestReplay(INT32 choice); +static void M_HandleChoosePlayerMenu(INT32 choice); static void M_ChoosePlayer(INT32 choice); menu_t SP_GameStatsDef, SP_LevelStatsDef; static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef; @@ -259,6 +289,7 @@ static void M_ConnectIPMenu(INT32 choice); #endif static void M_StartSplitServerMenu(INT32 choice); static void M_StartServer(INT32 choice); +static void M_ServerOptions(INT32 choice); #ifndef NONET static void M_Refresh(INT32 choice); static void M_Connect(INT32 choice); @@ -307,8 +338,9 @@ static void M_DrawSkyRoom(void); static void M_DrawChecklist(void); static void M_DrawEmblemHints(void); static void M_DrawPauseMenu(void); +static void M_DrawGameTypeMenu(void); static void M_DrawServerMenu(void); -static void M_DrawLevelSelectMenu(void); +static void M_DrawLevelPlatterMenu(void); static void M_DrawImageDef(void); static void M_DrawLoad(void); static void M_DrawLevelStats(void); @@ -337,6 +369,7 @@ static boolean M_CancelConnect(void); #endif static boolean M_ExitPandorasBox(void); static boolean M_QuitMultiPlayerMenu(void); +static void M_HandleLevelPlatter(INT32 choice); static void M_HandleSoundTest(INT32 choice); static void M_HandleImageDef(INT32 choice); static void M_HandleLoadSave(INT32 choice); @@ -352,7 +385,6 @@ static void M_HandleFogColor(INT32 choice); static void M_HandleVideoMode(INT32 choice); // Consvar onchange functions -static void Nextmap_OnChange(void); static void Newgametype_OnChange(void); static void Dummymares_OnChange(void); @@ -370,7 +402,7 @@ static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; consvar_t cv_chooseskin = {"chooseskin", DEFAULTSKIN, CV_HIDEN|CV_CALL, skins_cons_t, Nextmap_OnChange, 0, NULL, NULL, 0, 0, NULL}; // This gametype list is integral for many different reasons. -// When you add gametypes here, don't forget to update them in CV_AddValue! +// When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h! CV_PossibleValue_t gametype_cons_t[] = { {GT_COOP, "Co-op"}, @@ -382,7 +414,7 @@ CV_PossibleValue_t gametype_cons_t[] = {GT_TEAMMATCH, "Team Match"}, {GT_TAG, "Tag"}, - {GT_HIDEANDSEEK, "Hide and Seek"}, + {GT_HIDEANDSEEK, "Hide & Seek"}, {GT_CTF, "CTF"}, {0, NULL} @@ -482,21 +514,21 @@ typedef enum // --------------------- static menuitem_t MPauseMenu[] = { - {IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16}, - {IT_STRING | IT_CALL, NULL, "Switch Map..." , M_MapChange, 24}, + {IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16}, + {IT_STRING | IT_CALL, NULL, "Switch Gametype/Level...", M_GameTypeChange, 24}, - {IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus,40}, - {IT_CALL | IT_STRING, NULL, "Player 1 Setup", M_SetupMultiPlayer, 48}, // splitscreen - {IT_CALL | IT_STRING, NULL, "Player 2 Setup", M_SetupMultiPlayer2, 56}, // splitscreen + {IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus,40}, + {IT_CALL | IT_STRING, NULL, "Player 1 Setup", M_SetupMultiPlayer, 48}, // splitscreen + {IT_CALL | IT_STRING, NULL, "Player 2 Setup", M_SetupMultiPlayer2, 56}, // splitscreen - {IT_STRING | IT_CALL, NULL, "Spectate", M_ConfirmSpectate, 48}, - {IT_STRING | IT_CALL, NULL, "Enter Game", M_ConfirmEnterGame, 48}, - {IT_STRING | IT_SUBMENU, NULL, "Switch Team...", &MISC_ChangeTeamDef, 48}, - {IT_CALL | IT_STRING, NULL, "Player Setup", M_SetupMultiPlayer, 56}, // alone - {IT_CALL | IT_STRING, NULL, "Options", M_Options, 64}, + {IT_STRING | IT_CALL, NULL, "Spectate", M_ConfirmSpectate, 48}, + {IT_STRING | IT_CALL, NULL, "Enter Game", M_ConfirmEnterGame, 48}, + {IT_STRING | IT_SUBMENU, NULL, "Switch Team...", &MISC_ChangeTeamDef, 48}, + {IT_CALL | IT_STRING, NULL, "Player Setup", M_SetupMultiPlayer, 56}, // alone + {IT_CALL | IT_STRING, NULL, "Options", M_Options, 64}, - {IT_CALL | IT_STRING, NULL, "Return to Title", M_EndGame, 80}, - {IT_CALL | IT_STRING, NULL, "Quit Game", M_QuitSRB2, 88}, + {IT_CALL | IT_STRING, NULL, "Return to Title", M_EndGame, 80}, + {IT_CALL | IT_STRING, NULL, "Quit Game", M_QuitSRB2, 88}, }; typedef enum @@ -564,11 +596,37 @@ static menuitem_t MISC_ChangeTeamMenu[] = {IT_WHITESTRING|IT_CALL, NULL, "Confirm", M_ConfirmTeamChange, 90}, }; +static menuitem_t MISC_ChangeGameTypeMenu[] = +{ + {IT_STRING|IT_CALL, NULL, "Co-op", M_MapChange, 0}, + + {IT_STRING|IT_CALL, NULL, "Competition", M_MapChange, 12}, + {IT_STRING|IT_CALL, NULL, "Race", M_MapChange, 20}, + + {IT_STRING|IT_CALL, NULL, "Match", M_MapChange, 32}, + {IT_STRING|IT_CALL, NULL, "Team Match", M_MapChange, 40}, + + {IT_STRING|IT_CALL, NULL, "Tag", M_MapChange, 52}, + {IT_STRING|IT_CALL, NULL, "Hide & Seek", M_MapChange, 60}, + + {IT_STRING|IT_CALL, NULL, "Capture the Flag", M_MapChange, 72}, +}; + +static const gtdesc_t gametypedesc[] = +{ + {"Play through the single-player campaign with your friends, teaming up to beat Dr Eggman's nefarious challenges!"}, + {"Speed your way through the main acts, competing in several different categories to see who's the best."}, + {"There's not much to it - zoom through the level faster than everyone else."}, + {"Sling rings at your foes in a free-for-all battle. Use the special weapon rings to your advantage!"}, + {"Sling rings at your foes in a color-coded battle. Use the special weapon rings to your advantage!"}, + {"Whoever's IT has to hunt down everyone else. If you get caught, you have to turn on your former friends!"}, + {"Try and find a good hiding place in these maps - we dare you."}, + {"Steal the flag from the enemy's base and bring it back to your own, but watch out - they could just as easily steal yours!"}, +}; + static menuitem_t MISC_ChangeLevelMenu[] = { - {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 30}, - {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 60}, - {IT_WHITESTRING|IT_CALL, NULL, "Change Level", M_ChangeLevel, 120}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0}, // dummy menuitem for the control func }; static menuitem_t MISC_HelpMenu[] = @@ -642,9 +700,7 @@ static menuitem_t SR_MainMenu[] = static menuitem_t SR_LevelSelectMenu[] = { - {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 60}, - - {IT_WHITESTRING|IT_CALL, NULL, "Start", M_LevelSelectWarp, 120}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0}, // dummy menuitem for the control func }; static menuitem_t SR_UnlockChecklistMenu[] = @@ -666,10 +722,10 @@ static menuitem_t SR_EmblemHintMenu[] = // Single Player Main static menuitem_t SP_MainMenu[] = { - {IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 92}, - {IT_SECRET, NULL, "Record Attack", M_TimeAttack, 100}, - {IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 108}, - {IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116}, + {IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 92}, + {IT_SECRET, NULL, "Record Attack", M_TimeAttack, 100}, + {IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 108}, + {IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116}, }; enum @@ -683,26 +739,30 @@ enum // Single Player Load Game static menuitem_t SP_LoadGameMenu[] = { - {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLoadSave, '\0'}, // dummy menuitem for the control func + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLoadSave, 0}, // dummy menuitem for the control func }; // Single Player Level Select static menuitem_t SP_LevelSelectMenu[] = { - {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 60}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0}, // dummy menuitem for the control func +}; - {IT_WHITESTRING|IT_CALL, NULL, "Start", M_LevelSelectWarp, 120}, +// Single Player Time Attack Level Select +static menuitem_t SP_TimeAttackLevelSelectMenu[] = +{ + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0}, // dummy menuitem for the control func }; // Single Player Time Attack static menuitem_t SP_TimeAttackMenu[] = { - {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 52}, - {IT_STRING|IT_CVAR, NULL, "Player", &cv_chooseskin, 62}, + {IT_STRING|IT_CALL, NULL, "Level Select...", &M_TimeAttackLevelSelect, 52}, + {IT_STRING|IT_CVAR, NULL, "Character", &cv_chooseskin, 62}, {IT_DISABLED, NULL, "Guest Option...", &SP_GuestReplayDef, 100}, - {IT_DISABLED, NULL, "Replay...", &SP_ReplayDef, 110}, - {IT_DISABLED, NULL, "Ghosts...", &SP_GhostDef, 120}, + {IT_DISABLED, NULL, "Replay...", &SP_ReplayDef, 110}, + {IT_DISABLED, NULL, "Ghosts...", &SP_GhostDef, 120}, {IT_WHITESTRING|IT_CALL|IT_CALL_NOTMODIFIED, NULL, "Start", M_ChooseTimeAttack, 130}, }; @@ -786,16 +846,22 @@ static menuitem_t SP_NightsGhostMenu[] = {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SP_NightsAttackDef, 50} }; +// Single Player Nights Attack Level Select +static menuitem_t SP_NightsAttackLevelSelectMenu[] = +{ + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0}, // dummy menuitem for the control func +}; + // Single Player Nights Attack static menuitem_t SP_NightsAttackMenu[] = { - {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 44}, - {IT_STRING|IT_CVAR, NULL, "Show Records For", &cv_dummymares, 54}, + {IT_STRING|IT_CALL, NULL, "Level Select...", &M_NightsAttackLevelSelect, 52}, + {IT_STRING|IT_CVAR, NULL, "Show Records For", &cv_dummymares, 62}, - {IT_DISABLED, NULL, "Guest Option...", &SP_NightsGuestReplayDef, 108}, - {IT_DISABLED, NULL, "Replay...", &SP_NightsReplayDef, 118}, - {IT_DISABLED, NULL, "Ghosts...", &SP_NightsGhostDef, 128}, - {IT_WHITESTRING|IT_CALL|IT_CALL_NOTMODIFIED, NULL, "Start", M_ChooseNightsAttack, 138}, + {IT_DISABLED, NULL, "Guest Option...", &SP_NightsGuestReplayDef, 100}, + {IT_DISABLED, NULL, "Replay...", &SP_NightsReplayDef, 110}, + {IT_DISABLED, NULL, "Ghosts...", &SP_NightsGhostDef, 120}, + {IT_WHITESTRING|IT_CALL|IT_CALL_NOTMODIFIED, NULL, "Start", M_ChooseNightsAttack, 130}, }; enum @@ -812,51 +878,18 @@ enum // Statistics static menuitem_t SP_GameStatsMenu[] = { - {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleGameStats, '\0'}, // dummy menuitem for the control func + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleGameStats, 0}, // dummy menuitem for the control func }; static menuitem_t SP_LevelStatsMenu[] = { - {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelStats, '\0'}, // dummy menuitem for the control func + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelStats, 0}, // dummy menuitem for the control func }; -// A rare case. -// External files modify this menu, so we can't call it static. -// And I'm too lazy to go through and rename it everywhere. ARRGH! -menuitem_t PlayerMenu[32] = +// Player menu dummy +static menuitem_t SP_PlayerMenu[] = { - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}, - {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0} + {IT_NOTHING | IT_KEYHANDLER, NULL, "", M_HandleChoosePlayerMenu, 0}, // dummy menuitem for the control func }; // ----------------------------------- @@ -878,28 +911,40 @@ static menuitem_t MP_MainMenu[] = static menuitem_t MP_ServerMenu[] = { - {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 10}, + {IT_DISABLED|IT_NOTHING, NULL, "", NULL, 0}, #ifndef NONET - {IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 20}, - {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 30}, + {IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 10}, + {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 20}, + {IT_STRING|IT_CVAR, NULL, "Max Players", &cv_maxplayers, 46}, + {IT_STRING|IT_CVAR, NULL, "Allow WAD Downloading", &cv_downloading, 56}, #endif - - {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 80}, - - {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 130}, + {IT_STRING|IT_CALL, NULL, "Select Gametype/Level...", M_GameTypeChange, 100}, + {IT_STRING|IT_CALL, NULL, "More Options...", M_ServerOptions, 130}, + {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 140}, }; enum { - mp_server_gametype = 0, + mp_server_dummy = 0, // exists solely so zero-indexed in both NONET and not NONET #ifndef NONET mp_server_room, mp_server_name, + mp_server_maxpl, + mp_server_waddl, #endif - mp_server_level, + mp_server_levelgt, + mp_server_options, mp_server_start }; +// Separated splitscreen and normal servers. +static menuitem_t MP_SplitServerMenu[] = +{ + {IT_STRING|IT_CALL, NULL, "Select Gametype/Level...", M_GameTypeChange, 100}, + {IT_STRING|IT_CALL, NULL, "More Options...", M_ServerOptions, 130}, + {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 140}, +}; + #ifndef NONET static menuitem_t MP_ConnectMenu[] = { @@ -958,14 +1003,6 @@ static menuitem_t MP_ConnectIPMenu[] = }; #endif -// Separated splitscreen and normal servers. -static menuitem_t MP_SplitServerMenu[] = -{ - {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 10}, - {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 80}, - {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 130}, -}; - static menuitem_t MP_PlayerSetupMenu[] = { {IT_KEYHANDLER | IT_STRING, NULL, "Your name", M_HandleSetupMultiPlayer, 0}, @@ -979,14 +1016,14 @@ static menuitem_t MP_PlayerSetupMenu[] = // Prefix: OP_ static menuitem_t OP_MainMenu[] = { - {IT_SUBMENU | IT_STRING, NULL, "Setup Controls...", &OP_ControlsDef, 10}, + {IT_SUBMENU | IT_STRING, NULL, "Setup Controls...", &OP_ControlsDef, 10}, - {IT_SUBMENU | IT_STRING, NULL, "Video Options...", &OP_VideoOptionsDef, 30}, - {IT_SUBMENU | IT_STRING, NULL, "Sound Options...", &OP_SoundOptionsDef, 40}, - {IT_SUBMENU | IT_STRING, NULL, "Data Options...", &OP_DataOptionsDef, 50}, + {IT_SUBMENU | IT_STRING, NULL, "Video Options...", &OP_VideoOptionsDef, 30}, + {IT_SUBMENU | IT_STRING, NULL, "Sound Options...", &OP_SoundOptionsDef, 40}, + {IT_SUBMENU | IT_STRING, NULL, "Data Options...", &OP_DataOptionsDef, 50}, - {IT_SUBMENU | IT_STRING, NULL, "Game Options...", &OP_GameOptionsDef, 70}, - {IT_SUBMENU | IT_STRING, NULL, "Server Options...", &OP_ServerOptionsDef, 80}, + {IT_SUBMENU | IT_STRING, NULL, "Game Options...", &OP_GameOptionsDef, 70}, + {IT_CALL | IT_STRING, NULL, "Server Options...", M_ServerOptions, 80}, }; static menuitem_t OP_ControlsMenu[] = @@ -1169,7 +1206,7 @@ static menuitem_t OP_VideoOptionsMenu[] = static menuitem_t OP_VideoModeMenu[] = { - {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleVideoMode, '\0'}, // dummy menuitem for the control func + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleVideoMode, 0}, // dummy menuitem for the control func }; #ifdef HWRENDER @@ -1402,7 +1439,31 @@ menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72); // Misc Main Menu menu_t MISC_ScrambleTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ScrambleTeamMenu, &MPauseDef, 27, 40); menu_t MISC_ChangeTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ChangeTeamMenu, &MPauseDef, 27, 40); -menu_t MISC_ChangeLevelDef = MAPICONMENUSTYLE(NULL, MISC_ChangeLevelMenu, &MPauseDef); + +// MP Gametype and map change menu +menu_t MISC_ChangeGameTypeDef = +{ + NULL, + sizeof (MISC_ChangeGameTypeMenu)/sizeof (menuitem_t), + &MainDef, // Doesn't matter. + MISC_ChangeGameTypeMenu, + M_DrawGameTypeMenu, + 30, (200 - (72+8))/2, // vertically centering + 0, + NULL +}; +menu_t MISC_ChangeLevelDef = +{ + NULL, + sizeof (MISC_ChangeLevelMenu)/sizeof (menuitem_t), + &MISC_ChangeGameTypeDef, + MISC_ChangeLevelMenu, + M_DrawLevelPlatterMenu, + 0, 0, + 0, + NULL +}; + menu_t MISC_HelpDef = IMAGEDEF(MISC_HelpMenu); // Sky Room @@ -1428,17 +1489,9 @@ menu_t SR_MainDef = 0, NULL }; -menu_t SR_LevelSelectDef = -{ - 0, - sizeof (SR_LevelSelectMenu)/sizeof (menuitem_t), - &SR_MainDef, - SR_LevelSelectMenu, - M_DrawLevelSelectMenu, - 40, 40, - 0, - NULL -}; + +menu_t SR_LevelSelectDef = MAPPLATTERMENUSTYLE(NULL, SR_LevelSelectMenu); + menu_t SR_UnlockChecklistDef = { NULL, @@ -1475,7 +1528,8 @@ menu_t SP_LoadDef = 0, NULL }; -menu_t SP_LevelSelectDef = MAPICONMENUSTYLE(NULL, SP_LevelSelectMenu, &SP_LoadDef); + +menu_t SP_LevelSelectDef = MAPPLATTERMENUSTYLE(NULL, SP_LevelSelectMenu); menu_t SP_GameStatsDef = { @@ -1500,6 +1554,8 @@ menu_t SP_LevelStatsDef = NULL }; +menu_t SP_TimeAttackLevelSelectDef = MAPPLATTERMENUSTYLE("M_ATTACK", SP_TimeAttackLevelSelectMenu); + static menu_t SP_TimeAttackDef = { "M_ATTACK", @@ -1545,6 +1601,8 @@ static menu_t SP_GhostDef = NULL }; +menu_t SP_NightsAttackLevelSelectDef = MAPPLATTERMENUSTYLE("M_NIGHTS", SP_NightsAttackLevelSelectMenu); + static menu_t SP_NightsAttackDef = { "M_NIGHTS", @@ -1594,9 +1652,9 @@ static menu_t SP_NightsGhostDef = menu_t SP_PlayerDef = { "M_PICKP", - sizeof (PlayerMenu)/sizeof (menuitem_t),//player_end, + sizeof (SP_PlayerMenu)/sizeof (menuitem_t), &SP_MainDef, - PlayerMenu, + SP_PlayerMenu, M_DrawSetupChoosePlayerMenu, 24, 32, 0, @@ -1605,7 +1663,35 @@ menu_t SP_PlayerDef = // Multiplayer menu_t MP_MainDef = DEFAULTMENUSTYLE("M_MULTI", MP_MainMenu, &MainDef, 60, 40); -menu_t MP_ServerDef = MAPICONMENUSTYLE("M_MULTI", MP_ServerMenu, &MP_MainDef); + +menu_t MP_ServerDef = +{ + "M_MULTI", + sizeof (MP_ServerMenu)/sizeof (menuitem_t), + &MP_MainDef, + MP_ServerMenu, + M_DrawServerMenu, + 27, 30 +#ifdef NONET + - 50 +#endif + , + 0, + NULL +}; + +menu_t MP_SplitServerDef = +{ + "M_MULTI", + sizeof (MP_SplitServerMenu)/sizeof (menuitem_t), + &MP_MainDef, + MP_SplitServerMenu, + M_DrawServerMenu, + 27, 30 - 50, + 0, + NULL +}; + #ifndef NONET menu_t MP_ConnectDef = { @@ -1641,7 +1727,7 @@ menu_t MP_RoomDef = NULL }; #endif -menu_t MP_SplitServerDef = MAPICONMENUSTYLE("M_MULTI", MP_SplitServerMenu, &MP_MainDef); + menu_t MP_PlayerSetupDef = { "M_SPLAYR", @@ -1748,11 +1834,11 @@ menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_DataOp // (there's only a couple anyway) // Prototypes -static INT32 M_FindFirstMap(INT32 gtype); -static INT32 M_GetFirstLevelInList(void); +static INT32 M_GetFirstLevelInList(INT32 gt); +static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt); -// Nextmap. Used for Time Attack. -static void Nextmap_OnChange(void) +// Nextmap. Used for Level select. +void Nextmap_OnChange(void) { char *leveltitle; char tabase[256]; @@ -1896,12 +1982,7 @@ static void Newgametype_OnChange(void) if(!mapheaderinfo[cv_nextmap.value-1]) P_AllocMapHeader((INT16)(cv_nextmap.value-1)); - if ((cv_newgametype.value == GT_COOP && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_COOP)) || - (cv_newgametype.value == GT_COMPETITION && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_COMPETITION)) || - (cv_newgametype.value == GT_RACE && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_RACE)) || - ((cv_newgametype.value == GT_MATCH || cv_newgametype.value == GT_TEAMMATCH) && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_MATCH)) || - ((cv_newgametype.value == GT_TAG || cv_newgametype.value == GT_HIDEANDSEEK) && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_TAG)) || - (cv_newgametype.value == GT_CTF && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_CTF))) + if (!M_CanShowLevelOnPlatter(cv_nextmap.value-1, cv_newgametype.value)) { INT32 value = 0; @@ -1929,9 +2010,7 @@ static void Newgametype_OnChange(void) break; } - CV_SetValue(&cv_nextmap, M_FindFirstMap(value)); - CV_AddValue(&cv_nextmap, -1); - CV_AddValue(&cv_nextmap, 1); + CV_SetValue(&cv_nextmap, M_GetFirstLevelInList(value)); } } } @@ -1976,6 +2055,34 @@ menu_t *currentMenu = &MainDef; // BASIC MENU HANDLING // ========================================================================= +static void M_GoBack(INT32 choice) +{ + (void)choice; + + if (currentMenu->prevMenu) + { + //If we entered the game search menu, but didn't enter a game, + //make sure the game doesn't still think we're in a netgame. + if (!Playing() && netgame && multiplayer) + { + MSCloseUDPSocket(); // Clean up so we can re-open the connection later. + netgame = false; + multiplayer = false; + } + + if ((currentMenu->prevMenu == &MainDef) && (currentMenu == &SP_TimeAttackDef || currentMenu == &SP_NightsAttackDef)) + { + // D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate. + menuactive = false; + D_StartTitle(); + } + else + M_SetupNextMenu(currentMenu->prevMenu); + } + else + M_ClearMenus(true); +} + static void M_ChangeCvar(INT32 choice) { consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction; @@ -2034,8 +2141,6 @@ static boolean M_ChangeStringCvar(INT32 choice) static void M_NextOpt(void) { INT16 oldItemOn = itemOn; // prevent infinite loop - lastdirection = true; - do { if (itemOn + 1 > currentMenu->numitems - 1) @@ -2048,8 +2153,6 @@ static void M_NextOpt(void) static void M_PrevOpt(void) { INT16 oldItemOn = itemOn; // prevent infinite loop - lastdirection = false; - do { if (!itemOn) @@ -2320,7 +2423,7 @@ boolean M_Responder(event_t *ev) case KEY_DOWNARROW: M_NextOpt(); S_StartSound(NULL, sfx_menu1); - if (currentMenu == &SP_PlayerDef) + if (currentMenu == &MISC_ChangeGameTypeDef) { Z_Free(char_notes); char_notes = NULL; @@ -2330,7 +2433,7 @@ boolean M_Responder(event_t *ev) case KEY_UPARROW: M_PrevOpt(); S_StartSound(NULL, sfx_menu1); - if (currentMenu == &SP_PlayerDef) + if (currentMenu == &MISC_ChangeGameTypeDef) { Z_Free(char_notes); char_notes = NULL; @@ -2394,28 +2497,8 @@ boolean M_Responder(event_t *ev) case KEY_ESCAPE: noFurtherInput = true; currentMenu->lastOn = itemOn; - if (currentMenu->prevMenu) - { - //If we entered the game search menu, but didn't enter a game, - //make sure the game doesn't still think we're in a netgame. - if (!Playing() && netgame && multiplayer) - { - MSCloseUDPSocket(); // Clean up so we can re-open the connection later. - netgame = false; - multiplayer = false; - } - if (currentMenu == &SP_TimeAttackDef || currentMenu == &SP_NightsAttackDef) - { - // D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate. - menuactive = false; - D_StartTitle(); - } - else - M_SetupNextMenu(currentMenu->prevMenu); - } - else - M_ClearMenus(true); + M_GoBack(0); return true; @@ -2940,6 +3023,8 @@ static void M_DrawMapEmblems(INT32 mapnum, INT32 x, INT32 y) curtype = 1; break; case ET_NGRADE: case ET_NTIME: curtype = 2; break; + case ET_MAP: + curtype = 3; break; default: curtype = 0; break; } @@ -3417,7 +3502,7 @@ static void M_PatchSkinNameTable(void) for (j = 0; j < MAXSKINS; j++) { - if (skins[j].name[0] != '\0' && R_SkinUnlock(j)) + if (skins[j].name[0] != '\0' && R_SkinUsable(-1, j)) { skins_cons_t[j].strvalue = skins[j].realname; skins_cons_t[j].value = j+1; @@ -3429,31 +3514,50 @@ static void M_PatchSkinNameTable(void) } } - CV_SetValue(&cv_chooseskin, cv_chooseskin.value); // This causes crash sometimes?! - CV_SetValue(&cv_chooseskin, 1); - CV_AddValue(&cv_chooseskin, -1); - CV_AddValue(&cv_chooseskin, 1); + Nextmap_OnChange(); return; } -// Call before showing any level-select menus -static void M_PrepareLevelSelect(void) +// +// M_LevelAvailableOnPlatter +// +// Okay, you know that the level SHOULD show up on the platter already. +// The only question is whether it should be as a question mark, +// (hinting as to its existence), or as its pure, unfettered self. +// +static boolean M_LevelAvailableOnPlatter(INT32 mapnum) { - if (levellistmode != LLM_CREATESERVER) - CV_SetValue(&cv_nextmap, M_GetFirstLevelInList()); - else - Newgametype_OnChange(); // Make sure to start on an appropriate map if wads have been added + if (M_MapLocked(mapnum+1)) + return false; // not unlocked + + switch (levellistmode) + { + case LLM_RECORDATTACK: + case LLM_NIGHTSATTACK: + if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) + return true; + + if (!mapvisited[mapnum]) + return false; + + // intentional fallthrough + case LLM_CREATESERVER: + case LLM_LEVELSELECT: + default: + return true; + } + return true; } // -// M_CanShowLevelInList +// M_CanShowLevelOnPlatter // // Determines whether to show a given map in the various level-select lists. // Set gt = -1 to ignore gametype. // -boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) +static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt) { // Does the map exist? if (!mapheaderinfo[mapnum]) @@ -3463,6 +3567,9 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) if (!mapheaderinfo[mapnum]->lvlttl[0]) return false; + /*if (M_MapLocked(mapnum+1)) + return false; // not unlocked*/ + switch (levellistmode) { case LLM_CREATESERVER: @@ -3470,9 +3577,6 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU) return false; - if (M_MapLocked(mapnum+1)) - return false; // not unlocked - if (gt == GT_COOP && (mapheaderinfo[mapnum]->typeoflevel & TOL_COOP)) return true; @@ -3497,37 +3601,16 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) if (mapheaderinfo[mapnum]->levelselect != maplistoption) return false; - if (M_MapLocked(mapnum+1)) - return false; // not unlocked - return true; case LLM_RECORDATTACK: if (!(mapheaderinfo[mapnum]->menuflags & LF2_RECORDATTACK)) return false; - if (M_MapLocked(mapnum+1)) - return false; // not unlocked - - if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) - return true; - - if (!mapvisited[mapnum]) - return false; - return true; case LLM_NIGHTSATTACK: if (!(mapheaderinfo[mapnum]->menuflags & LF2_NIGHTSATTACK)) return false; - if (M_MapLocked(mapnum+1)) - return false; // not unlocked - - if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) - return true; - - if (!mapvisited[mapnum]) - return false; - return true; } @@ -3535,23 +3618,538 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) return false; } -static INT32 M_CountLevelsToShowInList(void) +#if 0 +static INT32 M_CountLevelsToShowOnPlatter(INT32 gt) { INT32 mapnum, count = 0; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) - if (M_CanShowLevelInList(mapnum, -1)) + if (M_CanShowLevelOnPlatter(mapnum, gt)) count++; return count; } +#endif -static INT32 M_GetFirstLevelInList(void) +#if 0 +static boolean M_SetNextMapOnPlatter(void) +{ + INT32 row, col = 0; + while (col < 3) + { + row = 0; + while (row < levelselect.numrows) + { + if (levelselect.rows[row].maplist[col] == cv_nextmap.value) + { + lsrow = row; + lscol = col; + return true; + } + row++; + } + col++; + } + return true; +} +#endif + +static INT32 M_CountRowsToShowOnPlatter(INT32 gt) +{ + INT32 mapnum = 0, prevmapnum = 0, col = 0, rows = 0; + + while (mapnum < NUMMAPS) + { + if (M_CanShowLevelOnPlatter(mapnum, gt)) + { + if (rows == 0) + rows++; + else + { + if (col == 2 + || (mapheaderinfo[prevmapnum]->menuflags & LF2_WIDEICON) + || (mapheaderinfo[mapnum]->menuflags & LF2_WIDEICON) + || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[prevmapnum]->selectheading))) + { + col = 0; + rows++; + } + else + col++; + } + prevmapnum = mapnum; + } + mapnum++; + } + + return rows; +} + +// +// M_PrepareLevelPlatter +// +// Prepares a tasty dish of zones and acts! +// Call before any attempt to access a level platter. +// +static boolean M_PrepareLevelPlatter(INT32 gt) +{ + INT32 numrows = M_CountRowsToShowOnPlatter(gt); + INT32 mapnum = 0, prevmapnum = 0, col = 0, row = 0; + + if (!numrows) + return false; + + if (levelselect.rows) + Z_Free(levelselect.rows); + levelselect.rows = NULL; + + levelselect.numrows = numrows; + levelselect.rows = Z_Realloc(levelselect.rows, numrows*sizeof(levelselectrow_t), PU_STATIC, NULL); + if (!levelselect.rows) + I_Error("Insufficient memory to prepare level platter"); + + // done here so lsrow and lscol can be set if cv_nextmap is on the platter + lsrow = lscol = lstic = lshli = lsoffs[0] = lsoffs[1] = 0; + + while (mapnum < NUMMAPS) + { + if (M_CanShowLevelOnPlatter(mapnum, gt)) + { + const INT32 actnum = mapheaderinfo[mapnum]->actnum; + const boolean headingisname = (fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[mapnum]->lvlttl)); + const boolean wide = (mapheaderinfo[mapnum]->menuflags & LF2_WIDEICON); + + // preparing next position to drop mapnum into + if (levelselect.rows[0].maplist[0]) + { + if (col == 2 // no more space on the row? + || wide + || (mapheaderinfo[prevmapnum]->menuflags & LF2_WIDEICON) + || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[prevmapnum]->selectheading))) // a new heading is starting? + { + col = 0; + row++; + } + else + col++; + } + + levelselect.rows[row].maplist[col] = mapnum+1; // putting the map on the platter + levelselect.rows[row].mapavailable[col] = M_LevelAvailableOnPlatter(mapnum); + + if ((lswide(row) = wide)) // intentionally assignment + { + levelselect.rows[row].maplist[2] = levelselect.rows[row].maplist[1] = levelselect.rows[row].maplist[0]; + levelselect.rows[row].mapavailable[2] = levelselect.rows[row].mapavailable[1] = levelselect.rows[row].mapavailable[0]; + } + + if (cv_nextmap.value == mapnum+1) // A little quality of life improvement. + { + lsrow = row; + lscol = col; + } + + // individual map name + if (levelselect.rows[row].mapavailable[col]) + { + if (headingisname) + { + if (actnum) + sprintf(levelselect.rows[row].mapnames[col], "ACT %d", actnum); + else + sprintf(levelselect.rows[row].mapnames[col], "THE ACT"); + } + else if (wide) + { + // Yes, with LF2_WIDEICON it'll continue on over into the next 17+1 char block. That's alright; col is always zero, the string is contiguous, and the maximum length is lvlttl[22] + ' ' + ZONE + ' ' + INT32, which is about 39 or so - barely crossing into the third column. + char* mapname = G_BuildMapTitle(mapnum+1); + strcpy(levelselect.rows[row].mapnames[col], (const char *)mapname); + Z_Free(mapname); + } + else + { + char mapname[22+1+11]; // lvlttl[22] + ' ' + INT32 + + if (actnum) + sprintf(mapname, "%s %d", mapheaderinfo[mapnum]->lvlttl, actnum); + else + sprintf(mapname, "%s", mapheaderinfo[mapnum]->lvlttl); + + if (strlen(mapname) >= 17) + sprintf(mapname+17-3, "..."); + + strcpy(levelselect.rows[row].mapnames[col], (const char *)mapname); + } + } + else + sprintf(levelselect.rows[row].mapnames[col], "???"); + + // creating header text + if (!col && (!row || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[levelselect.rows[row-1].maplist[0]-1]->selectheading)))) + { + if (!levelselect.rows[row].mapavailable[col]) + sprintf(levelselect.rows[row].header, "???"); + else + { + sprintf(levelselect.rows[row].header, "%s", mapheaderinfo[mapnum]->selectheading); + if (!(mapheaderinfo[mapnum]->levelflags & LF_NOZONE) && headingisname) + { + sprintf(levelselect.rows[row].header + strlen(levelselect.rows[row].header), " ZONE"); + } + } + } + + prevmapnum = mapnum; + } + + mapnum++; + } + + if (levselp[0][0]) // never going to have some provided but not all, saves individually checking + { + W_UnlockCachedPatch(levselp[0][0]); + W_UnlockCachedPatch(levselp[0][1]); + W_UnlockCachedPatch(levselp[0][2]); + + W_UnlockCachedPatch(levselp[1][0]); + W_UnlockCachedPatch(levselp[1][1]); + W_UnlockCachedPatch(levselp[1][2]); + } + + levselp[0][0] = W_CachePatchName("SLCT1LVL", PU_STATIC); + levselp[0][1] = W_CachePatchName("SLCT2LVL", PU_STATIC); + levselp[0][2] = W_CachePatchName("BLANKLVL", PU_STATIC); + + levselp[1][0] = W_CachePatchName("SLCT1LVW", PU_STATIC); + levselp[1][1] = W_CachePatchName("SLCT2LVW", PU_STATIC); + levselp[1][2] = W_CachePatchName("BLANKLVW", PU_STATIC); + + return true; +} + +#define selectvalnextmapnobrace(column) selectval = levelselect.rows[lsrow].maplist[column];\ + if (selectval && levelselect.rows[lsrow].mapavailable[column])\ + {\ + CV_SetValue(&cv_nextmap, selectval); + +#define selectvalnextmap(column) selectvalnextmapnobrace(column)} + +// +// M_HandleLevelPlatter +// +// Reacts to your key inputs. Basically a mini menu thinker. +// +static void M_HandleLevelPlatter(INT32 choice) +{ + boolean exitmenu = false; // exit to previous menu + INT32 selectval; + + switch (choice) + { + case KEY_DOWNARROW: + lsrow++; + if (lsrow == levelselect.numrows) + lsrow = 0; + + lsoffs[0] = lsvseperation(lsrow); + + if (levelselect.rows[lsrow].header[0]) + lshli = lsrow; + // no else needed - headerless lines associate upwards, so moving down to a row without a header is identity + + S_StartSound(NULL,sfx_s3kb7); + + selectvalnextmap(lscol) else selectvalnextmap(0) + break; + + case KEY_UPARROW: + lsoffs[0] = -lsvseperation(lsrow); + + lsrow--; + if (lsrow == UINT8_MAX) + lsrow = levelselect.numrows-1; + + if (levelselect.rows[lsrow].header[0]) + lshli = lsrow; + else + { + UINT8 iter = lsrow; + do + iter = ((iter == 0) ? levelselect.numrows-1 : iter-1); + while ((iter != lsrow) && !(levelselect.rows[iter].header[0])); + lshli = iter; + } + + S_StartSound(NULL,sfx_s3kb7); + + selectvalnextmap(lscol) else selectvalnextmap(0) + break; + + case KEY_LEFTARROW: + if (lscol > 0) + { + lscol--; + + lsoffs[1] = (lswide(lsrow) ? -8 : lshseperation); + S_StartSound(NULL,sfx_s3kb7); + + selectvalnextmap(lscol) else selectvalnextmap(0) + } + else if (!lsoffs[1]) // prevent sound spam + { + lsoffs[1] = -8; + S_StartSound(NULL,sfx_s3kb7); + } + break; + + case KEY_RIGHTARROW: + if (lscol < 2) + { + lscol++; + + lsoffs[1] = (lswide(lsrow) ? 8 : -lshseperation); + S_StartSound(NULL,sfx_s3kb7); + + selectvalnextmap(lscol) else selectvalnextmap(0) + } + else if (!lsoffs[1]) // prevent sound spam + { + lsoffs[1] = 8; + S_StartSound(NULL,sfx_s3kb7); + } + break; + + case KEY_ENTER: + selectvalnextmapnobrace(lscol) + + lsoffs[0] = lsoffs[1] = 0; + S_StartSound(NULL,sfx_menu1); + if (gamestate == GS_TIMEATTACK) + M_SetupNextMenu(currentMenu->prevMenu); + else if (currentMenu == &MISC_ChangeLevelDef) + { + if (currentMenu->prevMenu && currentMenu->prevMenu->prevMenu != &MPauseDef) + M_SetupNextMenu(currentMenu->prevMenu->prevMenu); + else + M_ChangeLevel(0); + } + else + M_LevelSelectWarp(0); + Nextmap_OnChange(); + } + else if (!lsoffs[0]) // prevent sound spam + { + lsoffs[0] = -8; + S_StartSound(NULL,sfx_s3kb2); + } + break; + + case KEY_ESCAPE: + exitmenu = true; + break; + + default: + break; + } + + if (exitmenu) + { + if (currentMenu->prevMenu) + { + M_SetupNextMenu(currentMenu->prevMenu); + Nextmap_OnChange(); + } + else + M_ClearMenus(true); + } +} + +static void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlight) +{ + y += lsheadingheight - 12; + V_DrawString(19, y, (headerhighlight ? V_YELLOWMAP : 0), header); + y += 9; + if ((y >= 0) && (y < 200)) + { + V_DrawFill(19, y, 281, 1, (headerhighlight ? yellowmap[3] : 3)); + V_DrawFill(300, y, 1, 1, 26); + } + y++; + if ((y >= 0) && (y < 200)) + { + V_DrawFill(19, y, 282, 1, 26); + } + y += 2; +} + +static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight) +{ + patch_t *patch; + + INT32 map = levelselect.rows[row].maplist[col]; + if (!map) + return; + + // A 564x100 image of the level as entry MAPxxW + if (!(levelselect.rows[row].mapavailable[col])) + V_DrawSmallScaledPatch(x, y, V_STATIC, levselp[1][2]); // static - make secret maps look ENTICING + else + { + if (W_CheckNumForName(va("%sW", G_BuildMapName(map))) != LUMPERROR) + patch = W_CachePatchName(va("%sW", G_BuildMapName(map)), PU_CACHE); + else + patch = levselp[1][2]; // don't static to indicate that it's just a normal level + + V_DrawSmallScaledPatch(x, y, 0, patch); + } + + if ((y+50) < 200) + { + INT32 topy = (y+50), h = 8; + + if (topy < 0) + { + h += topy; + topy = 0; + } + else if (topy + h >= 200) + h = 200 - y; + if (h > 0) + V_DrawFill(x, topy, 282, h, + ((mapheaderinfo[map-1]->unlockrequired < 0) + ? 159 : 63)); + } + + V_DrawString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); +} + +static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight) +{ + patch_t *patch; + + INT32 map = levelselect.rows[row].maplist[col]; + if (!map) + return; + + // A 160x100 image of the level as entry MAPxxP + if (!(levelselect.rows[row].mapavailable[col])) + V_DrawSmallScaledPatch(x, y, V_STATIC, levselp[0][2]); // static - make secret maps look ENTICING + else + { + if (W_CheckNumForName(va("%sP", G_BuildMapName(map))) != LUMPERROR) + patch = W_CachePatchName(va("%sP", G_BuildMapName(map)), PU_CACHE); + else + patch = levselp[0][2]; // don't static to indicate that it's just a normal level + + V_DrawSmallScaledPatch(x, y, 0, patch); + } + + if ((y+50) < 200) + { + INT32 topy = (y+50), h = 8; + + if (topy < 0) + { + h += topy; + topy = 0; + } + else if (topy + h >= 200) + h = 200 - y; + if (h > 0) + V_DrawFill(x, topy, 80, h, + ((mapheaderinfo[map-1]->unlockrequired < 0) + ? 159 : 63)); + } + + if (strlen(levelselect.rows[row].mapnames[col]) > 6) // "AERIAL GARDEN" vs "ACT 18" - "THE ACT" intentionally compressed + V_DrawThinString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); + else + V_DrawString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); +} + +static void M_DrawLevelPlatterRow(UINT8 row, INT32 y) +{ + UINT8 col; + const boolean rowhighlight = (row == lsrow); + if (levelselect.rows[row].header[0]) + { + M_DrawLevelPlatterHeader(y, levelselect.rows[row].header, (rowhighlight || (row == lshli))); + y += lsheadingheight; + } + + if (lswide(row)) + M_DrawLevelPlatterWideMap(row, 0, lsbasex, y, rowhighlight); + else + { + for (col = 0; col < 3; col++) + M_DrawLevelPlatterMap(row, col, lsbasex+(col*lshseperation), y, (rowhighlight && (col == lscol))); + } +} + +static void M_DrawLevelPlatterMenu(void) +{ + UINT8 iter = lsrow, sizeselect = (lswide(lsrow) ? 1 : 0); + INT32 y = lsbasey + lsoffs[0] - getheadingoffset(lsrow); + const INT32 cursorx = (sizeselect ? 0 : (lscol*lshseperation)); + + if (++lstic == 32) + lstic = 0; + + if (gamestate == GS_TIMEATTACK) + V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); + + // finds row at top of the screen + while (y > 0) + { + iter = ((iter == 0) ? levelselect.numrows-1 : iter-1); + y -= lsvseperation(iter); + } + + // draw from top to bottom + while (y < 200) + { + M_DrawLevelPlatterRow(iter, y); + y += lsvseperation(iter); + iter = ((iter == levelselect.numrows-1) ? 0 : iter+1); + } + + // draw cursor box + V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey, 0, ((lstic & 8) ? levselp[sizeselect][0] : levselp[sizeselect][1])); + + if (levelselect.rows[lsrow].maplist[lscol]) + V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE)); + + // handle movement of cursor box + if (abs(lsoffs[0]) > 1) + lsoffs[0] = 2*lsoffs[0]/3; + else + lsoffs[0] = 0; + + if (abs(lsoffs[1]) > 1) + lsoffs[1] = 2*lsoffs[1]/3; + else + lsoffs[1] = 0; + + M_DrawMenuTitle(); +} + +// +// M_CanShowLevelInList +// +// Determines whether to show a given map in level-select lists where you don't want to see locked levels. +// Set gt = -1 to ignore gametype. +// +boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) +{ + return (M_CanShowLevelOnPlatter(mapnum, gt) && M_LevelAvailableOnPlatter(mapnum)); +} + +static INT32 M_GetFirstLevelInList(INT32 gt) { INT32 mapnum; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) - if (M_CanShowLevelInList(mapnum, -1)) + if (M_CanShowLevelInList(mapnum, gt)) return mapnum + 1; return 1; @@ -3909,7 +4507,7 @@ static void M_Options(INT32 choice) (void)choice; // if the player is not admin or server, disable server options - OP_MainMenu[5].status = (Playing() && !(server || adminplayer == consoleplayer)) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); + OP_MainMenu[5].status = (Playing() && !(server || adminplayer == consoleplayer)) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // if the player is playing _at all_, disable the erase data options OP_DataOptionsMenu[1].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); @@ -4085,27 +4683,6 @@ static void M_DrawEmblemHints(void) M_DrawGenericMenu(); } -static void M_DrawLevelSelectMenu(void) -{ - M_DrawGenericMenu(); - - if (cv_nextmap.value) - { - lumpnum_t lumpnum; - patch_t *PictureOfLevel; - - // A 160x100 image of the level as entry MAPxxP - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); - - if (lumpnum != LUMPERROR) - PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE); - else - PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); - - V_DrawSmallScaledPatch(200, 110, 0, PictureOfLevel); - } -} - static void M_DrawSkyRoom(void) { INT32 i, y = 0; @@ -4288,13 +4865,13 @@ static void M_CustomLevelSelect(INT32 choice) SR_LevelSelectDef.prevMenu = currentMenu; levellistmode = LLM_LEVELSELECT; maplistoption = (UINT8)(unlockables[ul].variable); - if (M_CountLevelsToShowInList() == 0) + + if (!M_PrepareLevelPlatter(-1)) { M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING); return; } - M_PrepareLevelSelect(); M_SetupNextMenu(&SR_LevelSelectDef); } @@ -4316,17 +4893,17 @@ static void M_SinglePlayerMenu(INT32 choice) static void M_LoadGameLevelSelect(INT32 choice) { (void)choice; + + SP_LevelSelectDef.prevMenu = currentMenu; levellistmode = LLM_LEVELSELECT; maplistoption = 1; - if (M_CountLevelsToShowInList() == 0) + + if (!M_PrepareLevelPlatter(-1)) { M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING); return; } - SP_LevelSelectDef.prevMenu = currentMenu; - - M_PrepareLevelSelect(); M_SetupNextMenu(&SP_LevelSelectDef); } @@ -4768,19 +5345,19 @@ static void M_SetupChoosePlayer(INT32 choice) UINT8 i; UINT8 firstvalid = 255; UINT8 lastvalid = 0; + boolean allowed = false; char *name; (void)choice; - if (PlayerMenu[0].status & (IT_DYBIGSPACE)) // Correcting a hack that may be made below. - PlayerMenu[0].status = (IT_DISABLED|(PlayerMenu[0].status & IT_CENTER)); + SP_PlayerMenu[0].status &= ~IT_DYBIGSPACE; // Correcting a hack that may be made below. for (i = 0; i < 32; i++) // Handle charsels, availability, and unlocks. { - if (PlayerMenu[i].status != IT_DISABLED) // If the character's disabled through SOC, there's nothing we can do for it. + if (description[i].used) // If the character's disabled through SOC, there's nothing we can do for it. { name = strtok(Z_StrDup(description[i].skinname), "&"); skinnum = R_SkinAvailable(name); - if ((skinnum != -1) && (R_SkinUnlock(skinnum))) + if ((skinnum != -1) && (R_SkinUsable(-1, skinnum))) { // Handling order. if (firstvalid == 255) @@ -4792,14 +5369,13 @@ static void M_SetupChoosePlayer(INT32 choice) } lastvalid = i; - // Handling visibility. - if (PlayerMenu[i].status & (IT_DISABLED|IT_CENTER)) - PlayerMenu[i].status = IT_CALL; + if (i == char_on) + allowed = true; + if (description[i].picname[0] == '\0') strncpy(description[i].picname, skins[skinnum].charsel, 8); } - else // Technically, character select icons without corresponding skins get bundled away behind this too. Sucks to be them. - PlayerMenu[i].status = (IT_DISABLED|IT_CENTER); + // else -- Technically, character select icons without corresponding skins get bundled away behind this too. Sucks to be them. Z_Free(name); } } @@ -4815,7 +5391,7 @@ static void M_SetupChoosePlayer(INT32 choice) } else // We're being forced into a specific character, so might as well. { - PlayerMenu[0].status = (IT_CALL|IT_DYBIGSPACE|(PlayerMenu[0].status & IT_CENTER)); // This is a hack to make a non-IT_CALL character in slot 0 not softlock the game. IT_DYBIGSPACE is a dummy flag, whilst IT_CENTER is preserved. + SP_PlayerMenu[0].status |= IT_DYBIGSPACE; // This is a dummy flag hack to make a non-IT_CALL character in slot 0 not softlock the game. M_ChoosePlayer(0); return; } @@ -4829,9 +5405,77 @@ static void M_SetupChoosePlayer(INT32 choice) SP_PlayerDef.prevMenu = currentMenu; M_SetupNextMenu(&SP_PlayerDef); - char_scroll = itemOn*128*FRACUNIT; // finish scrolling the menu + if (!allowed) + char_on = firstvalid; + char_scroll = 0; // finish scrolling the menu Z_Free(char_notes); - char_notes = NULL; + char_notes = V_WordWrap(0, 21*8, V_ALLOWLOWERCASE, description[char_on].notes); +} + +// +// M_HandleChoosePlayerMenu +// +// Reacts to your key inputs. Basically a mini menu thinker. +// +static void M_HandleChoosePlayerMenu(INT32 choice) +{ + boolean exitmenu = false; // exit to previous menu + INT32 selectval; + + switch (choice) + { + case KEY_DOWNARROW: + if ((selectval = description[char_on].next) != char_on) + { + S_StartSound(NULL,sfx_s3kb7); + char_on = selectval; + char_scroll = -128*FRACUNIT; + Z_Free(char_notes); + char_notes = V_WordWrap(0, 21*8, V_ALLOWLOWERCASE, description[char_on].notes); + } + else if (!char_scroll) + { + S_StartSound(NULL,sfx_s3kb7); + char_scroll = 16*FRACUNIT; + } + break; + + case KEY_UPARROW: + if ((selectval = description[char_on].prev) != char_on) + { + S_StartSound(NULL,sfx_s3kb7); + char_on = selectval; + char_scroll = 128*FRACUNIT; + Z_Free(char_notes); + char_notes = V_WordWrap(0, 21*8, V_ALLOWLOWERCASE, description[char_on].notes); + } + else if (!char_scroll) + { + S_StartSound(NULL,sfx_s3kb7); + char_scroll = -16*FRACUNIT; + } + break; + + case KEY_ENTER: + S_StartSound(NULL, sfx_menu1); + M_ChoosePlayer(char_on); + break; + + case KEY_ESCAPE: + exitmenu = true; + break; + + default: + break; + } + + if (exitmenu) + { + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu); + else + M_ClearMenus(true); + } } // Draw the choose player setup menu, had some fun with player anim @@ -4841,7 +5485,6 @@ static void M_DrawSetupChoosePlayerMenu(void) patch_t *patch; INT32 i, o; UINT8 prev, next; - boolean loophack = false; // Black BG V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); @@ -4850,37 +5493,20 @@ static void M_DrawSetupChoosePlayerMenu(void) // Character select profile images!1 M_DrawTextBox(0, my, 16, 20); - i = (itemOn*128 - (char_scroll / FRACUNIT)); - - if (!char_notes) - { - if (i) // turns out this and the preceding check is better then (abs(i) > 128) - { - o = (lastdirection) ? -1 : 1; - char_scroll = (itemOn + o)*128*FRACUNIT; - i = -o*128; - } - char_notes = V_WordWrap(0, 21*8, V_ALLOWLOWERCASE, description[itemOn].notes); - } - - if (abs(i) > 1) - char_scroll += i*FRACUNIT>>2; + if (abs(char_scroll) > FRACUNIT) + char_scroll -= (char_scroll>>2); else // close enough. - char_scroll = itemOn*128*FRACUNIT; // just be exact now. + char_scroll = 0; // just be exact now. - o = ((char_scroll / FRACUNIT) + 16); + o = (char_scroll >> FRACBITS) + 16; - if (o < 0) // This hack is to prevent visual glitches when looping from the last character to the 1st character. - loophack = true; - - if (loophack) + if (o < 0) // A little hacky... + { + i = description[char_on].prev; o += 128; - - i = (o / 128); - o = (o % 128); - - if (loophack) - i = description[i].prev; + } + else + i = char_on; // Get prev character... prev = description[i].prev; @@ -4891,7 +5517,7 @@ static void M_DrawSetupChoosePlayerMenu(void) next = description[i].next; // Draw prev character if it's visible and its number isn't greater than the current one or there's more than two - if (o < 32) // (prev != i) was previously a part of this, but we don't need to check again after above. + if (o < 32) { patch = W_CachePatchName(description[prev].picname, PU_CACHE); if (SHORT(patch->width) >= 256) @@ -4911,31 +5537,24 @@ static void M_DrawSetupChoosePlayerMenu(void) V_DrawCroppedPatch(8<width), o); W_UnlockCachedPatch(patch); } - - // current character - if (PlayerMenu[i].status & IT_DISABLED) // Prevent flickering. - i = (lastdirection) ? prev : next; // This actually causes duplication at slow scroll speeds (<16FU per tic), but thankfully we always go quickly. } - if (!(PlayerMenu[i].status & IT_DISABLED)) + patch = W_CachePatchName(description[i].picname, PU_CACHE); + if (o >= 0 && o <= 32) { - patch = W_CachePatchName(description[i].picname, PU_CACHE); - if (o >= 0 && o <= 32) - { - if (SHORT(patch->width) >= 256) - V_DrawSmallScaledPatch(8, my + 40 - o, 0, patch); - else - V_DrawScaledPatch(8, my + 40 - o, 0, patch); - } + if (SHORT(patch->width) >= 256) + V_DrawSmallScaledPatch(8, my + 40 - o, 0, patch); else - { - if (SHORT(patch->width) >= 256) - V_DrawCroppedPatch(8<width), SHORT(patch->height)); - else - V_DrawCroppedPatch(8<width), SHORT(patch->height)); - } - W_UnlockCachedPatch(patch); + V_DrawScaledPatch(8, my + 40 - o, 0, patch); } + else + { + if (SHORT(patch->width) >= 256) + V_DrawCroppedPatch(8<width), SHORT(patch->height)); + else + V_DrawCroppedPatch(8<width), SHORT(patch->height)); + } + W_UnlockCachedPatch(patch); // draw title (or big pic) M_DrawMenuTitle(); @@ -4953,10 +5572,10 @@ static void M_ChoosePlayer(INT32 choice) boolean ultmode = (ultimate_selectable && SP_PlayerDef.prevMenu == &SP_LoadDef && saveSlotSelected == NOSAVESLOT); // skip this if forcecharacter or no characters available - if (!(PlayerMenu[choice].status & IT_DYBIGSPACE)) + if (!(SP_PlayerMenu[0].status & IT_DYBIGSPACE)) { // M_SetupChoosePlayer didn't call us directly, that means we've been properly set up. - char_scroll = itemOn*128*FRACUNIT; // finish scrolling the menu + char_scroll = 0; // finish scrolling the menu M_DrawSetupChoosePlayerMenu(); // draw the finally selected character one last time for the fadeout } M_ClearMenus(true); @@ -5255,9 +5874,7 @@ void M_DrawTimeAttackMenu(void) { INT32 i, x, y, cursory = 0; UINT16 dispstatus; - patch_t *PictureOfLevel, *PictureOfUrFace; - lumpnum_t lumpnum; - char beststr[40]; + patch_t *PictureOfUrFace; S_ChangeMusicInternal("_inter", true); // Eww, but needed for when user hits escape during demo playback @@ -5301,16 +5918,6 @@ void M_DrawTimeAttackMenu(void) V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text); - // A 160x100 image of the level as entry MAPxxP - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); - - if (lumpnum != LUMPERROR) - PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE); - else - PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); - - V_DrawSmallScaledPatch(208, 32, 0, PictureOfLevel); - // Character face! if (W_CheckNumForName(skins[cv_chooseskin.value-1].charsel) != LUMPERROR) { @@ -5326,16 +5933,31 @@ void M_DrawTimeAttackMenu(void) { emblem_t *em; INT32 yHeight; + patch_t *PictureOfLevel; + lumpnum_t lumpnum; + char beststr[40]; - V_DrawCenteredString(104, 32, 0, "* LEVEL RECORDS *"); + M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true); + + // A 160x100 image of the level as entry MAPxxP + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); + + if (lumpnum != LUMPERROR) + PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE); + else + PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + + V_DrawSmallScaledPatch(208, 32+lsheadingheight, 0, PictureOfLevel); + + V_DrawString(104 - 72, 32+lsheadingheight/2, 0, "* LEVEL RECORDS *"); if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->score) sprintf(beststr, "(none)"); else sprintf(beststr, "%u", mainrecords[cv_nextmap.value-1]->score); - V_DrawString(104-72, 48, V_YELLOWMAP, "SCORE:"); - V_DrawRightAlignedString(104+72, 48, V_ALLOWLOWERCASE, beststr); + V_DrawString(104-72, 48+lsheadingheight/2, V_YELLOWMAP, "SCORE:"); + V_DrawRightAlignedString(104+72, 48+lsheadingheight/2, V_ALLOWLOWERCASE, beststr); if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->time) sprintf(beststr, "(none)"); @@ -5344,16 +5966,16 @@ void M_DrawTimeAttackMenu(void) G_TicsToSeconds(mainrecords[cv_nextmap.value-1]->time), G_TicsToCentiseconds(mainrecords[cv_nextmap.value-1]->time)); - V_DrawString(104-72, 58, V_YELLOWMAP, "TIME:"); - V_DrawRightAlignedString(104+72, 58, V_ALLOWLOWERCASE, beststr); + V_DrawString(104-72, 58+lsheadingheight/2, V_YELLOWMAP, "TIME:"); + V_DrawRightAlignedString(104+72, 58+lsheadingheight/2, V_ALLOWLOWERCASE, beststr); if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->rings) sprintf(beststr, "(none)"); else sprintf(beststr, "%hu", mainrecords[cv_nextmap.value-1]->rings); - V_DrawString(104-72, 68, V_YELLOWMAP, "RINGS:"); - V_DrawRightAlignedString(104+72, 68, V_ALLOWLOWERCASE, beststr); + V_DrawString(104-72, 68+lsheadingheight/2, V_YELLOWMAP, "RINGS:"); + V_DrawRightAlignedString(104+72, 68+lsheadingheight/2, V_ALLOWLOWERCASE, beststr); // Draw record emblems. em = M_GetLevelEmblems(cv_nextmap.value); @@ -5369,17 +5991,17 @@ void M_DrawTimeAttackMenu(void) } if (em->collected) - V_DrawSmallMappedPatch(104+76, yHeight, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE), + V_DrawSmallMappedPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE)); else - V_DrawSmallScaledPatch(104+76, yHeight, 0, W_CachePatchName("NEEDIT", PU_CACHE)); + V_DrawSmallScaledPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDIT", PU_CACHE)); skipThisOne: em = M_GetLevelEmblems(-1); } } - // ALWAYS DRAW level name and skin even when not on this menu! + // ALWAYS DRAW level and skin even when not on this menu! if (currentMenu != &SP_TimeAttackDef) { consvar_t *ncv; @@ -5387,27 +6009,30 @@ void M_DrawTimeAttackMenu(void) x = SP_TimeAttackDef.x; y = SP_TimeAttackDef.y; - for (i = 0; i < 2; ++i) - { - ncv = (consvar_t *)SP_TimeAttackMenu[i].itemaction; + V_DrawString(x, y + SP_TimeAttackMenu[talevel].alphaKey, V_TRANSLUCENT, SP_TimeAttackMenu[talevel].text); - V_DrawString(x, y + SP_TimeAttackMenu[i].alphaKey, V_TRANSLUCENT, SP_TimeAttackMenu[i].text); - V_DrawString(BASEVIDWIDTH - x - V_StringWidth(ncv->string, 0), - y + SP_TimeAttackMenu[i].alphaKey, V_YELLOWMAP|V_TRANSLUCENT, ncv->string); - } + ncv = (consvar_t *)SP_TimeAttackMenu[taplayer].itemaction; + V_DrawString(x, y + SP_TimeAttackMenu[taplayer].alphaKey, V_TRANSLUCENT, SP_TimeAttackMenu[taplayer].text); + V_DrawString(BASEVIDWIDTH - x - V_StringWidth(ncv->string, 0), y + SP_TimeAttackMenu[taplayer].alphaKey, V_YELLOWMAP|V_TRANSLUCENT, ncv->string); } } +static void M_TimeAttackLevelSelect(INT32 choice) +{ + (void)choice; + SP_TimeAttackLevelSelectDef.prevMenu = currentMenu; + M_SetupNextMenu(&SP_TimeAttackLevelSelectDef); +} + // Going to Time Attack menu... static void M_TimeAttack(INT32 choice) { (void)choice; - memset(skins_cons_t, 0, sizeof (skins_cons_t)); - + SP_TimeAttackDef.prevMenu = &MainDef; levellistmode = LLM_RECORDATTACK; // Don't be dependent on cv_newgametype - if (M_CountLevelsToShowInList() == 0) + if (!M_PrepareLevelPlatter(-1)) { M_StartMessage(M_GetText("No record-attackable levels found.\n"),NULL,MM_NOTHING); return; @@ -5415,119 +6040,158 @@ static void M_TimeAttack(INT32 choice) M_PatchSkinNameTable(); - M_PrepareLevelSelect(); M_SetupNextMenu(&SP_TimeAttackDef); - Nextmap_OnChange(); - - itemOn = tastart; // "Start" is selected. + if (!M_CanShowLevelInList(cv_nextmap.value-1, -1) && levelselect.rows[0].maplist[0]) + CV_SetValue(&cv_nextmap, levelselect.rows[0].maplist[0]); + else + Nextmap_OnChange(); G_SetGamestate(GS_TIMEATTACK); S_ChangeMusicInternal("_inter", true); + + itemOn = tastart; // "Start" is selected. } // Drawing function for Nights Attack void M_DrawNightsAttackMenu(void) { - patch_t *PictureOfLevel; - lumpnum_t lumpnum; - char beststr[40]; + INT32 i, x, y, cursory = 0; + UINT16 dispstatus; S_ChangeMusicInternal("_inter", true); // Eww, but needed for when user hits escape during demo playback V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); + M_DrawMenuTitle(); + // draw menu (everything else goes on top of it) - M_DrawGenericMenu(); + // Sadly we can't just use generic mode menus because we need some extra hacks + x = currentMenu->x; + y = currentMenu->y; - // A 160x100 image of the level as entry MAPxxP - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); + for (i = 0; i < currentMenu->numitems; ++i) + { + dispstatus = (currentMenu->menuitems[i].status & IT_DISPLAY); + if (dispstatus != IT_STRING && dispstatus != IT_WHITESTRING) + continue; - if (lumpnum != LUMPERROR) - PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE); - else - PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + y = currentMenu->y+currentMenu->menuitems[i].alphaKey; + if (i == itemOn) + cursory = y; - V_DrawSmallScaledPatch(90, 28, 0, PictureOfLevel); + V_DrawString(x, y, (dispstatus == IT_WHITESTRING) ? V_YELLOWMAP : 0 , currentMenu->menuitems[i].text); + + // Cvar specific handling + if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_CVAR) + { + consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; + INT32 soffset = 0; + + // hack to keep the menu from overlapping the overall grade icon + if (currentMenu != &SP_NightsAttackDef) + soffset = 80; + + // Should see nothing but strings + V_DrawString(BASEVIDWIDTH - x - soffset - V_StringWidth(cv->string, 0), y, V_YELLOWMAP, cv->string); + } + } + + // DRAW THE SKULL CURSOR + V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); + V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text); // Level record list if (cv_nextmap.value) { emblem_t *em; INT32 yHeight; + patch_t *PictureOfLevel; + lumpnum_t lumpnum; + char beststr[40]; UINT8 bestoverall = G_GetBestNightsGrade(cv_nextmap.value, 0); UINT8 bestgrade = G_GetBestNightsGrade(cv_nextmap.value, cv_dummymares.value); UINT32 bestscore = G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value); tic_t besttime = G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value); - if (P_HasGrades(cv_nextmap.value, 0)) - V_DrawScaledPatch(200, 28 + 8, 0, ngradeletters[bestoverall]); + M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true); - if (currentMenu == &SP_NightsAttackDef) - { - if (P_HasGrades(cv_nextmap.value, cv_dummymares.value)) - { - V_DrawString(160-88, 112, V_YELLOWMAP, "BEST GRADE:"); - V_DrawSmallScaledPatch(160 + 86 - (ngradeletters[bestgrade]->width/2), - 112 + 8 - (ngradeletters[bestgrade]->height/2), - 0, ngradeletters[bestgrade]); - } + // A 160x100 image of the level as entry MAPxxP + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); - if (!bestscore) - sprintf(beststr, "(none)"); - else - sprintf(beststr, "%u", bestscore); - - V_DrawString(160 - 88, 122, V_YELLOWMAP, "BEST SCORE:"); - V_DrawRightAlignedString(160 + 88, 122, V_ALLOWLOWERCASE, beststr); - - if (besttime == UINT32_MAX) - sprintf(beststr, "(none)"); - else - sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(besttime, true), - G_TicsToSeconds(besttime), - G_TicsToCentiseconds(besttime)); - - V_DrawString(160-88, 132, V_YELLOWMAP, "BEST TIME:"); - V_DrawRightAlignedString(160+88, 132, V_ALLOWLOWERCASE, beststr); - - if (cv_dummymares.value == 0) { - // Draw record emblems. - em = M_GetLevelEmblems(cv_nextmap.value); - while (em) - { - switch (em->type) - { - case ET_NGRADE: yHeight = 112; break; - case ET_NTIME: yHeight = 132; break; - default: - goto skipThisOne; - } - - if (em->collected) - V_DrawSmallMappedPatch(160+88, yHeight, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE), - R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE)); - else - V_DrawSmallScaledPatch(160+88, yHeight, 0, W_CachePatchName("NEEDIT", PU_CACHE)); - - skipThisOne: - em = M_GetLevelEmblems(-1); - } - } - } - // ALWAYS DRAW level name even when not on this menu! + if (lumpnum != LUMPERROR) + PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE); else - { - consvar_t *ncv; - INT32 x = SP_NightsAttackDef.x; - INT32 y = SP_NightsAttackDef.y; + PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); - ncv = (consvar_t *)SP_NightsAttackMenu[0].itemaction; - V_DrawString(x, y + SP_NightsAttackMenu[0].alphaKey, V_TRANSLUCENT, SP_NightsAttackMenu[0].text); - V_DrawString(BASEVIDWIDTH - x - V_StringWidth(ncv->string, 0), - y + SP_NightsAttackMenu[0].alphaKey, V_YELLOWMAP|V_TRANSLUCENT, ncv->string); + V_DrawSmallScaledPatch(208, 32+lsheadingheight, 0, PictureOfLevel); + + V_DrawString(104 - 72, 32+lsheadingheight/2, 0, "* LEVEL RECORDS *"); + + if (P_HasGrades(cv_nextmap.value, 0)) + V_DrawScaledPatch(235, 135, 0, ngradeletters[bestoverall]); + + if (P_HasGrades(cv_nextmap.value, cv_dummymares.value)) + {//make bigger again + V_DrawString(104 - 72, 48+lsheadingheight/2, V_YELLOWMAP, "BEST GRADE:"); + V_DrawSmallScaledPatch(104 + 72 - (ngradeletters[bestgrade]->width/2), + 48+lsheadingheight/2 + 8 - (ngradeletters[bestgrade]->height/2), + 0, ngradeletters[bestgrade]); + } + + if (!bestscore) + sprintf(beststr, "(none)"); + else + sprintf(beststr, "%u", bestscore); + + V_DrawString(104 - 72, 58+lsheadingheight/2, V_YELLOWMAP, "BEST SCORE:"); + V_DrawRightAlignedString(104 + 72, 58+lsheadingheight/2, V_ALLOWLOWERCASE, beststr); + + if (besttime == UINT32_MAX) + sprintf(beststr, "(none)"); + else + sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(besttime, true), + G_TicsToSeconds(besttime), + G_TicsToCentiseconds(besttime)); + + V_DrawString(104 - 72, 68+lsheadingheight/2, V_YELLOWMAP, "BEST TIME:"); + V_DrawRightAlignedString(104 + 72, 68+lsheadingheight/2, V_ALLOWLOWERCASE, beststr); + + if (cv_dummymares.value == 0) { + // Draw record emblems. + em = M_GetLevelEmblems(cv_nextmap.value); + while (em) + { + switch (em->type) + { + case ET_NGRADE: yHeight = 48; break; + case ET_NTIME: yHeight = 68; break; + default: + goto skipThisOne; + } + + if (em->collected) + V_DrawSmallMappedPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE), + R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE)); + else + V_DrawSmallScaledPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDIT", PU_CACHE)); + + skipThisOne: + em = M_GetLevelEmblems(-1); + } } } + + // ALWAYS DRAW level even when not on this menu! + if (currentMenu != &SP_NightsAttackDef) + V_DrawString(SP_NightsAttackDef.x, SP_NightsAttackDef.y + SP_TimeAttackMenu[nalevel].alphaKey, V_TRANSLUCENT, SP_NightsAttackMenu[nalevel].text); +} + +static void M_NightsAttackLevelSelect(INT32 choice) +{ + (void)choice; + SP_NightsAttackLevelSelectDef.prevMenu = currentMenu; + M_SetupNextMenu(&SP_NightsAttackLevelSelectDef); } // Going to Nights Attack menu... @@ -5535,27 +6199,27 @@ static void M_NightsAttack(INT32 choice) { (void)choice; - memset(skins_cons_t, 0, sizeof (skins_cons_t)); - + SP_NightsAttackDef.prevMenu = &MainDef; levellistmode = LLM_NIGHTSATTACK; // Don't be dependent on cv_newgametype - if (M_CountLevelsToShowInList() == 0) + if (!M_PrepareLevelPlatter(-1)) { M_StartMessage(M_GetText("No NiGHTS-attackable levels found.\n"),NULL,MM_NOTHING); return; } - // This is really just to make sure Sonic is the played character, just in case M_PatchSkinNameTable(); - M_PrepareLevelSelect(); M_SetupNextMenu(&SP_NightsAttackDef); - Nextmap_OnChange(); - - itemOn = nastart; // "Start" is selected. + if (!M_CanShowLevelInList(cv_nextmap.value-1, -1) && levelselect.rows[0].maplist[0]) + CV_SetValue(&cv_nextmap, levelselect.rows[0].maplist[0]); + else + Nextmap_OnChange(); G_SetGamestate(GS_TIMEATTACK); S_ChangeMusicInternal("_inter", true); + + itemOn = nastart; // "Start" is selected. } // Player has selected the "START" from the nights attack screen @@ -5671,8 +6335,7 @@ static void M_EraseGuest(INT32 choice) M_SetupNextMenu(&SP_NightsAttackDef); else M_SetupNextMenu(&SP_TimeAttackDef); - CV_AddValue(&cv_nextmap, -1); - CV_AddValue(&cv_nextmap, 1); + Nextmap_OnChange(); M_StartMessage(M_GetText("Guest replay data erased.\n"),NULL,MM_NOTHING); } @@ -5698,8 +6361,7 @@ static void M_OverwriteGuest(const char *which, boolean nights) M_SetupNextMenu(&SP_NightsAttackDef); else M_SetupNextMenu(&SP_TimeAttackDef); - CV_AddValue(&cv_nextmap, -1); - CV_AddValue(&cv_nextmap, 1); + Nextmap_OnChange(); M_StartMessage(M_GetText("Guest replay data saved.\n"),NULL,MM_NOTHING); } @@ -5790,9 +6452,7 @@ static void M_ModeAttackEndGame(INT32 choice) G_SetGamestate(GS_TIMEATTACK); modeattacking = ATTACKING_NONE; S_ChangeMusicInternal("_inter", true); - // Update replay availability. - CV_AddValue(&cv_nextmap, 1); - CV_AddValue(&cv_nextmap, -1); + Nextmap_OnChange(); } // ======== @@ -6164,25 +6824,6 @@ static void M_ChooseRoom(INT32 choice) // Start Server Menu //=========================================================================== -// -// FindFirstMap -// -// Finds the first map of a particular gametype -// Defaults to 1 if nothing found. -// -static INT32 M_FindFirstMap(INT32 gtype) -{ - INT32 i; - - for (i = 0; i < NUMMAPS; i++) - { - if (mapheaderinfo[i] && (mapheaderinfo[i]->typeoflevel & gtype)) - return i + 1; - } - - return 1; -} - static void M_StartServer(INT32 choice) { boolean StartSplitScreenGame = (currentMenu == &MP_SplitServerDef); @@ -6223,15 +6864,13 @@ static void M_StartServer(INT32 choice) static void M_DrawServerMenu(void) { - lumpnum_t lumpnum; - patch_t *PictureOfLevel; - M_DrawGenericMenu(); #ifndef NONET // Room name if (currentMenu == &MP_ServerDef) { + M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight/2, "Server settings", true); if (ms_RoomId < 0) V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, V_YELLOWMAP, (itemOn == mp_server_room) ? "