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/appveyor.yml b/appveyor.yml index 25b95d292..b0544a90b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -47,7 +47,7 @@ before_build: - upx -V - ccache -V - ccache -s -- set SRB2_MFLAGS=-C src MINGW=1 WARNINGMODE=1 GCC53=1 CCACHE=1 +- set SRB2_MFLAGS=-C src MINGW=1 WARNINGMODE=1 GCC63=1 CCACHE=1 build_script: - cmd: mingw32-make.exe %SRB2_MFLAGS% %CONFIGURATION%=1 clean diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 72404becc..80d018c4b 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -7,6 +7,23 @@ # and other things # + +ifdef GCC63 +GCC62=1 +endif + +ifdef GCC62 +GCC61=1 +endif + +ifdef GCC61 +GCC54=1 +endif + +ifdef GCC54 +GCC53=1 +endif + ifdef GCC53 GCC52=1 endif @@ -164,19 +181,29 @@ ifdef GCC45 WFLAGS+=-Wunsuffixed-float-constants endif endif + ifdef NOLDWARNING LDFLAGS+=-Wl,--as-needed endif + ifdef ERRORMODE WFLAGS+=-Werror endif + +WFLAGS+=$(OLDWFLAGS) + ifdef GCC43 #WFLAGS+=-Wno-error=clobbered endif ifdef GCC46 WFLAGS+=-Wno-error=suggest-attribute=noreturn endif -WFLAGS+=$(OLDWFLAGS) +ifdef GCC54 + WFLAGS+=-Wno-logical-op -Wno-error=logical-op +endif +ifdef GCC61 + WFLAGS+=-Wno-tautological-compare -Wno-error=tautological-compare +endif #indicate platform and what interface use with 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_main.c b/src/d_main.c index 63f6bd02c..b23ffebb4 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -187,7 +187,7 @@ UINT8 altdown = 0; // 0x1 left, 0x2 right // static inline void D_ModifierKeyResponder(event_t *ev) { - if (ev->type == ev_keydown) switch (ev->data1) + if (ev->type == ev_keydown || ev->type == ev_console) switch (ev->data1) { case KEY_LSHIFT: shiftdown |= 0x1; return; case KEY_RSHIFT: shiftdown |= 0x2; return; diff --git a/src/d_net.c b/src/d_net.c index fae1ea311..7f16c302d 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -991,12 +991,14 @@ void Command_Droprate(void) packetdroprate = droprate; } +#ifndef NONET static boolean ShouldDropPacket(void) { return (packetdropquantity[netbuffer->packettype]) || (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100; } #endif +#endif // // HSendPacket diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 55a3b30f9..6b97eb5b7 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1011,7 +1011,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. @@ -1109,6 +1109,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) { @@ -1127,7 +1129,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; @@ -1174,7 +1176,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; @@ -1182,6 +1184,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); @@ -1224,6 +1227,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) { @@ -1252,7 +1257,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; @@ -1307,6 +1312,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) #endif READSTRINGN(*cp, name, MAXPLAYERNAME); + p->availabilities = READUINT32(*cp); color = READUINT8(*cp); skin = READUINT8(*cp); @@ -1323,6 +1329,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()) @@ -1337,6 +1344,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]; @@ -1532,10 +1549,13 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese // The supplied data are assumed to be good. I_Assert(delay >= 0 && delay <= 2); + if (mapnum != -1) + CV_SetValue(&cv_nextmap, mapnum); + CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d ultmode=%d resetplayers=%d delay=%d skipprecutscene=%d\n", mapnum, newgametype, pultmode, resetplayers, delay, skipprecutscene); - if (netgame || multiplayer) + if ((netgame || multiplayer) && !((gametype == newgametype) && (newgametype == GT_COOP))) FLS = false; if (delay != 2) @@ -1701,9 +1721,19 @@ static void Command_Map_f(void) } } + // Prevent warping to locked levels + // ... unless you're in a dedicated server. Yes, technically this means you can view any level by + // running a dedicated server and joining it yourself, but that's better than making dedicated server's + // lives hell. + if (!dedicated && M_MapLocked(newmapnum)) + { + CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n")); + return; + } + // don't use a gametype the map doesn't support if (cv_debug || COM_CheckParm("-force") || cv_skipmapcheck.value) - ; // The player wants us to trek on anyway. Do so. + fromlevelselect = false; // The player wants us to trek on anyway. Do so. // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer // Alternatively, bail if the map header is completely missing anyway. else if (!mapheaderinfo[newmapnum-1] @@ -1722,19 +1752,10 @@ static void Command_Map_f(void) CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring); return; } + else + fromlevelselect = ((netgame || multiplayer) && ((gametype == newgametype) && (newgametype == GT_COOP))); - // Prevent warping to locked levels - // ... unless you're in a dedicated server. Yes, technically this means you can view any level by - // running a dedicated server and joining it yourself, but that's better than making dedicated server's - // lives hell. - if (!dedicated && M_MapLocked(newmapnum)) - { - CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n")); - return; - } - - fromlevelselect = false; - D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, false); + D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect); } /** Receives a map command and changes the map. @@ -1800,17 +1821,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(); @@ -4042,13 +4060,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; @@ -4057,7 +4068,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); } } diff --git a/src/d_player.h b/src/d_player.h index dd6855786..05a99db2a 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, @@ -263,9 +266,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; @@ -344,6 +345,7 @@ typedef struct player_s UINT8 skincolor; INT32 skin; + UINT32 availabilities; UINT32 score; // player score fixed_t dashspeed; // dashing speed @@ -381,7 +383,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 dda17a2e5..8657e8de3 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -432,22 +432,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. @@ -479,7 +477,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++) @@ -528,7 +526,7 @@ 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; DEH_WriteUndoline(word, &description[num].picname[0], UNDO_NONE); @@ -536,12 +534,6 @@ static void readPlayer(MYFILE *f, INT32 num) } 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. @@ -549,15 +541,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; - DEH_WriteUndoline(word, va("%d", PlayerMenu[num].status), UNDO_NONE); - PlayerMenu[num].status = (INT16)i; + DEH_WriteUndoline(word, va("%d", description[num].used), UNDO_NONE); + 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; DEH_WriteUndoline(word, description[num].skinname, UNDO_NONE); @@ -1211,6 +1203,12 @@ static void readlevelheader(MYFILE *f, INT32 num) { deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2, sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num)); + strlcpy(mapheaderinfo[num-1]->selectheading, word2, sizeof(mapheaderinfo[num-1]->selectheading)); // not deh_ so only complains once + } + else if (fastcmp(word, "SELECTHEADING")) + { + deh_strlcpy(mapheaderinfo[num-1]->selectheading, word2, + sizeof(mapheaderinfo[num-1]->selectheading), va("Level header %d: selectheading", num)); } else if (fastcmp(word, "SCRIPTNAME")) { @@ -1418,6 +1416,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); } @@ -2277,6 +2282,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; } @@ -2898,7 +2905,7 @@ static void readpatch(MYFILE *f, const char *name, UINT16 wad) char *word2; char *tmp; INT32 i = 0, j = 0, value; - texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX, 0}; + texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX, 0, 255, AST_COPY}; // Jump to the texture this patch belongs to, which, // coincidentally, is always the last one on the buffer cache. @@ -3910,12 +3917,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", @@ -3923,6 +3930,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", @@ -3933,30 +3943,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", @@ -3995,7 +4000,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", @@ -4772,11 +4777,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_METALSONIC_FLOAT", "S_METALSONIC_VECTOR", "S_METALSONIC_STUN", - "S_METALSONIC_BLOCK", "S_METALSONIC_RAISE", "S_METALSONIC_GATHER", "S_METALSONIC_DASH", "S_METALSONIC_BOUNCE", + "S_METALSONIC_BADBOUNCE", "S_METALSONIC_SHOOT", "S_METALSONIC_PAIN", "S_METALSONIC_DEATH", @@ -5920,14 +5925,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", @@ -6718,9 +6727,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 @@ -6734,6 +6743,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", @@ -6957,10 +6968,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", @@ -6972,12 +6981,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", @@ -6991,7 +6999,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. }; @@ -7141,8 +7149,7 @@ static const char *const POWERS_LIST[] = { "NIGHTS_LINKFREEZE", //for linedef exec 427 - "NOCONTROL", - "INGOOP" // In goop + "NOCONTROL" }; static const char *const HUDITEMS_LIST[] = { @@ -7234,14 +7241,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) @@ -7304,6 +7312,7 @@ struct { {"LF2_RECORDATTACK",LF2_RECORDATTACK}, {"LF2_NIGHTSATTACK",LF2_NIGHTSATTACK}, {"LF2_NOVISITNEEDED",LF2_NOVISITNEEDED}, + {"LF2_WIDEICON",LF2_WIDEICON}, // NiGHTS grades {"GRADE_F",GRADE_F}, @@ -7365,6 +7374,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}, @@ -7380,8 +7391,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}, @@ -7391,7 +7402,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 @@ -7409,12 +7422,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 @@ -7425,7 +7438,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}, @@ -7478,7 +7496,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}, @@ -7667,6 +7685,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 a35c17ba6..73ecf7dc9 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 @@ -543,4 +543,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 5e04af496..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; @@ -2901,7 +2891,7 @@ static void G_DoCompleted(void) if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) P_AllocMapHeader(nextmap); - if (skipstats) + if (skipstats && !modeattacking) // Don't skip stats if we're in record attack G_AfterIntermission(); else { @@ -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/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 54dd94854..3a0bf7054 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1836,7 +1836,7 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value) } } -static inline void DrawMD2Ex(INT32 *gl_cmd_buffer, md2_frame_t *frame, UINT32 duration, UINT32 tics, md2_frame_t *nextframe, FTransform *pos, float scale, UINT8 flipped, UINT8 *color) +static void DrawMD2Ex(INT32 *gl_cmd_buffer, md2_frame_t *frame, UINT32 duration, UINT32 tics, md2_frame_t *nextframe, FTransform *pos, float scale, UINT8 flipped, UINT8 *color) { INT32 val, count, pindex; GLfloat s, t; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 01d4ed524..f6275631c 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -198,21 +198,6 @@ void HU_LoadGraphics(void) tny_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); } - // cache the level title font for entire game execution - lt_font[0] = (patch_t *)W_CachePatchName("LTFNT039", PU_HUDGFX); /// \note fake start hack - - // Number support - lt_font[9] = (patch_t *)W_CachePatchName("LTFNT048", PU_HUDGFX); - lt_font[10] = (patch_t *)W_CachePatchName("LTFNT049", PU_HUDGFX); - lt_font[11] = (patch_t *)W_CachePatchName("LTFNT050", PU_HUDGFX); - lt_font[12] = (patch_t *)W_CachePatchName("LTFNT051", PU_HUDGFX); - lt_font[13] = (patch_t *)W_CachePatchName("LTFNT052", PU_HUDGFX); - lt_font[14] = (patch_t *)W_CachePatchName("LTFNT053", PU_HUDGFX); - lt_font[15] = (patch_t *)W_CachePatchName("LTFNT054", PU_HUDGFX); - lt_font[16] = (patch_t *)W_CachePatchName("LTFNT055", PU_HUDGFX); - lt_font[17] = (patch_t *)W_CachePatchName("LTFNT056", PU_HUDGFX); - lt_font[18] = (patch_t *)W_CachePatchName("LTFNT057", PU_HUDGFX); - j = LT_FONTSTART; for (i = 0; i < LT_FONTSIZE; i++) { @@ -1241,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 fdea617c1..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 @@ -1369,14 +1364,14 @@ state_t states[NUMSTATES] = {SPR_METL, 4, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_FLOAT {SPR_METL, 12, -1, {NULL}, 0, 0, S_METALSONIC_STUN}, // S_METALSONIC_VECTOR {SPR_METL, 0, -1, {NULL}, 0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_STUN - {SPR_METL, 13, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_BLOCK {SPR_METL, 13, 40, {NULL}, 0, 0, S_METALSONIC_GATHER},// S_METALSONIC_RAISE {SPR_METL, 14, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_GATHER - {SPR_METL, 9, -1, {NULL}, 0, 0, S_METALSONIC_BOUNCE},// S_METALSONIC_DASH + {SPR_METL, 15, -1, {NULL}, 0, 0, S_METALSONIC_BOUNCE},// S_METALSONIC_DASH {SPR_METL, 14, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_BOUNCE + {SPR_METL, 16, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_BADBOUNCE {SPR_METL, 13, -1, {NULL}, 0, 0, S_METALSONIC_GATHER},// S_METALSONIC_SHOOT {SPR_METL, 11, 40, {A_Pain}, 0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_PAIN - {SPR_METL, 0, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_METALSONIC_DEATH + {SPR_METL, 11, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_METALSONIC_DEATH {SPR_METL, 3, 4, {NULL}, 0, 0, S_METALSONIC_FLEE2}, // S_METALSONIC_FLEE1 {SPR_METL, 4, 4, {A_BossScream}, 0, 0, S_METALSONIC_FLEE3}, // S_METALSONIC_FLEE2 {SPR_METL, 5, 4, {NULL}, 0, 0, S_METALSONIC_FLEE4}, // S_METALSONIC_FLEE3 @@ -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 @@ -4952,7 +4951,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_METALSONIC_PAIN, // painstate S_METALSONIC_VECTOR,// painchance sfx_dmpain, // painsound - S_METALSONIC_BLOCK, // meleestate + S_METALSONIC_BADBOUNCE, // meleestate S_METALSONIC_SHOOT, // missilestate S_METALSONIC_DEATH, // deathstate S_METALSONIC_FLEE1, // xdeathstate @@ -4961,7 +4960,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 16*FRACUNIT, // radius 48*FRACUNIT, // height 0, // display offset - sfx_mspogo, // mass + sfx_s3k5a, // mass 3, // damage sfx_mswarp, // activesound MF_NOGRAVITY|MF_BOSS|MF_SLIDEME, // flags @@ -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 1a6c14a70..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, @@ -1575,11 +1570,11 @@ typedef enum state S_METALSONIC_FLOAT, S_METALSONIC_VECTOR, S_METALSONIC_STUN, - S_METALSONIC_BLOCK, S_METALSONIC_RAISE, S_METALSONIC_GATHER, S_METALSONIC_DASH, S_METALSONIC_BOUNCE, + S_METALSONIC_BADBOUNCE, S_METALSONIC_SHOOT, S_METALSONIC_PAIN, S_METALSONIC_DEATH, @@ -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 def0ad1b3..71f6a7e65 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -25,7 +25,10 @@ #include "lua_libs.h" #include "lua_hud.h" // hud_running errors -#define NOHUD if (hud_running) return luaL_error(L, "HUD rendering code should not call this function!"); +#define NOHUD if (hud_running)\ +return luaL_error(L, "HUD rendering code should not call this function!"); +#define INLEVEL if (gamestate != GS_LEVEL)\ +return luaL_error(L, "This function can only be used in a level!"); boolean luaL_checkboolean(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TBOOLEAN); @@ -84,6 +87,86 @@ static int lib_print(lua_State *L) return 0; } +static const struct { + const char *meta; + const char *utype; +} meta2utype[] = { + {META_STATE, "state_t"}, + {META_MOBJINFO, "mobjinfo_t"}, + {META_SFXINFO, "sfxinfo_t"}, + + {META_MOBJ, "mobj_t"}, + {META_MAPTHING, "mapthing_t"}, + + {META_PLAYER, "player_t"}, + {META_TICCMD, "ticcmd_t"}, + {META_SKIN, "skin_t"}, + {META_POWERS, "player_t.powers"}, + {META_SOUNDSID, "skin_t.soundsid"}, + + {META_VERTEX, "vertex_t"}, + {META_LINE, "line_t"}, + {META_SIDE, "side_t"}, + {META_SUBSECTOR, "subsector_t"}, + {META_SECTOR, "sector_t"}, + {META_FFLOOR, "ffloor_t"}, +#ifdef HAVE_LUA_SEGS + {META_SEG, "seg_t"}, + {META_NODE, "node_t"}, +#endif + {META_MAPHEADER, "mapheader_t"}, + + {META_CVAR, "consvar_t"}, + + {META_SECTORLINES, "sector_t.lines"}, + {META_SIDENUM, "line_t.sidenum"}, +#ifdef HAVE_LUA_SEGS + {META_NODEBBOX, "node_t.bbox"}, + {META_NODECHILDREN, "node_t.children"}, +#endif + + {META_BBOX, "bbox"}, + + {META_HUDINFO, "hudinfo_t"}, + {META_PATCH, "patch_t"}, + {META_COLORMAP, "colormap"}, + {META_CAMERA, "camera_t"}, + {NULL, NULL} +}; + +// goes through the above list and returns the utype string for the userdata type +// returns "unknown" instead if we couldn't find the right userdata type +static const char *GetUserdataUType(lua_State *L) +{ + UINT8 i; + lua_getmetatable(L, -1); + + for (i = 0; meta2utype[i].meta; i++) + { + luaL_getmetatable(L, meta2utype[i].meta); + if (lua_rawequal(L, -1, -2)) + { + lua_pop(L, 2); + return meta2utype[i].utype; + } + lua_pop(L, 1); + } + + lua_pop(L, 1); + return "unknown"; +} + +// Return a string representing the type of userdata the given var is +// e.g. players[0] -> "player_t" +// or players[0].powers -> "player_t.powers" +static int lib_userdataType(lua_State *L) +{ + lua_settop(L, 1); // pop everything except arg 1 (in case somebody decided to add more) + luaL_checktype(L, 1, LUA_TUSERDATA); + lua_pushstring(L, GetUserdataUType(L)); + return 1; +} + // M_RANDOM ////////////// @@ -246,6 +329,7 @@ static int lib_pCheckMeleeRange(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_CheckMeleeRange(actor)); @@ -256,6 +340,7 @@ static int lib_pJetbCheckMeleeRange(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_JetbCheckMeleeRange(actor)); @@ -266,6 +351,7 @@ static int lib_pFaceStabCheckMeleeRange(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_FaceStabCheckMeleeRange(actor)); @@ -276,6 +362,7 @@ static int lib_pSkimCheckMeleeRange(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_SkimCheckMeleeRange(actor)); @@ -286,6 +373,7 @@ static int lib_pCheckMissileRange(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_CheckMissileRange(actor)); @@ -296,6 +384,7 @@ static int lib_pNewChaseDir(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); P_NewChaseDir(actor); @@ -309,6 +398,7 @@ static int lib_pLookForPlayers(lua_State *L) boolean allaround = lua_optboolean(L, 3); boolean tracer = lua_optboolean(L, 4); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_LookForPlayers(actor, allaround, tracer, dist)); @@ -325,6 +415,7 @@ static int lib_pSpawnMobj(lua_State *L) fixed_t z = luaL_checkfixed(L, 3); mobjtype_t type = luaL_checkinteger(L, 4); NOHUD + INLEVEL if (type >= NUMMOBJTYPES) return luaL_error(L, "mobj type %d out of range (0 - %d)", type, NUMMOBJTYPES-1); LUA_PushUserdata(L, P_SpawnMobj(x, y, z, type), META_MOBJ); @@ -335,6 +426,7 @@ static int lib_pRemoveMobj(lua_State *L) { mobj_t *th = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!th) return LUA_ErrInvalid(L, "mobj_t"); if (th->player) @@ -350,18 +442,42 @@ static int lib_pIsValidSprite2(lua_State *L) mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); UINT8 spr2 = (UINT8)luaL_checkinteger(L, 2); //HUDSAFE + 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)); mobj_t *dest = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); mobjtype_t type = luaL_checkinteger(L, 3); NOHUD + INLEVEL if (!source || !dest) return LUA_ErrInvalid(L, "mobj_t"); if (type >= NUMMOBJTYPES) @@ -379,6 +495,7 @@ static int lib_pSpawnXYZMissile(lua_State *L) fixed_t y = luaL_checkfixed(L, 5); fixed_t z = luaL_checkfixed(L, 6); NOHUD + INLEVEL if (!source || !dest) return LUA_ErrInvalid(L, "mobj_t"); if (type >= NUMMOBJTYPES) @@ -398,6 +515,7 @@ static int lib_pSpawnPointMissile(lua_State *L) fixed_t y = luaL_checkfixed(L, 7); fixed_t z = luaL_checkfixed(L, 8); NOHUD + INLEVEL if (!source) return LUA_ErrInvalid(L, "mobj_t"); if (type >= NUMMOBJTYPES) @@ -415,6 +533,7 @@ static int lib_pSpawnAlteredDirectionMissile(lua_State *L) fixed_t z = luaL_checkfixed(L, 5); INT32 shiftingAngle = (INT32)luaL_checkinteger(L, 5); NOHUD + INLEVEL if (!source) return LUA_ErrInvalid(L, "mobj_t"); if (type >= NUMMOBJTYPES) @@ -428,6 +547,7 @@ static int lib_pColorTeamMissile(lua_State *L) mobj_t *missile = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); player_t *source = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); NOHUD + INLEVEL if (!missile) return LUA_ErrInvalid(L, "mobj_t"); if (!source) @@ -444,6 +564,7 @@ static int lib_pSPMAngle(lua_State *L) UINT8 allowaim = (UINT8)luaL_optinteger(L, 4, 0); UINT32 flags2 = (UINT32)luaL_optinteger(L, 5, 0); NOHUD + INLEVEL if (!source) return LUA_ErrInvalid(L, "mobj_t"); if (type >= NUMMOBJTYPES) @@ -458,6 +579,7 @@ static int lib_pSpawnPlayerMissile(lua_State *L) mobjtype_t type = luaL_checkinteger(L, 2); UINT32 flags2 = (UINT32)luaL_optinteger(L, 3, 0); NOHUD + INLEVEL if (!source) return LUA_ErrInvalid(L, "mobj_t"); if (type >= NUMMOBJTYPES) @@ -470,6 +592,7 @@ static int lib_pMobjFlip(lua_State *L) { mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); //HUDSAFE + INLEVEL if (!mobj) return LUA_ErrInvalid(L, "mobj_t"); lua_pushinteger(L, P_MobjFlip(mobj)); @@ -480,6 +603,7 @@ static int lib_pGetMobjGravity(lua_State *L) { mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); //HUDSAFE + INLEVEL if (!mobj) return LUA_ErrInvalid(L, "mobj_t"); lua_pushfixed(L, P_GetMobjGravity(mobj)); @@ -502,6 +626,7 @@ static int lib_pFlashPal(lua_State *L) UINT16 type = (UINT16)luaL_checkinteger(L, 2); UINT16 duration = (UINT16)luaL_checkinteger(L, 3); NOHUD + INLEVEL if (!pl) return LUA_ErrInvalid(L, "player_t"); P_FlashPal(pl, type, duration); @@ -512,6 +637,7 @@ static int lib_pGetClosestAxis(lua_State *L) { mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); //HUDSAFE + INLEVEL if (!source) return LUA_ErrInvalid(L, "mobj_t"); LUA_PushUserdata(L, P_GetClosestAxis(source), META_MOBJ); @@ -530,6 +656,7 @@ static int lib_pSpawnParaloop(lua_State *L) statenum_t nstate = luaL_optinteger(L, 8, S_NULL); boolean spawncenter = lua_optboolean(L, 9); NOHUD + INLEVEL if (type >= NUMMOBJTYPES) return luaL_error(L, "mobj type %d out of range (0 - %d)", type, NUMMOBJTYPES-1); if (nstate >= NUMSTATES) @@ -543,6 +670,7 @@ static int lib_pBossTargetPlayer(lua_State *L) mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); boolean closest = lua_optboolean(L, 2); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_BossTargetPlayer(actor, closest)); @@ -553,6 +681,7 @@ static int lib_pSupermanLook4Players(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_SupermanLook4Players(actor)); @@ -564,6 +693,7 @@ static int lib_pSetScale(lua_State *L) mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); fixed_t newscale = luaL_checkfixed(L, 2); NOHUD + INLEVEL if (!mobj) return LUA_ErrInvalid(L, "mobj_t"); if (newscale < FRACUNIT/100) @@ -577,6 +707,7 @@ static int lib_pInsideANonSolidFFloor(lua_State *L) mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR)); //HUDSAFE + INLEVEL if (!mobj) return LUA_ErrInvalid(L, "mobj_t"); if (!rover) @@ -589,6 +720,7 @@ static int lib_pCheckDeathPitCollide(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); //HUDSAFE + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_CheckDeathPitCollide(mo)); @@ -600,6 +732,7 @@ static int lib_pCheckSolidLava(lua_State *L) mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR)); //HUDSAFE + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); if (!rover) @@ -613,6 +746,7 @@ static int lib_pCanRunOnWater(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR)); //HUDSAFE + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); if (!rover) @@ -628,6 +762,7 @@ static int lib_pGetPlayerHeight(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); //HUDSAFE + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); lua_pushfixed(L, P_GetPlayerHeight(player)); @@ -638,6 +773,7 @@ static int lib_pGetPlayerSpinHeight(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); //HUDSAFE + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); lua_pushfixed(L, P_GetPlayerSpinHeight(player)); @@ -648,6 +784,7 @@ static int lib_pGetPlayerControlDirection(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); //HUDSAFE + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); lua_pushinteger(L, P_GetPlayerControlDirection(player)); @@ -659,6 +796,7 @@ static int lib_pAddPlayerScore(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); UINT32 amount = (UINT32)luaL_checkinteger(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_AddPlayerScore(player, amount); @@ -670,16 +808,29 @@ static int lib_pStealPlayerScore(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); UINT32 amount = (UINT32)luaL_checkinteger(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_StealPlayerScore(player, amount); 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)); //HUDSAFE + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); lua_pushboolean(L, P_PlayerInPain(player)); @@ -691,6 +842,7 @@ static int lib_pDoPlayerPain(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); mobj_t *source = NULL, *inflictor = NULL; NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) @@ -705,6 +857,7 @@ static int lib_pResetPlayer(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_ResetPlayer(player); @@ -715,6 +868,7 @@ static int lib_pIsObjectInGoop(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); //HUDSAFE + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_IsObjectInGoop(mo)); @@ -725,6 +879,7 @@ static int lib_pIsObjectOnGround(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); //HUDSAFE + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_IsObjectOnGround(mo)); @@ -735,6 +890,7 @@ static int lib_pInSpaceSector(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); //HUDSAFE + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_InSpaceSector(mo)); @@ -745,6 +901,7 @@ static int lib_pInQuicksand(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); //HUDSAFE + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_InQuicksand(mo)); @@ -757,6 +914,7 @@ static int lib_pSetObjectMomZ(lua_State *L) fixed_t value = luaL_checkfixed(L, 2); boolean relative = lua_optboolean(L, 3); NOHUD + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); P_SetObjectMomZ(mo, value, relative); @@ -767,6 +925,7 @@ static int lib_pRestoreMusic(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_RestoreMusic(player); @@ -777,6 +936,7 @@ static int lib_pSpawnShieldOrb(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_SpawnShieldOrb(player); @@ -787,6 +947,7 @@ static int lib_pSpawnGhostMobj(lua_State *L) { mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!mobj) return LUA_ErrInvalid(L, "mobj_t"); LUA_PushUserdata(L, P_SpawnGhostMobj(mobj), META_MOBJ); @@ -798,6 +959,7 @@ static int lib_pGivePlayerRings(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); INT32 num_rings = (INT32)luaL_checkinteger(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_GivePlayerRings(player, num_rings); @@ -809,6 +971,7 @@ static int lib_pGivePlayerLives(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); INT32 numlives = (INT32)luaL_checkinteger(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_GivePlayerLives(player, numlives); @@ -819,6 +982,7 @@ static int lib_pResetScore(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_ResetScore(player); @@ -829,6 +993,7 @@ static int lib_pDoJumpShield(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_DoJumpShield(player); @@ -839,6 +1004,7 @@ static int lib_pDoBubbleBounce(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_DoBubbleBounce(player); @@ -849,6 +1015,7 @@ static int lib_pBlackOw(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_BlackOw(player); @@ -860,6 +1027,7 @@ static int lib_pElementalFire(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); boolean cropcircle = lua_optboolean(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_ElementalFire(player, cropcircle); @@ -870,6 +1038,7 @@ static int lib_pDoPlayerExit(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_DoPlayerExit(player); @@ -882,6 +1051,7 @@ static int lib_pInstaThrust(lua_State *L) angle_t angle = luaL_checkangle(L, 2); fixed_t move = luaL_checkfixed(L, 3); NOHUD + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); P_InstaThrust(mo, angle, move); @@ -918,10 +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; } @@ -931,6 +1103,7 @@ static int lib_pNukeEnemies(lua_State *L) mobj_t *source = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); fixed_t radius = luaL_checkfixed(L, 3); NOHUD + INLEVEL if (!inflictor || !source) return LUA_ErrInvalid(L, "mobj_t"); P_NukeEnemies(inflictor, source, radius); @@ -942,6 +1115,7 @@ static int lib_pHomingAttack(lua_State *L) mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); mobj_t *enemy = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); NOHUD + INLEVEL if (!source || !enemy) return LUA_ErrInvalid(L, "mobj_t"); P_HomingAttack(source, enemy); @@ -952,6 +1126,7 @@ static int lib_pSuperReady(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); //HUDSAFE + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); lua_pushboolean(L, P_SuperReady(player)); @@ -963,6 +1138,7 @@ static int lib_pDoJump(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); boolean soundandstate = (boolean)lua_opttrueboolean(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_DoJump(player, soundandstate); @@ -973,6 +1149,7 @@ static int lib_pSpawnThokMobj(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_SpawnThokMobj(player); @@ -984,6 +1161,7 @@ static int lib_pSpawnSpinMobj(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); mobjtype_t type = luaL_checkinteger(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); if (type >= NUMMOBJTYPES) @@ -998,6 +1176,7 @@ static int lib_pTelekinesis(lua_State *L) fixed_t thrust = luaL_checkfixed(L, 2); fixed_t range = luaL_checkfixed(L, 3); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_Telekinesis(player, thrust, range); @@ -1014,6 +1193,7 @@ static int lib_pCheckPosition(lua_State *L) fixed_t x = luaL_checkfixed(L, 2); fixed_t y = luaL_checkfixed(L, 3); NOHUD + INLEVEL if (!thing) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_CheckPosition(thing, x, y)); @@ -1030,6 +1210,7 @@ static int lib_pTryMove(lua_State *L) fixed_t y = luaL_checkfixed(L, 3); boolean allowdropoff = lua_optboolean(L, 4); NOHUD + INLEVEL if (!thing) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_TryMove(thing, x, y, allowdropoff)); @@ -1044,6 +1225,7 @@ static int lib_pMove(lua_State *L) mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); fixed_t speed = luaL_checkfixed(L, 2); NOHUD + INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_Move(actor, speed)); @@ -1060,6 +1242,7 @@ static int lib_pTeleportMove(lua_State *L) fixed_t y = luaL_checkfixed(L, 3); fixed_t z = luaL_checkfixed(L, 4); NOHUD + INLEVEL if (!thing) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_TeleportMove(thing, x, y, z)); @@ -1072,6 +1255,7 @@ static int lib_pSlideMove(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); P_SlideMove(mo); @@ -1082,6 +1266,7 @@ static int lib_pBounceMove(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); P_BounceMove(mo); @@ -1093,6 +1278,7 @@ static int lib_pCheckSight(lua_State *L) mobj_t *t1 = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); mobj_t *t2 = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); //HUDSAFE? + INLEVEL if (!t1 || !t2) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_CheckSight(t1, t2)); @@ -1107,6 +1293,7 @@ static int lib_pCheckHoopPosition(lua_State *L) fixed_t z = luaL_checkfixed(L, 4); fixed_t radius = luaL_checkfixed(L, 5); NOHUD + INLEVEL if (!hoopthing) return LUA_ErrInvalid(L, "mobj_t"); P_CheckHoopPosition(hoopthing, x, y, z, radius); @@ -1119,6 +1306,7 @@ static int lib_pRadiusAttack(lua_State *L) mobj_t *source = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); fixed_t damagedist = luaL_checkfixed(L, 3); NOHUD + INLEVEL if (!spot || !source) return LUA_ErrInvalid(L, "mobj_t"); P_RadiusAttack(spot, source, damagedist); @@ -1132,6 +1320,7 @@ static int lib_pFloorzAtPos(lua_State *L) fixed_t z = luaL_checkfixed(L, 3); fixed_t height = luaL_checkfixed(L, 4); //HUDSAFE + INLEVEL lua_pushfixed(L, P_FloorzAtPos(x, y, z, height)); return 1; } @@ -1141,6 +1330,7 @@ static int lib_pDoSpring(lua_State *L) mobj_t *spring = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); mobj_t *object = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); NOHUD + INLEVEL if (!spring || !object) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_DoSpring(spring, object)); @@ -1154,6 +1344,7 @@ static int lib_pRemoveShield(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_RemoveShield(player); @@ -1166,6 +1357,7 @@ static int lib_pDamageMobj(lua_State *L) INT32 damage; UINT8 damagetype; NOHUD + INLEVEL if (!target) return LUA_ErrInvalid(L, "mobj_t"); if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) @@ -1183,6 +1375,7 @@ static int lib_pKillMobj(lua_State *L) mobj_t *target = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)), *inflictor = NULL, *source = NULL; UINT8 damagetype; NOHUD + INLEVEL if (!target) return LUA_ErrInvalid(L, "mobj_t"); if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) @@ -1199,6 +1392,7 @@ static int lib_pPlayerRingBurst(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); INT32 num_rings = (INT32)luaL_optinteger(L, 2, -1); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); if (num_rings == -1) @@ -1211,6 +1405,7 @@ static int lib_pPlayerWeaponPanelBurst(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_PlayerWeaponPanelBurst(player); @@ -1221,6 +1416,7 @@ static int lib_pPlayerWeaponAmmoBurst(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_PlayerWeaponAmmoBurst(player); @@ -1231,6 +1427,7 @@ static int lib_pPlayerWeaponPanelOrAmmoBurst(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_PlayerWeaponPanelOrAmmoBurst(player); @@ -1242,6 +1439,7 @@ static int lib_pPlayerEmeraldBurst(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); boolean toss = lua_optboolean(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_PlayerEmeraldBurst(player, toss); @@ -1253,6 +1451,7 @@ static int lib_pPlayerFlagBurst(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); boolean toss = lua_optboolean(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_PlayerFlagBurst(player, toss); @@ -1264,6 +1463,7 @@ static int lib_pPlayRinglossSound(lua_State *L) mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); player_t *player = NULL; NOHUD + INLEVEL if (!source) return LUA_ErrInvalid(L, "mobj_t"); if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) @@ -1282,6 +1482,7 @@ static int lib_pPlayDeathSound(lua_State *L) mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); player_t *player = NULL; NOHUD + INLEVEL if (!source) return LUA_ErrInvalid(L, "mobj_t"); if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) @@ -1300,6 +1501,7 @@ static int lib_pPlayVictorySound(lua_State *L) mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); player_t *player = NULL; NOHUD + INLEVEL if (!source) return LUA_ErrInvalid(L, "mobj_t"); if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) @@ -1317,6 +1519,7 @@ static int lib_pPlayLivesJingle(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_PlayLivesJingle(player); @@ -1328,6 +1531,7 @@ static int lib_pCanPickupItem(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); boolean weapon = lua_optboolean(L, 2); //HUDSAFE + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); lua_pushboolean(L, P_CanPickupItem(player, weapon)); @@ -1338,6 +1542,7 @@ static int lib_pDoNightsScore(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_DoNightsScore(player); @@ -1348,6 +1553,7 @@ static int lib_pDoMatchSuper(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_DoMatchSuper(player); @@ -1363,6 +1569,7 @@ static int lib_pThrust(lua_State *L) angle_t angle = luaL_checkangle(L, 2); fixed_t move = luaL_checkfixed(L, 3); NOHUD + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); P_Thrust(mo, angle, move); @@ -1374,6 +1581,7 @@ static int lib_pSetMobjStateNF(lua_State *L) mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); statenum_t state = luaL_checkinteger(L, 2); NOHUD + INLEVEL if (!mobj) return LUA_ErrInvalid(L, "mobj_t"); if (state >= NUMSTATES) @@ -1389,6 +1597,7 @@ static int lib_pDoSuperTransformation(lua_State *L) player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); boolean giverings = lua_optboolean(L, 2); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_DoSuperTransformation(player, giverings); @@ -1399,6 +1608,7 @@ static int lib_pExplodeMissile(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); P_ExplodeMissile(mo); @@ -1411,6 +1621,7 @@ static int lib_pPlayerTouchingSectorSpecial(lua_State *L) INT32 section = (INT32)luaL_checkinteger(L, 2); INT32 number = (INT32)luaL_checkinteger(L, 3); //HUDSAFE + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); LUA_PushUserdata(L, P_PlayerTouchingSectorSpecial(player, section, number), META_SECTOR); @@ -1423,6 +1634,7 @@ static int lib_pFindSpecialLineFromTag(lua_State *L) INT16 line = (INT16)luaL_checkinteger(L, 2); INT32 start = (INT32)luaL_optinteger(L, 3, -1); NOHUD + INLEVEL lua_pushinteger(L, P_FindSpecialLineFromTag(special, line, start)); return 1; } @@ -1432,6 +1644,7 @@ static int lib_pSwitchWeather(lua_State *L) INT32 weathernum = (INT32)luaL_checkinteger(L, 1); player_t *user = NULL; NOHUD + INLEVEL if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) // if a player, setup weather for only the player, otherwise setup weather for all players user = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); if (!user) // global @@ -1447,6 +1660,7 @@ static int lib_pLinedefExecute(lua_State *L) mobj_t *actor = NULL; sector_t *caller = NULL; NOHUD + INLEVEL if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) actor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) @@ -1459,6 +1673,7 @@ static int lib_pSpawnLightningFlash(lua_State *L) { sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR)); NOHUD + INLEVEL if (!sector) return LUA_ErrInvalid(L, "sector_t"); P_SpawnLightningFlash(sector); @@ -1471,6 +1686,7 @@ static int lib_pFadeLight(lua_State *L) INT32 destvalue = (INT32)luaL_checkinteger(L, 2); INT32 speed = (INT32)luaL_checkinteger(L, 3); NOHUD + INLEVEL P_FadeLight(tag, destvalue, speed); return 0; } @@ -1479,6 +1695,7 @@ static int lib_pThingOnSpecial3DFloor(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!mo) return LUA_ErrInvalid(L, "mobj_t"); LUA_PushUserdata(L, P_ThingOnSpecial3DFloor(mo), META_SECTOR); @@ -1489,6 +1706,7 @@ static int lib_pIsFlagAtBase(lua_State *L) { mobjtype_t flag = luaL_checkinteger(L, 1); NOHUD + INLEVEL if (flag >= NUMMOBJTYPES) return luaL_error(L, "mobj type %d out of range (0 - %d)", flag, NUMMOBJTYPES-1); lua_pushboolean(L, P_IsFlagAtBase(flag)); @@ -1500,6 +1718,7 @@ static int lib_pSetupLevelSky(lua_State *L) INT32 skynum = (INT32)luaL_checkinteger(L, 1); player_t *user = NULL; NOHUD + INLEVEL if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) // if a player, setup sky for only the player, otherwise setup sky for all players user = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); if (!user) // global @@ -1518,6 +1737,7 @@ static int lib_pSetSkyboxMobj(lua_State *L) int w = 0; NOHUD + INLEVEL if (!lua_isnil(L,1)) // nil leaves mo as NULL to remove the skybox rendering. { mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); // otherwise it is a skybox mobj. @@ -1564,6 +1784,7 @@ static int lib_pStartQuake(lua_State *L) static mappoint_t q_epicenter = {0,0,0}; NOHUD + INLEVEL // While technically we don't support epicenter and radius, // we get their values anyway if they exist. @@ -1625,6 +1846,7 @@ static int lib_evCrumbleChain(lua_State *L) sector_t *sec = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR)); ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR)); NOHUD + INLEVEL if (!sec) return LUA_ErrInvalid(L, "sector_t"); if (!rover) @@ -1633,6 +1855,33 @@ static int lib_evCrumbleChain(lua_State *L) return 0; } +static int lib_evStartCrumble(lua_State *L) +{ + sector_t *sec = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR)); + ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR)); + boolean floating = lua_optboolean(L, 3); + player_t *player = NULL; + fixed_t origalpha; + boolean crumblereturn = lua_optboolean(L, 6); + NOHUD + if (!sec) + return LUA_ErrInvalid(L, "sector_t"); + if (!rover) + return LUA_ErrInvalid(L, "ffloor_t"); + if (!lua_isnone(L, 4) && lua_isuserdata(L, 4)) + { + player = *((player_t **)luaL_checkudata(L, 4, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + if (!lua_isnone(L,5)) + origalpha = luaL_checkfixed(L, 5); + else + origalpha = rover->alpha; + lua_pushboolean(L, EV_StartCrumble(sec, rover, floating, player, origalpha, crumblereturn) != 0); + return 0; +} + // R_DEFS //////////// @@ -1681,6 +1930,7 @@ static int lib_rPointInSubsector(lua_State *L) fixed_t x = luaL_checkfixed(L, 1); fixed_t y = luaL_checkfixed(L, 2); //HUDSAFE + INLEVEL LUA_PushUserdata(L, R_PointInSubsector(x, y), META_SUBSECTOR); return 1; } @@ -1715,6 +1965,7 @@ static int lib_rSetPlayerSkin(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); NOHUD + INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); if (lua_isnoneornil(L, 2)) @@ -1899,6 +2150,7 @@ static int lib_sOriginPlaying(lua_State *L) { void *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); NOHUD + INLEVEL if (!origin) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, S_OriginPlaying(origin)); @@ -1920,6 +2172,7 @@ static int lib_sSoundPlaying(lua_State *L) void *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); sfxenum_t id = luaL_checkinteger(L, 2); NOHUD + INLEVEL if (!origin) return LUA_ErrInvalid(L, "mobj_t"); if (id >= NUMSFX) @@ -1935,6 +2188,7 @@ static int lib_gBuildMapName(lua_State *L) { INT32 map = luaL_optinteger(L, 1, gamemap); //HUDSAFE + INLEVEL lua_pushstring(L, G_BuildMapName(map)); return 1; } @@ -1943,6 +2197,7 @@ static int lib_gDoReborn(lua_State *L) { INT32 playernum = luaL_checkinteger(L, 1); NOHUD + INLEVEL if (playernum >= MAXPLAYERS) return luaL_error(L, "playernum %d out of range (0 - %d)", playernum, MAXPLAYERS-1); G_DoReborn(playernum); @@ -1953,6 +2208,7 @@ static int lib_gExitLevel(lua_State *L) { int n = lua_gettop(L); // Num arguments NOHUD + INLEVEL // LUA EXTENSION: Custom exit like support // Supported: @@ -1979,6 +2235,7 @@ static int lib_gIsSpecialStage(lua_State *L) { INT32 mapnum = luaL_optinteger(L, 1, gamemap); //HUDSAFE + INLEVEL lua_pushboolean(L, G_IsSpecialStage(mapnum)); return 1; } @@ -1986,6 +2243,7 @@ static int lib_gIsSpecialStage(lua_State *L) static int lib_gGametypeUsesLives(lua_State *L) { //HUDSAFE + INLEVEL lua_pushboolean(L, G_GametypeUsesLives()); return 1; } @@ -1993,6 +2251,7 @@ static int lib_gGametypeUsesLives(lua_State *L) static int lib_gGametypeHasTeams(lua_State *L) { //HUDSAFE + INLEVEL lua_pushboolean(L, G_GametypeHasTeams()); return 1; } @@ -2000,6 +2259,7 @@ static int lib_gGametypeHasTeams(lua_State *L) static int lib_gGametypeHasSpectators(lua_State *L) { //HUDSAFE + INLEVEL lua_pushboolean(L, G_GametypeHasSpectators()); return 1; } @@ -2007,6 +2267,7 @@ static int lib_gGametypeHasSpectators(lua_State *L) static int lib_gRingSlingerGametype(lua_State *L) { //HUDSAFE + INLEVEL lua_pushboolean(L, G_RingSlingerGametype()); return 1; } @@ -2014,6 +2275,7 @@ static int lib_gRingSlingerGametype(lua_State *L) static int lib_gPlatformGametype(lua_State *L) { //HUDSAFE + INLEVEL lua_pushboolean(L, G_PlatformGametype()); return 1; } @@ -2021,6 +2283,7 @@ static int lib_gPlatformGametype(lua_State *L) static int lib_gTagGametype(lua_State *L) { //HUDSAFE + INLEVEL lua_pushboolean(L, G_TagGametype()); return 1; } @@ -2068,6 +2331,7 @@ static int lib_gTicsToMilliseconds(lua_State *L) static luaL_Reg lib[] = { {"print", lib_print}, + {"userdataType", lib_userdataType}, // m_random {"P_RandomFixed",lib_pRandomFixed}, @@ -2097,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}, @@ -2124,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}, @@ -2203,6 +2469,7 @@ static luaL_Reg lib[] = { {"P_SetSkyboxMobj",lib_pSetSkyboxMobj}, {"P_StartQuake",lib_pStartQuake}, {"EV_CrumbleChain",lib_evCrumbleChain}, + {"EV_StartCrumble",lib_evStartCrumble}, // r_defs {"R_PointToAngle",lib_rPointToAngle}, diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c index 33f350d69..d90ef4d67 100644 --- a/src/lua_blockmaplib.c +++ b/src/lua_blockmaplib.c @@ -186,6 +186,9 @@ static int lib_searchBlockmap(lua_State *L) UINT8 funcret = 0; blockmap_func searchFunc; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); + lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2] luaL_checktype(L, 1, LUA_TFUNCTION); diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index 28cc91762..322fecb64 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -22,7 +22,10 @@ #include "lua_libs.h" #include "lua_hud.h" // hud_running errors -#define NOHUD if (hud_running) return luaL_error(L, "HUD rendering code should not call this function!"); +#define NOHUD if (hud_running)\ +return luaL_error(L, "HUD rendering code should not call this function!"); +#define INLEVEL if (gamestate != GS_LEVEL)\ +return luaL_error(L, "This function can only be used in a level!"); static const char *cvname = NULL; @@ -412,6 +415,7 @@ static int lib_consPrintf(lua_State *L) if (n < 2) return luaL_error(L, "CONS_Printf requires at least two arguments: player and text."); //HUDSAFE + INLEVEL plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); if (!plr) return LUA_ErrInvalid(L, "player_t"); diff --git a/src/lua_hook.h b/src/lua_hook.h index 7192a2979..88867db2b 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -45,6 +45,7 @@ enum hook { hook_PlayerSpawn, hook_ShieldSpawn, hook_ShieldSpecial, + hook_MobjMoveBlocked, hook_MAX // last hook }; @@ -81,5 +82,6 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 #define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer #define LUAh_ShieldSpawn(player) LUAh_PlayerHook(player, hook_ShieldSpawn) // Hook for P_SpawnShieldOrb #define LUAh_ShieldSpecial(player) LUAh_PlayerHook(player, hook_ShieldSpecial) // Hook for shield abilities +#define LUAh_MobjMoveBlocked(mo) LUAh_MobjHook(mo, hook_MobjMoveBlocked) // Hook for P_XYMovement (when movement is blocked) #endif diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index f7ea25224..dadc1861a 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -56,6 +56,7 @@ const char *const hookNames[hook_MAX+1] = { "PlayerSpawn", "ShieldSpawn", "ShieldSpecial", + "MobjMoveBlocked", NULL }; @@ -126,6 +127,7 @@ static int lib_addHook(lua_State *L) case hook_BossDeath: case hook_MobjRemoved: case hook_HurtMsg: + case hook_MobjMoveBlocked: hook.s.mt = MT_NULL; if (lua_isnumber(L, 2)) hook.s.mt = lua_tonumber(L, 2); @@ -184,6 +186,7 @@ static int lib_addHook(lua_State *L) case hook_MobjDeath: case hook_BossDeath: case hook_MobjRemoved: + case hook_MobjMoveBlocked: lastp = &mobjhooks[hook.s.mt]; break; case hook_JumpSpecial: diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 9f6d3e7fa..f1bfcb8f1 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -264,12 +264,19 @@ static const char *const bbox_opt[] = { static const char *const array_opt[] ={"iterate",NULL}; static const char *const valid_opt[] ={"valid",NULL}; +/////////////////////////////////// +// sector list iterate functions // +/////////////////////////////////// + // iterates through a sector's thinglist! static int lib_iterateSectorThinglist(lua_State *L) { mobj_t *state = NULL; mobj_t *thing = NULL; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); + if (lua_gettop(L) < 2) return luaL_error(L, "Don't call sector.thinglist() directly, use it as 'for rover in sector.thinglist do end'."); @@ -303,6 +310,9 @@ static int lib_iterateSectorFFloors(lua_State *L) ffloor_t *state = NULL; ffloor_t *ffloor = NULL; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); + if (lua_gettop(L) < 2) return luaL_error(L, "Don't call sector.ffloors() directly, use it as 'for rover in sector.ffloors do end'."); @@ -338,6 +348,10 @@ static int sector_iterate(lua_State *L) return 3; } +//////////////////// +// sector.lines[] // +//////////////////// + // sector.lines, i -> sector.lines[i] // sector.lines.valid, for validity checking static int sectorlines_get(lua_State *L) @@ -399,6 +413,10 @@ static int sectorlines_num(lua_State *L) return 1; } +////////////// +// sector_t // +////////////// + static int sector_get(lua_State *L) { sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR)); @@ -540,6 +558,10 @@ static int sector_num(lua_State *L) return 1; } +///////////////// +// subsector_t // +///////////////// + static int subsector_get(lua_State *L) { subsector_t *subsector = *((subsector_t **)luaL_checkudata(L, 1, META_SUBSECTOR)); @@ -579,6 +601,10 @@ static int subsector_num(lua_State *L) return 1; } +//////////// +// line_t // +//////////// + static int line_get(lua_State *L) { line_t *line = *((line_t **)luaL_checkudata(L, 1, META_LINE)); @@ -676,6 +702,10 @@ static int line_num(lua_State *L) return 1; } +//////////////////// +// line.sidenum[] // +//////////////////// + static int sidenum_get(lua_State *L) { UINT16 *sidenum = *((UINT16 **)luaL_checkudata(L, 1, META_SIDENUM)); @@ -704,6 +734,10 @@ static int sidenum_get(lua_State *L) return 1; } +//////////// +// side_t // +//////////// + static int side_get(lua_State *L) { side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE)); @@ -805,6 +839,10 @@ static int side_num(lua_State *L) return 1; } +////////////// +// vertex_t // +////////////// + static int vertex_get(lua_State *L) { vertex_t *vertex = *((vertex_t **)luaL_checkudata(L, 1, META_VERTEX)); @@ -845,6 +883,11 @@ static int vertex_num(lua_State *L) } #ifdef HAVE_LUA_SEGS + +/////////// +// seg_t // +/////////// + static int seg_get(lua_State *L) { seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG)); @@ -902,6 +945,10 @@ static int seg_num(lua_State *L) return 1; } +//////////// +// node_t // +//////////// + static int node_get(lua_State *L) { node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE)); @@ -949,6 +996,11 @@ static int node_num(lua_State *L) lua_pushinteger(L, node-nodes); return 1; } + +/////////////// +// node.bbox // +/////////////// + /* // node.bbox[i][j]: i = 0 or 1, j = 0 1 2 or 3 // NOTE: 2D arrays are NOT double pointers, @@ -1028,6 +1080,10 @@ static int nodebbox_call(lua_State *L) return 1; } +///////////////////// +// node.children[] // +///////////////////// + // node.children[i]: i = 0 or 1 static int nodechildren_get(lua_State *L) { @@ -1063,6 +1119,10 @@ static int nodechildren_get(lua_State *L) } #endif +////////// +// bbox // +////////// + // bounding box (aka fixed_t array with four elements) // NOTE: may be useful for polyobjects or other things later static int bbox_get(lua_State *L) @@ -1100,9 +1160,15 @@ static int bbox_get(lua_State *L) return 1; } +/////////////// +// sectors[] // +/////////////// + static int lib_iterateSectors(lua_State *L) { size_t i = 0; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); if (lua_gettop(L) < 2) return luaL_error(L, "Don't call sectors.iterate() directly, use it as 'for sector in sectors.iterate do end'."); lua_settop(L, 2); @@ -1120,6 +1186,8 @@ static int lib_iterateSectors(lua_State *L) static int lib_getSector(lua_State *L) { int field; + if (gamestate != GS_LEVEL) + return luaL_error(L, "You cannot access this outside of a level!"); lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1146,9 +1214,15 @@ static int lib_numsectors(lua_State *L) return 1; } +////////////////// +// subsectors[] // +////////////////// + static int lib_iterateSubsectors(lua_State *L) { size_t i = 0; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); if (lua_gettop(L) < 2) return luaL_error(L, "Don't call subsectors.iterate() directly, use it as 'for subsector in subsectors.iterate do end'."); lua_settop(L, 2); @@ -1166,6 +1240,8 @@ static int lib_iterateSubsectors(lua_State *L) static int lib_getSubsector(lua_State *L) { int field; + if (gamestate != GS_LEVEL) + return luaL_error(L, "You cannot access this outside of a level!"); lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1192,9 +1268,15 @@ static int lib_numsubsectors(lua_State *L) return 1; } +///////////// +// lines[] // +///////////// + static int lib_iterateLines(lua_State *L) { size_t i = 0; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); if (lua_gettop(L) < 2) return luaL_error(L, "Don't call lines.iterate() directly, use it as 'for line in lines.iterate do end'."); lua_settop(L, 2); @@ -1212,6 +1294,8 @@ static int lib_iterateLines(lua_State *L) static int lib_getLine(lua_State *L) { int field; + if (gamestate != GS_LEVEL) + return luaL_error(L, "You cannot access this outside of a level!"); lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1238,9 +1322,15 @@ static int lib_numlines(lua_State *L) return 1; } +///////////// +// sides[] // +///////////// + static int lib_iterateSides(lua_State *L) { size_t i = 0; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); if (lua_gettop(L) < 2) return luaL_error(L, "Don't call sides.iterate() directly, use it as 'for side in sides.iterate do end'."); lua_settop(L, 2); @@ -1258,6 +1348,8 @@ static int lib_iterateSides(lua_State *L) static int lib_getSide(lua_State *L) { int field; + if (gamestate != GS_LEVEL) + return luaL_error(L, "You cannot access this outside of a level!"); lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1284,9 +1376,15 @@ static int lib_numsides(lua_State *L) return 1; } +//////////////// +// vertexes[] // +//////////////// + static int lib_iterateVertexes(lua_State *L) { size_t i = 0; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); if (lua_gettop(L) < 2) return luaL_error(L, "Don't call vertexes.iterate() directly, use it as 'for vertex in vertexes.iterate do end'."); lua_settop(L, 2); @@ -1304,6 +1402,8 @@ static int lib_iterateVertexes(lua_State *L) static int lib_getVertex(lua_State *L) { int field; + if (gamestate != GS_LEVEL) + return luaL_error(L, "You cannot access this outside of a level!"); lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1331,9 +1431,16 @@ static int lib_numvertexes(lua_State *L) } #ifdef HAVE_LUA_SEGS + +//////////// +// segs[] // +//////////// + static int lib_iterateSegs(lua_State *L) { size_t i = 0; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); if (lua_gettop(L) < 2) return luaL_error(L, "Don't call segs.iterate() directly, use it as 'for seg in segs.iterate do end'."); lua_settop(L, 2); @@ -1351,6 +1458,8 @@ static int lib_iterateSegs(lua_State *L) static int lib_getSeg(lua_State *L) { int field; + if (gamestate != GS_LEVEL) + return luaL_error(L, "You cannot access this outside of a level!"); lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1377,9 +1486,15 @@ static int lib_numsegs(lua_State *L) return 1; } +///////////// +// nodes[] // +///////////// + static int lib_iterateNodes(lua_State *L) { size_t i = 0; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); if (lua_gettop(L) < 2) return luaL_error(L, "Don't call nodes.iterate() directly, use it as 'for node in nodes.iterate do end'."); lua_settop(L, 2); @@ -1397,6 +1512,8 @@ static int lib_iterateNodes(lua_State *L) static int lib_getNode(lua_State *L) { int field; + if (gamestate != GS_LEVEL) + return luaL_error(L, "You cannot access this outside of a level!"); lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1424,6 +1541,10 @@ static int lib_numnodes(lua_State *L) } #endif +////////////// +// ffloor_t // +////////////// + static int ffloor_get(lua_State *L) { ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR)); @@ -1567,6 +1688,10 @@ static int ffloor_set(lua_State *L) return 0; } +///////////////////// +// mapheaderinfo[] // +///////////////////// + static int lib_getMapheaderinfo(lua_State *L) { // i -> mapheaderinfo[i-1] @@ -1599,6 +1724,10 @@ static int lib_nummapheaders(lua_State *L) return 1; } +///////////////// +// mapheader_t // +///////////////// + static int mapheaderinfo_get(lua_State *L) { mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER)); diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 9ebc38a61..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); @@ -751,6 +752,8 @@ static int mapthing_set(lua_State *L) static int lib_iterateMapthings(lua_State *L) { size_t i = 0; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); if (lua_gettop(L) < 2) return luaL_error(L, "Don't call mapthings.iterate() directly, use it as 'for mapthing in mapthings.iterate do end'."); lua_settop(L, 2); @@ -768,6 +771,8 @@ static int lib_iterateMapthings(lua_State *L) static int lib_getMapthing(lua_State *L) { int field; + if (gamestate != GS_LEVEL) + return luaL_error(L, "You cannot access this outside of a level!"); lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index cc8b67e88..19ede443b 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -25,6 +25,8 @@ static int lib_iteratePlayers(lua_State *L) { INT32 i = -1; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); if (lua_gettop(L) < 2) { //return luaL_error(L, "Don't call players.iterate() directly, use it as 'for player in players.iterate do end'."); @@ -51,6 +53,8 @@ static int lib_getPlayer(lua_State *L) { const char *field; // i -> players[i] + if (gamestate != GS_LEVEL) + return luaL_error(L, "You cannot access this outside of a level!"); if (lua_type(L, 2) == LUA_TNUMBER) { lua_Integer i = luaL_checkinteger(L, 2); @@ -192,8 +196,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 +461,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/lua_thinkerlib.c b/src/lua_thinkerlib.c index d5251425a..b8cf1baec 100644 --- a/src/lua_thinkerlib.c +++ b/src/lua_thinkerlib.c @@ -29,6 +29,9 @@ static int lib_iterateThinkers(lua_State *L) actionf_p1 searchFunc; const char *searchMeta; + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); + lua_settop(L, 2); lua_remove(L, 1); // remove state now. @@ -84,6 +87,8 @@ static int lib_iterateThinkers(lua_State *L) static int lib_startIterate(lua_State *L) { + if (gamestate != GS_LEVEL) + return luaL_error(L, "This function can only be used in a level!"); luaL_checkoption(L, 1, iter_opt[0], iter_opt); lua_pushcfunction(L, lib_iterateThinkers); lua_pushvalue(L, 1); 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) ? "