diff --git a/assets/CMakeLists.txt b/assets/CMakeLists.txt index 89be796ad..e5cfa5ca2 100644 --- a/assets/CMakeLists.txt +++ b/assets/CMakeLists.txt @@ -17,7 +17,7 @@ set(SRB2_ASSET_HASHED gfx.pk3;\ textures.pk3;\ chars.pk3;\ -maps.wad;\ +maps.pk3;\ patch.pk3" CACHE STRING "Asset filenames to apply MD5 checks. No spaces between entries!" ) diff --git a/src/Makefile b/src/Makefile index cf9e38bf3..16cb8912d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -60,6 +60,7 @@ # Compile with GCC 4.6x version, add 'GCC46=1' # Compile a profile version, add 'PROFILEMODE=1' # Compile a debug version, add 'DEBUGMODE=1' +# Compile for the testers group (they don't get to play unless we're watching *wink*), add 'TESTERS=1' # Compile with extra warnings, add 'WARNINGMODE=1' # Compile without NASM's tmap.nas, add 'NOASM=1' # Compile without 3D hardware support, add 'NOHW=1' @@ -434,6 +435,10 @@ else endif CFLAGS+=-g $(OPTS) $(ARCHOPTS) $(WINDRESFLAGS) +ifdef TESTERS + OPTS+=-DTESTERS +endif + ifdef YASM ifdef STABS NASMOPTS?= -g stabs @@ -489,6 +494,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/y_inter.o \ $(OBJDIR)/st_stuff.o \ $(OBJDIR)/k_kart.o \ + $(OBJDIR)/k_pwrlv.o \ $(OBJDIR)/k_waypoint.o\ $(OBJDIR)/k_pathfind.o\ $(OBJDIR)/k_bheap.o \ diff --git a/src/command.c b/src/command.c index 6d9c86d3e..625065120 100644 --- a/src/command.c +++ b/src/command.c @@ -68,10 +68,14 @@ CV_PossibleValue_t CV_YesNo[] = {{0, "No"}, {1, "Yes"}, {0, NULL}}; CV_PossibleValue_t CV_Unsigned[] = {{0, "MIN"}, {999999999, "MAX"}, {0, NULL}}; CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}}; -//SRB2kart +// SRB2kart CV_PossibleValue_t kartspeed_cons_t[] = { - {0, "Easy"}, {1, "Normal"}, {2, "Hard"}, - {0, NULL}}; + {KARTSPEED_AUTO, "Auto"}, + {KARTSPEED_EASY, "Easy"}, + {KARTSPEED_NORMAL, "Normal"}, + {KARTSPEED_HARD, "Hard"}, + {0, NULL} +}; // Filter consvars by EXECVERSION // First implementation is 2 (1.0.2), so earlier configs default at 1 (1.0.0) @@ -1769,7 +1773,7 @@ void CV_AddValue(consvar_t *var, INT32 increment) { newvalue = var->value + 1; if (newvalue > maxspeed) - newvalue = 0; + newvalue = -1; var->value = newvalue; var->string = var->PossibleValue[var->value].strvalue; var->func(); @@ -1778,7 +1782,7 @@ void CV_AddValue(consvar_t *var, INT32 increment) else if (increment < 0) // Going down! { newvalue = var->value - 1; - if (newvalue < 0) + if (newvalue < -1) newvalue = maxspeed; var->value = newvalue; var->string = var->PossibleValue[var->value].strvalue; diff --git a/src/command.h b/src/command.h index 6b5d513ef..33d232bcb 100644 --- a/src/command.h +++ b/src/command.h @@ -131,6 +131,10 @@ extern CV_PossibleValue_t CV_Unsigned[]; extern CV_PossibleValue_t CV_Natural[]; // SRB2kart +#define KARTSPEED_AUTO -1 +#define KARTSPEED_EASY 0 +#define KARTSPEED_NORMAL 1 +#define KARTSPEED_HARD 2 extern CV_PossibleValue_t kartspeed_cons_t[]; extern consvar_t cv_execversion; diff --git a/src/config.h.in b/src/config.h.in index 4bf84ff1d..964207e34 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -15,7 +15,7 @@ #define ASSET_HASH_GFX_PK3 "${SRB2_ASSET_gfx.pk3_HASH}" #define ASSET_HASH_TEXTURES_PK3 "${SRB2_ASSET_textures.pk3_HASH}" #define ASSET_HASH_CHARS_PK3 "${SRB2_ASSET_chars.pk3_HASH}" -#define ASSET_HASH_MAPS_WAD "${SRB2_ASSET_maps.wad_HASH}" +#define ASSET_HASH_MAPS_PK3 "${SRB2_ASSET_maps.pk3_HASH}" #ifdef USE_PATCH_FILE #define ASSET_HASH_PATCH_PK3 "${SRB2_ASSET_patch.pk3_HASH}" #endif @@ -36,7 +36,7 @@ #define ASSET_HASH_GFX_PK3 "00000000000000000000000000000000" #define ASSET_HASH_TEXTURES_PK3 "00000000000000000000000000000000" #define ASSET_HASH_CHARS_PK3 "00000000000000000000000000000000" -#define ASSET_HASH_MAPS_WAD "00000000000000000000000000000000" +#define ASSET_HASH_MAPS_PK3 "00000000000000000000000000000000" #ifdef USE_PATCH_FILE #define ASSET_HASH_PATCH_PK3 "00000000000000000000000000000000" #endif diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 4d61194c4..308eb2616 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -47,6 +47,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "k_kart.h" +#include "k_pwrlv.h" #ifdef CLIENT_LOADINGSCREEN // cl loading screen @@ -96,6 +97,8 @@ UINT16 pingmeasurecount = 1; UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. tic_t servermaxping = 800; // server's max ping. Defaults to 800 +static tic_t lowest_lag; +boolean server_lagless; SINT8 nodetoplayer[MAXNETNODES]; SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) SINT8 nodetoplayer3[MAXNETNODES]; // say the numplayer for this node if any (splitscreen == 2) @@ -1240,6 +1243,7 @@ static inline void CL_DrawConnectionStatus(void) case CL_ASKDOWNLOADFILES: case CL_WAITDOWNLOADFILESRESPONSE: cltext = M_GetText("Waiting to download files..."); + break; default: cltext = M_GetText("Connecting to server..."); break; @@ -1350,7 +1354,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); netbuffer->u.serverinfo.kartvars = (UINT8) ( - (cv_kartspeed.value & SV_SPEEDMASK) | + (gamespeed & SV_SPEEDMASK) | (dedicated ? SV_DEDICATED : 0) | (D_IsJoinPasswordOn() ? SV_PASSWORD : 0) ); @@ -1420,6 +1424,11 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) p = PutFileNeeded(0); + if (cv_kartusepwrlv.value) + netbuffer->u.serverinfo.avgpwrlv = K_CalculatePowerLevelAvg(); + else + netbuffer->u.serverinfo.avgpwrlv = -1; + HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); } @@ -1494,7 +1503,7 @@ static void SV_SendPlayerInfo(INT32 node) */ static boolean SV_SendServerConfig(INT32 node) { - INT32 i; + INT32 i, j; UINT8 *p, *op; boolean waspacketsent; @@ -1517,10 +1526,13 @@ static boolean SV_SendServerConfig(INT32 node) memset(netbuffer->u.servercfg.playercolor, 0xFF, sizeof(netbuffer->u.servercfg.playercolor)); memset(netbuffer->u.servercfg.adminplayers, -1, sizeof(netbuffer->u.servercfg.adminplayers)); + memset(netbuffer->u.servercfg.powerlevels, 0, sizeof(netbuffer->u.servercfg.powerlevels)); for (i = 0; i < MAXPLAYERS; i++) { netbuffer->u.servercfg.adminplayers[i] = (SINT8)adminplayers[i]; + for (j = 0; j < PWRLV_NUMTYPES; j++) + netbuffer->u.servercfg.powerlevels[i][j] = clientpowerlevels[i][j]; if (!playeringame[i]) continue; @@ -2306,6 +2318,7 @@ static void CL_ConnectToServer(boolean viams) wipegamestate = GS_WAITINGPLAYERS; ClearAdminPlayers(); + K_ClearClientPowerLevels(); pnumnodes = 1; oldtic = I_GetTime() - 1; #ifndef NONET @@ -3156,6 +3169,21 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) break; } + // SRB2Kart: kicks count as forfeit + switch (kickreason) + { + case KR_KICK: + case KR_BAN: + case KR_LEAVE: + // Intentional removals should be hit with a true forfeit. + K_PlayerForfeit(pnum, true); + break; + default: + // Otherwise, give remaining players the point compensation, but doesn't penalize who left. + K_PlayerForfeit(pnum, false); + break; + } + if (playernode[pnum] == playernode[consoleplayer]) { #ifdef DUMPCONSISTENCY @@ -3324,6 +3352,7 @@ void SV_ResetServer(void) playernode[i] = UINT8_MAX; sprintf(player_names[i], "Player %d", i + 1); adminplayers[i] = -1; // Populate the entire adminplayers array with -1. + K_ClearClientPowerLevels(); } mynode = 0; @@ -3399,6 +3428,7 @@ void D_QuitNetGame(void) D_CloseConnection(); ClearAdminPlayers(); + K_ClearClientPowerLevels(); DEBFILE("===========================================================================\n" " Log finish\n" @@ -3445,11 +3475,12 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) return; } - node = READUINT8(*p); - newplayernum = READUINT8(*p); - splitscreenplayer = newplayernum/MAXPLAYERS; - newplayernum %= MAXPLAYERS; + node = (UINT8)READUINT8(*p); + newplayernum = (UINT8)READUINT8(*p); + splitscreenplayer = (UINT8)READUINT8(*p); + CONS_Debug(DBG_NETPLAY, "addplayer: %d %d %d\n", node, newplayernum, splitscreenplayer); + // Clear player before joining, lest some things get set incorrectly CL_ClearPlayer(newplayernum); @@ -3466,7 +3497,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) if (splitscreenplayer) { displayplayers[splitscreenplayer] = newplayernum; - DEBFILE(va("spawning one of my sister number %d\n", splitscreenplayer)); + DEBFILE(va("spawning sister # %d\n", splitscreenplayer)); if (splitscreenplayer == 1 && botingame) players[newplayernum].bot = 1; } @@ -3477,6 +3508,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) displayplayers[i] = newplayernum; DEBFILE("spawning me\n"); } + D_SendPlayerConfig(); addedtogame = true; } @@ -3532,7 +3564,6 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum) static boolean SV_AddWaitingPlayers(void) { INT32 node, n, newplayer = false; - XBOXSTATIC UINT8 buf[2]; UINT8 newplayernum = 0; // What is the reason for this? Why can't newplayernum always be 0? @@ -3545,6 +3576,9 @@ static boolean SV_AddWaitingPlayers(void) // splitscreen can allow 2+ players in one node for (; nodewaiting[node] > 0; nodewaiting[node]--) { + UINT8 buf[3]; + UINT8 *buf_p = buf; + newplayer = true; // search for a free playernum @@ -3552,8 +3586,10 @@ static boolean SV_AddWaitingPlayers(void) for (; newplayernum < MAXPLAYERS; newplayernum++) { for (n = 0; n < MAXNETNODES; n++) - if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum - || nodetoplayer3[n] == newplayernum || nodetoplayer4[n] == newplayernum) + if (nodetoplayer[n] == newplayernum + || nodetoplayer2[n] == newplayernum + || nodetoplayer3[n] == newplayernum + || nodetoplayer4[n] == newplayernum) break; if (n == MAXNETNODES) break; @@ -3565,28 +3601,23 @@ static boolean SV_AddWaitingPlayers(void) playernode[newplayernum] = (UINT8)node; - buf[0] = (UINT8)node; - buf[1] = newplayernum; + WRITEUINT8(buf_p, (UINT8)node); + WRITEUINT8(buf_p, newplayernum); + if (playerpernode[node] < 1) nodetoplayer[node] = newplayernum; else if (playerpernode[node] < 2) - { nodetoplayer2[node] = newplayernum; - buf[1] += MAXPLAYERS; - } else if (playerpernode[node] < 3) - { nodetoplayer3[node] = newplayernum; - buf[1] += MAXPLAYERS*2; - } - else - { + else if (playerpernode[node] < 4) nodetoplayer4[node] = newplayernum; - buf[1] += MAXPLAYERS*3; - } + + WRITEUINT8(buf_p, playerpernode[node]); // splitscreen num + playerpernode[node]++; - SendNetXCmd(XD_ADDPLAYER, &buf, 2); + SendNetXCmd(XD_ADDPLAYER, buf, buf_p - buf); DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); // use the next free slot (we can't put playeringame[newplayernum] = true here) @@ -3623,6 +3654,11 @@ boolean Playing(void) boolean SV_SpawnServer(void) { +#ifdef TESTERS + /* Just don't let the testers play. Easy. */ + I_Error("What do you think you're doing?"); + return false; +#else if (demo.playback) G_StopDemo(); // reset engine parameter if (metalplayback) @@ -3649,6 +3685,7 @@ boolean SV_SpawnServer(void) } return SV_AddWaitingPlayers(); +#endif } void SV_StopServer(void) @@ -4075,7 +4112,7 @@ static void HandlePacketFromAwayNode(SINT8 node) case PT_SERVERCFG: // Positive response of client join request { - INT32 j; + INT32 j, k; UINT8 *scp; if (server && serverrunning && node != servernode) @@ -4098,7 +4135,11 @@ static void HandlePacketFromAwayNode(SINT8 node) I_Error("Bad gametype in cliserv!"); modifiedgame = netbuffer->u.servercfg.modifiedgame; for (j = 0; j < MAXPLAYERS; j++) + { adminplayers[j] = netbuffer->u.servercfg.adminplayers[j]; + for (k = 0; k < PWRLV_NUMTYPES; k++) + clientpowerlevels[j][k] = netbuffer->u.servercfg.powerlevels[j][k]; + } memcpy(server_context, netbuffer->u.servercfg.server_context, 8); } @@ -4921,6 +4962,9 @@ static void CL_SendClientCmd(void) size_t packetsize = 0; boolean mis = false; + if (lowest_lag && ( gametic % lowest_lag )) + return; + netbuffer->packettype = PT_CLIENTCMD; if (cl_packetmissed) @@ -5389,16 +5433,65 @@ static tic_t gametime = 0; static void UpdatePingTable(void) { + tic_t fastest; + tic_t lag; + INT32 i; + if (server) { if (netgame && !(gametime % 35)) // update once per second. PingUpdate(); + + fastest = 0; + // update node latency values so we can take an average later. for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); + { + if (playeringame[i] && playernode[i] > 0) + { + if (! server_lagless && playernode[i] > 0) + { + lag = GetLag(playernode[i]); + realpingtable[i] += G_TicsToMilliseconds(lag); + + if (! fastest || lag < fastest) + fastest = lag; + } + else + realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); + } + } pingmeasurecount++; + + if (server_lagless) + lowest_lag = 0; + else + { + lowest_lag = fastest; + + if (fastest) + lag = fastest; + else + lag = GetLag(0); + + lag = ( realpingtable[0] + G_TicsToMilliseconds(lag) ); + + switch (playerpernode[0]) + { + case 4: + realpingtable[nodetoplayer4[0]] = lag; + /*FALLTHRU*/ + case 3: + realpingtable[nodetoplayer3[0]] = lag; + /*FALLTHRU*/ + case 2: + realpingtable[nodetoplayer2[0]] = lag; + /*FALLTHRU*/ + case 1: + realpingtable[nodetoplayer[0]] = lag; + } + } } } diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 66d9e73ea..a33d06a2c 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -18,6 +18,7 @@ #include "d_netcmd.h" #include "tables.h" #include "d_player.h" +#include "k_pwrlv.h" // PWRLV_NUMTYPES #include "md5.h" @@ -325,6 +326,7 @@ typedef struct UINT8 gametype; UINT8 modifiedgame; SINT8 adminplayers[MAXPLAYERS]; // Needs to be signed + UINT16 powerlevels[MAXPLAYERS][PWRLV_NUMTYPES]; // SRB2kart: player power levels char server_context[8]; // Unique context id, generated at server startup. @@ -387,6 +389,10 @@ typedef struct UINT8 actnum; UINT8 iszone; UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h) + // Anything beyond this point won't be read by the normal SRB2 Master Server display. + // The MS uses a simple unpack, so the size of the packet above shouldn't be changed, either. + // As long as those two conditions are met, we can add as much information as we want to the end. + INT16 avgpwrlv; // Kart avg power level } ATTRPACK serverinfo_pak; typedef struct @@ -537,6 +543,8 @@ extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS]; extern tic_t servermaxping; +extern boolean server_lagless; + extern consvar_t #ifdef VANILLAJOINNEXTROUND cv_joinnextround, diff --git a/src/d_main.c b/src/d_main.c index 1404a5e61..dee752107 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -308,13 +308,6 @@ static void D_Display(void) wipedefindex = gamestate; // wipe_xxx_toblack if (gamestate == GS_TITLESCREEN && wipegamestate != GS_INTRO) wipedefindex = wipe_timeattack_toblack; - else if (gamestate == GS_INTERMISSION) - { - if (intertype == int_spec) // Special Stage - wipedefindex = wipe_specinter_toblack; - else //if (intertype != int_coop) // Multiplayer - wipedefindex = wipe_multinter_toblack; - } if (rendermode != render_none) { @@ -325,7 +318,7 @@ static void D_Display(void) F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK); + F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK, "FADEMAP0", false, false); } if (gamestate != GS_LEVEL && rendermode != render_none) @@ -447,7 +440,7 @@ static void D_Display(void) { if (i > 0) // Splitscreen-specific { - switch (i) + switch (i) { case 1: if (splitscreen > 1) @@ -556,7 +549,7 @@ static void D_Display(void) if (rendermode != render_none) { F_WipeEndScreen(); - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK); + F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK, "FADEMAP0", true, false); } } @@ -896,11 +889,18 @@ static void IdentifyVersion(void) D_AddFile(va(pandf,srb2waddir,"gfx.pk3"), startupwadfiles); D_AddFile(va(pandf,srb2waddir,"textures.pk3"), startupwadfiles); D_AddFile(va(pandf,srb2waddir,"chars.pk3"), startupwadfiles); - D_AddFile(va(pandf,srb2waddir,"maps.wad"), startupwadfiles); // TODO: make this a pk3 too! + D_AddFile(va(pandf,srb2waddir,"maps.pk3"), startupwadfiles); #ifdef USE_PATCH_FILE D_AddFile(va(pandf,srb2waddir,"patch.pk3"), startupwadfiles); #endif +#if 0 + // TODO: pk3 doesn't support music replacement IIRC + // music barely benefits from the compression anyway + // would be nice for the folders, though + D_AddFile(va(pandf,srb2waddir,"sounds.pk3"), startupwadfiles); + D_AddFile(va(pandf,srb2waddir,"music.pk3"), startupwadfiles); +#else #if !defined (HAVE_SDL) || defined (HAVE_MIXER) #define MUSICTEST(str) \ {\ @@ -915,6 +915,7 @@ static void IdentifyVersion(void) MUSICTEST("music.wad") #undef MUSICTEST #endif +#endif } /* ======================================================================== */ @@ -1182,12 +1183,12 @@ void D_SRB2Main(void) M_InitCharacterTables(); // load wad, including the main wad file - CONS_Printf("W_InitMultipleFiles(): Adding IWAD and main PWADs.\n"); + CONS_Printf("W_InitMultipleFiles(): Adding main IWAD and PWADs.\n"); if (!W_InitMultipleFiles(startupwadfiles, false)) #ifdef _DEBUG - CONS_Error("A WAD file was not found or not valid.\nCheck the log to see which ones.\n"); + CONS_Error("A main WAD file was not found or not valid.\nCheck the log to see which ones.\n"); #else - I_Error("A WAD file was not found or not valid.\nCheck the log to see which ones.\n"); + I_Error("A main WAD file was not found or not valid.\nCheck the log to see which ones.\n"); #endif D_CleanFile(startupwadfiles); @@ -1200,7 +1201,7 @@ void D_SRB2Main(void) mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_GFX_PK3); // gfx.pk3 mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_TEXTURES_PK3); // textures.pk3 mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_CHARS_PK3); // chars.pk3 - mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_MAPS_WAD); // maps.wad -- 4 - If you touch this, make sure to touch up the majormods stuff below. + mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_MAPS_PK3); // maps.pk3 -- 4 - If you touch this, make sure to touch up the majormods stuff below. #ifdef USE_PATCH_FILE mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_PATCH_PK3); // patch.pk3 #endif @@ -1208,7 +1209,7 @@ void D_SRB2Main(void) mainwads++; // gfx.pk3 mainwads++; // textures.pk3 mainwads++; // chars.pk3 - mainwads++; // maps.wad + mainwads++; // maps.pk3 #ifdef USE_PATCH_FILE mainwads++; // patch.pk3 #endif @@ -1241,8 +1242,9 @@ void D_SRB2Main(void) } } + CONS_Printf("W_InitMultipleFiles(): Adding external PWADs.\n"); if (!W_InitMultipleFiles(startuppwads, true)) - CONS_Error("A PWAD file was not found or not valid.\nCheck the log to see which ones.\n"); + M_StartMessage(M_GetText("A PWAD file was not found or not valid.\nCheck log.txt to see which ones.\n\nPress ESC\n"), NULL, MM_NOTHING); D_CleanFile(startuppwads); // @@ -1534,10 +1536,11 @@ void D_SRB2Main(void) newskill = (INT16)kartspeed_cons_t[j].value; break; } + if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match { j = atoi(sskill); // assume they gave us a skill number, which is okay too - if (j >= 0 && j <= 2) + if (j >= KARTSPEED_EASY && j <= KARTSPEED_HARD) newskill = (INT16)j; } @@ -1557,7 +1560,7 @@ void D_SRB2Main(void) else if (!dedicated && M_MapLocked(pstartmap)) I_Error("You need to unlock this level before you can warp to it!\n"); else - D_MapChange(pstartmap, gametype, (boolean)cv_kartencore.value, true, 0, false, false); + D_MapChange(pstartmap, gametype, (cv_kartencore.value == 1), true, 0, false, false); } } else if (M_CheckParm("-skipintro")) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 8bd03dfbf..ecd1de50a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -47,6 +47,7 @@ #include "m_cond.h" #include "m_anigif.h" #include "k_kart.h" // SRB2kart +#include "k_pwrlv.h" #include "y_inter.h" #ifdef NETGAME_DEVMODE @@ -61,6 +62,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum); static void Got_WeaponPref(UINT8 **cp, INT32 playernum); +static void Got_PowerLevel(UINT8 **cp, INT32 playernum); static void Got_Mapcmd(UINT8 **cp, INT32 playernum); static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum); static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum); @@ -91,6 +93,8 @@ static void TeamScramble_OnChange(void); static void NetTimeout_OnChange(void); static void JoinTimeout_OnChange(void); +static void Lagless_OnChange (void); + static void Ringslinger_OnChange(void); static void Gravity_OnChange(void); static void ForceSkin_OnChange(void); @@ -358,12 +362,13 @@ consvar_t cv_kartminimap = {"kartminimap", "4", CV_SAVE, kartminimap_cons_t, NUL consvar_t cv_kartcheck = {"kartcheck", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t kartinvinsfx_cons_t[] = {{0, "Music"}, {1, "SFX"}, {0, NULL}}; consvar_t cv_kartinvinsfx = {"kartinvinsfx", "SFX", CV_SAVE, kartinvinsfx_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_kartspeed = {"kartspeed", "Normal", CV_NETVAR|CV_CALL|CV_NOINIT, kartspeed_cons_t, KartSpeed_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_kartspeed = {"kartspeed", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartspeed_cons_t, KartSpeed_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t kartbumpers_cons_t[] = {{1, "MIN"}, {12, "MAX"}, {0, NULL}}; consvar_t cv_kartbumpers = {"kartbumpers", "3", CV_NETVAR|CV_CHEAT, kartbumpers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartfrantic = {"kartfrantic", "Off", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOINIT, CV_OnOff, KartFrantic_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartcomeback = {"kartcomeback", "On", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOINIT, CV_OnOff, KartComeback_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_kartencore = {"kartencore", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartEncore_OnChange, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t kartencore_cons_t[] = {{-1, "Auto"}, {0, "Off"}, {1, "On"}, {0, NULL}}; +consvar_t cv_kartencore = {"kartencore", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartencore_cons_t, KartEncore_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}}; consvar_t cv_kartvoterulechanges = {"kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}}; @@ -373,6 +378,8 @@ consvar_t cv_kartvoices = {"kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, consvar_t cv_karteliminatelast = {"karteliminatelast", "Yes", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOSHOWHELP, CV_YesNo, KartEliminateLast_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_kartusepwrlv = {"kartusepwrlv", "Yes", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; + static CV_PossibleValue_t kartdebugitem_cons_t[] = {{-1, "MIN"}, {NUMKARTITEMS-1, "MAX"}, {0, NULL}}; consvar_t cv_kartdebugitem = {"kartdebugitem", "0", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugitem_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t kartdebugamount_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, NULL}}; @@ -411,7 +418,8 @@ consvar_t cv_itemfinder = {"itemfinder", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, // Scoring type options consvar_t cv_match_scoring = {"matchscoring", "Normal", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, match_scoring_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t overtime_cons_t[] = {{0, "No"}, {1, "Yes"}, {2, "Super"}, {0, NULL}}; +consvar_t cv_overtime = {"overtime", "Yes", CV_NETVAR|CV_CHEAT, overtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_rollingdemos = {"rollingdemos", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -420,13 +428,13 @@ consvar_t cv_timetic = {"timerres", "Normal", CV_SAVE|CV_NOSHOWHELP, timetic_con static CV_PossibleValue_t pointlimit_cons_t[] = {{0, "MIN"}, {999999990, "MAX"}, {0, NULL}}; consvar_t cv_pointlimit = {"pointlimit", "0", CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange, 0, NULL, NULL, 0, 0, NULL}; -static CV_PossibleValue_t timelimit_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t timelimit_cons_t[] = {{0, "MIN"}, {1800, "MAX"}, {0, NULL}}; consvar_t cv_timelimit = {"timelimit", "0", CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange, 0, NULL, NULL, 0, 0, NULL}; -static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}}; consvar_t cv_numlaps = {"numlaps", "3", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t, NumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL}; -static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, "Map default"}, {0, NULL}}; +static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, "Map default"}, {0, NULL}}; consvar_t cv_basenumlaps = {"basenumlaps", "Map default", CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_forceskin = {"forceskin", "Off", CV_NETVAR|CV_CALL|CV_CHEAT, Forceskin_cons_t, ForceSkin_OnChange, 0, NULL, NULL, 0, 0, NULL}; @@ -443,6 +451,8 @@ consvar_t cv_jointimeout = {"jointimeout", "105", CV_CALL|CV_SAVE, nettimeout_co static CV_PossibleValue_t maxping_cons_t[] = {{0, "MIN"}, {1000, "MAX"}, {0, NULL}}; consvar_t cv_maxping = {"maxping", "800", CV_SAVE, maxping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_lagless = {"lagless", "Off", CV_SAVE|CV_NETVAR|CV_CALL, CV_OnOff, Lagless_OnChange, 0, NULL, NULL, 0, 0, NULL}; + static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}}; consvar_t cv_pingtimeout = {"pingtimeout", "10", CV_SAVE, pingtimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -501,6 +511,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "MODIFYVOTE", "PICKVOTE", "REMOVEPLAYER", + "POWERLEVEL", #ifdef HAVE_BLUA "LUACMD", "LUAVAR" @@ -538,6 +549,7 @@ void D_RegisterServerCommands(void) } RegisterNetXCmd(XD_NAMEANDCOLOR, Got_NameAndColor); RegisterNetXCmd(XD_WEAPONPREF, Got_WeaponPref); + RegisterNetXCmd(XD_POWERLEVEL, Got_PowerLevel); RegisterNetXCmd(XD_MAP, Got_Mapcmd); RegisterNetXCmd(XD_EXITLEVEL, Got_ExitLevelcmd); RegisterNetXCmd(XD_ADDFILE, Got_Addfilecmd); @@ -705,6 +717,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_skipmapcheck); CV_RegisterVar(&cv_sleep); CV_RegisterVar(&cv_maxping); + CV_RegisterVar(&cv_lagless); CV_RegisterVar(&cv_pingtimeout); CV_RegisterVar(&cv_showping); @@ -1925,6 +1938,17 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum) players[playernum].pflags |= PF_ANALOGMODE; } +static void Got_PowerLevel(UINT8 **cp,INT32 playernum) +{ + UINT16 race = (UINT16)READUINT16(*cp); + UINT16 battle = (UINT16)READUINT16(*cp); + + clientpowerlevels[playernum][PWRLV_RACE] = min(PWRLVRECORD_MAX, race); + clientpowerlevels[playernum][PWRLV_BATTLE] = min(PWRLVRECORD_MAX, battle); + + CONS_Debug(DBG_GAMELOGIC, "set player %d to power %d\n", playernum, race); +} + void D_SendPlayerConfig(void) { SendNameAndColor(); @@ -1941,6 +1965,31 @@ void D_SendPlayerConfig(void) SendWeaponPref3(); if (splitscreen > 2) SendWeaponPref4(); + + { + UINT8 buf[4]; + UINT8 *buf_p = buf; + + WRITEUINT16(buf_p, vspowerlevel[PWRLV_RACE]); + WRITEUINT16(buf_p, vspowerlevel[PWRLV_BATTLE]); + + SendNetXCmd(XD_POWERLEVEL, buf, 4); + } + + if (splitscreen) + { + UINT8 buf[4]; + UINT8 *buf_p = buf; + + WRITEUINT16(buf_p, 0); + WRITEUINT16(buf_p, 0); + + SendNetXCmd2(XD_POWERLEVEL, buf, 4); + if (splitscreen > 1) + SendNetXCmd3(XD_POWERLEVEL, buf, 4); + if (splitscreen > 2) + SendNetXCmd4(XD_POWERLEVEL, buf, 4); + } } // Only works for displayplayer, sorry! @@ -2353,7 +2402,7 @@ void D_SetupVote(void) UINT8 secondgt = G_SometimesGetDifferentGametype(); INT16 votebuffer[3] = {-1,-1,-1}; - if (cv_kartencore.value && G_RaceGametype()) + if ((cv_kartencore.value == 1) && G_RaceGametype()) WRITEUINT8(p, (gametype|0x80)); else WRITEUINT8(p, gametype); @@ -2524,7 +2573,7 @@ static void Command_Map_f(void) // new encoremode value // use cvar by default - newencoremode = (boolean)cv_kartencore.value; + newencoremode = (cv_kartencore.value == 1); if (COM_CheckParm("-encore")) { @@ -3651,6 +3700,9 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) if (K_IsPlayerWanted(&players[playernum])) K_CalculateBattleWanted(); } + + K_PlayerForfeit(playernum, true); + players[playernum].health = 1; if (players[playernum].mo) players[playernum].mo->health = 1; @@ -4724,6 +4776,14 @@ static void JoinTimeout_OnChange(void) jointimeout = (tic_t)cv_jointimeout.value; } +static void +Lagless_OnChange (void) +{ + /* don't back out of dishonesty, or go lagless after playing honestly */ + if (cv_lagless.value && gamestate == GS_LEVEL) + server_lagless = true; +} + UINT32 timelimitintics = 0; /** Deals with a timelimit change by printing the change to the console. @@ -4745,8 +4805,8 @@ static void TimeLimit_OnChange(void) if (cv_timelimit.value != 0) { - CONS_Printf(M_GetText("Levels will end after %d minute%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003 - timelimitintics = cv_timelimit.value * 60 * TICRATE; + CONS_Printf(M_GetText("Levels will end after %d second%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003 + timelimitintics = cv_timelimit.value * TICRATE; //add hidetime for tag too! if (G_TagGametype()) @@ -4796,9 +4856,9 @@ void D_GameTypeChanged(INT32 lastgametype) case GT_TEAMMATCH: if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits { - // default settings for match: no timelimit, no pointlimit - CV_SetValue(&cv_pointlimit, 0); - CV_SetValue(&cv_timelimit, 0); + // default settings for match: 2 mins, no pointlimit + CV_SetValue(&cv_pointlimit, 0); + CV_SetValue(&cv_timelimit, 120); } if (!cv_itemrespawntime.changed) CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally @@ -5104,7 +5164,7 @@ static void Hidetime_OnChange(void) //uh oh, gotta change timelimitintics now too if (G_TagGametype()) - timelimitintics = (cv_timelimit.value * 60 * TICRATE) + (hidetime * TICRATE); + timelimitintics = (cv_timelimit.value * TICRATE) + (hidetime * TICRATE); } static void Command_Showmap_f(void) @@ -5734,22 +5794,24 @@ static void KartFrantic_OnChange(void) static void KartSpeed_OnChange(void) { - if (!M_SecretUnlocked(SECRET_HARDSPEED) && cv_kartspeed.value == 2) + if (!M_SecretUnlocked(SECRET_HARDSPEED) && cv_kartspeed.value == KARTSPEED_HARD) { CONS_Printf(M_GetText("You haven't earned this yet.\n")); - CV_StealthSetValue(&cv_kartspeed, 1); + CV_StealthSet(&cv_kartspeed, cv_kartspeed.defaultvalue); return; } if (G_RaceGametype()) { - if ((UINT8)cv_kartspeed.value != gamespeed && gamestate == GS_LEVEL && leveltime > starttime) - CONS_Printf(M_GetText("Game speed will be changed to \"%s\" next round.\n"), cv_kartspeed.string); - else + if ((gamestate == GS_LEVEL && leveltime < starttime) && (cv_kartspeed.value != KARTSPEED_AUTO)) { CONS_Printf(M_GetText("Game speed has been changed to \"%s\".\n"), cv_kartspeed.string); gamespeed = (UINT8)cv_kartspeed.value; } + else if (cv_kartspeed.value != (signed)gamespeed) + { + CONS_Printf(M_GetText("Game speed will be changed to \"%s\" next round.\n"), cv_kartspeed.string); + } } } @@ -5757,10 +5819,10 @@ static void KartEncore_OnChange(void) { if (G_RaceGametype()) { - if ((boolean)cv_kartencore.value != encoremode && gamestate == GS_LEVEL /*&& leveltime > starttime*/) - CONS_Printf(M_GetText("Encore Mode will be turned %s next round.\n"), cv_kartencore.value ? M_GetText("on") : M_GetText("off")); + if ((cv_kartencore.value == 1) != encoremode && gamestate == GS_LEVEL /*&& leveltime > starttime*/) + CONS_Printf(M_GetText("Encore Mode will be set to %s next round.\n"), cv_kartencore.string); else - CONS_Printf(M_GetText("Encore Mode has been turned %s.\n"), cv_kartencore.value ? M_GetText("on") : M_GetText("off")); + CONS_Printf(M_GetText("Encore Mode has been set to %s.\n"), cv_kartencore.string); } } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 00ddc2254..d2a88eddf 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -120,8 +120,8 @@ extern consvar_t cv_kartencore; extern consvar_t cv_kartvoterulechanges; extern consvar_t cv_kartspeedometer; extern consvar_t cv_kartvoices; - extern consvar_t cv_karteliminatelast; +extern consvar_t cv_kartusepwrlv; extern consvar_t cv_votetime; @@ -145,6 +145,7 @@ extern consvar_t cv_ringslinger, cv_soundtest; extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionboxes; extern consvar_t cv_maxping; +extern consvar_t cv_lagless; extern consvar_t cv_pingtimeout; extern consvar_t cv_showping; @@ -179,9 +180,10 @@ typedef enum XD_MODIFYVOTE, // 23 XD_PICKVOTE, // 24 XD_REMOVEPLAYER,// 25 + XD_POWERLEVEL, // 26 #ifdef HAVE_BLUA - XD_LUACMD, // 26 - XD_LUAVAR, // 27 + XD_LUACMD, // 27 + XD_LUAVAR, // 28 #endif MAXNETXCMD } netxcmd_t; diff --git a/src/d_player.h b/src/d_player.h index 1eb63770c..6e238e74c 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -326,6 +326,10 @@ typedef enum k_jawztargetdelay, // Delay for Jawz target switching, to make it less twitchy k_spectatewait, // How long have you been waiting as a spectator k_growcancel, // Hold the item button down to cancel Grow + k_tiregrease, // Reduced friction timer after hitting a horizontal spring + k_springstars, // Spawn stars around a player when they hit a spring + k_springcolor, // Color of spring stars + k_killfield, // How long have you been in the kill field, stay in too long and lose a bumper NUMKARTSTUFF } kartstufftype_t; diff --git a/src/dehacked.c b/src/dehacked.c index 2162bcbf5..88c283c3e 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1230,8 +1230,9 @@ static void readlevelheader(MYFILE *f, INT32 num) } else if (fastcmp(word, "WEATHER")) mapheaderinfo[num-1]->weather = (UINT8)get_number(word2); - else if (fastcmp(word, "SKYNUM")) - mapheaderinfo[num-1]->skynum = (INT16)i; + else if (fastcmp(word, "SKYTEXTURE")) + deh_strlcpy(mapheaderinfo[num-1]->skytexture, word2, + sizeof(mapheaderinfo[num-1]->skytexture), va("Level header %d: sky texture", num)); else if (fastcmp(word, "INTERSCREEN")) strncpy(mapheaderinfo[num-1]->interscreen, word2, 8); else if (fastcmp(word, "PRECUTSCENENUM")) @@ -1773,7 +1774,6 @@ static actionpointer_t actionpointers[] = {{A_GrenadeRing}, "A_GRENADERING"}, // SRB2kart {{A_SetSolidSteam}, "A_SETSOLIDSTEAM"}, {{A_UnsetSolidSteam}, "A_UNSETSOLIDSTEAM"}, - {{A_SignPlayer}, "A_SIGNPLAYER"}, {{A_OverlayThink}, "A_OVERLAYTHINK"}, {{A_JetChase}, "A_JETCHASE"}, {{A_JetbThink}, "A_JETBTHINK"}, @@ -2552,6 +2552,19 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) ty = UC_PLAYTIME + offset; re = atoi(params[1]); } + else if ((offset=0) || fastcmp(params[0], "POWERLEVEL")) + { + PARAMCHECK(2); + ty = UC_POWERLEVEL; + re = atoi(params[1]); + x1 = atoi(params[2]); + + if (x1 < 0 || x1 >= PWRLV_NUMTYPES) + { + deh_warning("Power level type %d out of range (0 - %d)", x1, PWRLV_NUMTYPES-1); + return; + } + } else if ((offset=0) || fastcmp(params[0], "GAMECLEAR") || (++offset && fastcmp(params[0], "ALLEMERALDS"))) //|| (++offset && fastcmp(params[0], "ULTIMATECLEAR"))) @@ -2604,7 +2617,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (x1 < 0 || x1 >= NUMMAPS) { - deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); + deh_warning("Level number %d out of range (1 - %d)", x1, NUMMAPS); return; } } @@ -3256,29 +3269,13 @@ static void readwipes(MYFILE *f) else if (fastcmp(pword, "FINAL")) wipeoffset = wipe_intermission_final; } - else if (fastncmp(word, "SPECINTER_", 10)) - { - pword = word + 10; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_specinter_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_specinter_final; - } else if (fastncmp(word, "VOTING_", 7)) { pword = word + 7; if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_specinter_toblack; + wipeoffset = wipe_voting_toblack; else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_specinter_final; - } - else if (fastncmp(word, "MULTINTER_", 10)) - { - pword = word + 10; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_multinter_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_multinter_final; + wipeoffset = wipe_voting_final; } else if (fastncmp(word, "CONTINUING_", 11)) { @@ -3330,11 +3327,13 @@ static void readwipes(MYFILE *f) else if (fastcmp(pword, "FINAL")) wipeoffset = wipe_gameend_final; } - else if (fastncmp(word, "SPECLEVEL_", 10)) + else if (fastncmp(word, "ENCORE_", 7)) { - pword = word + 10; - if (fastcmp(pword, "TOWHITE")) - wipeoffset = wipe_speclevel_towhite; + pword = word + 7; + if (fastcmp(pword, "TOINVERT")) + wipeoffset = wipe_encore_toinvert; + else if (fastcmp(pword, "TOWHITE")) + wipeoffset = wipe_encore_towhite; } if (wipeoffset < 0) @@ -3344,10 +3343,10 @@ static void readwipes(MYFILE *f) } if (value == UINT8_MAX - && (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_speclevel_towhite)) + && (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_encore_toinvert)) { // Cannot disable non-toblack wipes - // (or the level toblack wipe, or the special towhite wipe) + // (or the level toblack wipe, or the special encore wipe) deh_warning("Wipes: can't disable wipe of type '%s'", word); continue; } @@ -4887,27 +4886,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BUBBLES2", // Level End Sign - "S_SIGN1", - "S_SIGN2", - "S_SIGN3", - "S_SIGN4", - "S_SIGN5", - "S_SIGN6", - "S_SIGN7", - "S_SIGN8", - "S_SIGN9", - "S_SIGN10", - "S_SIGN11", - "S_SIGN12", - "S_SIGN13", - "S_SIGN14", - "S_SIGN15", - "S_SIGN16", - "S_SIGN17", - "S_SIGN18", - "S_SIGN19", - "S_SIGN20", - "S_SIGN_END", + "S_SIGN_POLE", + "S_SIGN_BACK", + "S_SIGN_SIDE", + "S_SIGN_FACE", // Steam Riser "S_STEAM1", @@ -5609,44 +5591,77 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_RBIRD2", "S_RBIRD3", - "S_YELLOWSPRING", + // Yellow Spring + "S_YELLOWSPRING1", "S_YELLOWSPRING2", "S_YELLOWSPRING3", "S_YELLOWSPRING4", - "S_YELLOWSPRING5", - "S_REDSPRING", + // Red Spring + "S_REDSPRING1", "S_REDSPRING2", "S_REDSPRING3", "S_REDSPRING4", - "S_REDSPRING5", - // Blue Springs - "S_BLUESPRING", + // Blue Spring + "S_BLUESPRING1", "S_BLUESPRING2", "S_BLUESPRING3", "S_BLUESPRING4", - "S_BLUESPRING5", + + // Grey Spring + "S_GREYSPRING1", + "S_GREYSPRING2", + "S_GREYSPRING3", + "S_GREYSPRING4", // Yellow Diagonal Spring "S_YDIAG1", "S_YDIAG2", "S_YDIAG3", "S_YDIAG4", - "S_YDIAG5", - "S_YDIAG6", - "S_YDIAG7", - "S_YDIAG8", // Red Diagonal Spring "S_RDIAG1", "S_RDIAG2", "S_RDIAG3", "S_RDIAG4", - "S_RDIAG5", - "S_RDIAG6", - "S_RDIAG7", - "S_RDIAG8", + + // Blue Diagonal Spring + "S_BDIAG1", + "S_BDIAG2", + "S_BDIAG3", + "S_BDIAG4", + + // Grey Diagonal Spring + "S_GDIAG1", + "S_GDIAG2", + "S_GDIAG3", + "S_GDIAG4", + + // Yellow Horizontal Spring + "S_YHORIZ1", + "S_YHORIZ2", + "S_YHORIZ3", + "S_YHORIZ4", + + // Red Horizontal Spring + "S_RHORIZ1", + "S_RHORIZ2", + "S_RHORIZ3", + "S_RHORIZ4", + + // Blue Horizontal Spring + "S_BHORIZ1", + "S_BHORIZ2", + "S_BHORIZ3", + "S_BHORIZ4", + + // Grey Horizontal Spring + "S_GHORIZ1", + "S_GHORIZ2", + "S_GHORIZ3", + "S_GHORIZ4", // Rain "S_RAIN1", @@ -5657,6 +5672,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SNOW2", "S_SNOW3", + // Blizzard Snowball + "S_BLIZZARDSNOW1", + "S_BLIZZARDSNOW2", + "S_BLIZZARDSNOW3", + // Water Splish "S_SPLISH1", "S_SPLISH2", @@ -6271,26 +6291,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SRB1_GENREX1", "S_SRB1_GENREX2", - // Gray Springs - "S_GRAYSPRING", - "S_GRAYSPRING2", - "S_GRAYSPRING3", - "S_GRAYSPRING4", - "S_GRAYSPRING5", - - // Invis-spring - this is used just for the sproing sound. - "S_INVISSPRING", - - // Blue Diagonal Spring - "S_BDIAG1", - "S_BDIAG2", - "S_BDIAG3", - "S_BDIAG4", - "S_BDIAG5", - "S_BDIAG6", - "S_BDIAG7", - "S_BDIAG8", - //{ Random Item Box "S_RANDOMITEM1", "S_RANDOMITEM2", @@ -6766,9 +6766,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_LAMPPOST", "S_MOSSYTREE", - "S_SHADOW", - "S_WHITESHADOW", - "S_BUMP1", "S_BUMP2", "S_BUMP3", @@ -7199,6 +7196,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DRAFTDUST4", "S_DRAFTDUST5", + "S_TIREGREASE", + + "S_OVERTIMEFOG", + "S_OVERTIMEORB", + "S_OVERTIMEBEAM", + #ifdef SEENAMES "S_NAMECHECK", #endif @@ -7328,15 +7331,23 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Springs and others "MT_FAN", "MT_STEAM", // Steam riser - "MT_BLUESPRING", "MT_YELLOWSPRING", "MT_REDSPRING", + "MT_BLUESPRING", + "MT_GREYSPRING", "MT_YELLOWDIAG", // Yellow Diagonal Spring "MT_REDDIAG", // Red Diagonal Spring + "MT_BLUEDIAG", // Blue Diagonal Spring + "MT_GREYDIAG", // Grey Diagonal Spring + "MT_YELLOWHORIZ", // Yellow Horizontal Spring + "MT_REDHORIZ", // Red Horizontal Spring + "MT_BLUEHORIZ", // Blue Horizontal Spring + "MT_GREYHORIZ", // Grey Horizontal Spring // Interactive Objects "MT_BUBBLES", // Bubble source "MT_SIGN", // Level end sign + "MT_SIGN_PIECE", "MT_SPIKEBALL", // Spike Ball "MT_SPECIALSPIKEBALL", "MT_SPINFIRE", @@ -7563,6 +7574,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Environmental Effects "MT_RAIN", // Rain "MT_SNOWFLAKE", // Snowflake + "MT_BLIZZARDSNOW", // Blizzard Snowball "MT_SPLISH", // Water splish! "MT_SMOKE", "MT_SMALLBUBBLE", // small bubble @@ -7714,9 +7726,6 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_SRB1_GENREX", // SRB2kart - "MT_GRAYSPRING", - "MT_INVISSPRING", - "MT_BLUEDIAG", "MT_RANDOMITEM", "MT_RANDOMITEMPOP", "MT_FLOATINGITEM", @@ -7826,8 +7835,6 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_LAMPPOST", "MT_MOSSYTREE", - "MT_SHADOW", - "MT_BUMP", "MT_FLINGENERGY", @@ -7987,6 +7994,11 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_KARMAFIREWORK", "MT_RINGSPARKS", "MT_DRAFTDUST", + "MT_TIREGREASE", + + "MT_OVERTIMEFOG", + "MT_OVERTIMEORB", + "MT_OVERTIMEBEAM", #ifdef SEENAMES "MT_NAMECHECK", @@ -8469,7 +8481,11 @@ static const char *const KARTSTUFF_LIST[] = { "GETSPARKS", "JAWZTARGETDELAY", "SPECTATEWAIT", - "GROWCANCEL" + "GROWCANCEL", + "TIREGREASE", + "SPRINGSTARS", + "SPRINGCOLOR", + "KILLFIELD" }; #endif @@ -8664,10 +8680,10 @@ struct { // Precipitation {"PRECIP_NONE",PRECIP_NONE}, - {"PRECIP_STORM",PRECIP_STORM}, - {"PRECIP_SNOW",PRECIP_SNOW}, {"PRECIP_RAIN",PRECIP_RAIN}, - {"PRECIP_BLANK",PRECIP_BLANK}, + {"PRECIP_SNOW",PRECIP_SNOW}, + {"PRECIP_BLIZZARD",PRECIP_BLIZZARD}, + {"PRECIP_STORM",PRECIP_STORM}, {"PRECIP_STORM_NORAIN",PRECIP_STORM_NORAIN}, {"PRECIP_STORM_NOSTRIKES",PRECIP_STORM_NOSTRIKES}, @@ -9899,11 +9915,11 @@ static inline int lib_getenum(lua_State *L) } else if (fastcmp(word,"globalweather")) { lua_pushinteger(L, globalweather); return 1; - } else if (fastcmp(word,"levelskynum")) { - lua_pushinteger(L, levelskynum); + } else if (fastcmp(word,"levelskytexture")) { + lua_pushstring(L, levelskytexture); return 1; - } else if (fastcmp(word,"globallevelskynum")) { - lua_pushinteger(L, globallevelskynum); + } else if (fastcmp(word,"globallevelskytexture")) { + lua_pushstring(L, globallevelskytexture); return 1; } else if (fastcmp(word,"mapmusname")) { lua_pushstring(L, mapmusname); diff --git a/src/djgppdos/i_system.c b/src/djgppdos/i_system.c index bc3c8b2bc..b5785bb36 100644 --- a/src/djgppdos/i_system.c +++ b/src/djgppdos/i_system.c @@ -59,6 +59,8 @@ #include "../z_zone.h" #include "../g_input.h" +#include "../k_pwrlv.h" + #include "../console.h" #ifdef __GNUG__ @@ -618,6 +620,11 @@ void I_Quit (void) #ifndef NONET D_SaveBan(); // save the ban list #endif + + // Make sure you lose points for ALT-F4 + if (Playing()) + K_PlayerForfeit(consoleplayer, true); + G_SaveGameData(); // Tails 12-08-2002 if (demorecording) G_CheckDemoStatus(); diff --git a/src/doomdef.h b/src/doomdef.h index 645864257..e1e9ab13d 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -158,9 +158,6 @@ extern FILE *logstream; // AND appveyor.yml, for the build bots! #endif -// Maintain compatibility with 1.0.x record attack replays? -//#define DEMO_COMPAT_100 - // Does this version require an added patch file? // Comment or uncomment this as necessary. #define USE_PATCH_FILE diff --git a/src/doomstat.h b/src/doomstat.h index 1f855da27..f4f7acfd0 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -41,18 +41,36 @@ extern UINT32 mapmusposition; extern INT16 maptol; extern UINT8 globalweather; -extern INT32 curWeather; +extern UINT8 curWeather; extern INT32 cursaveslot; extern INT16 lastmapsaved; extern boolean gamecomplete; -#define PRECIP_NONE 0 -#define PRECIP_STORM 1 -#define PRECIP_SNOW 2 -#define PRECIP_RAIN 3 -#define PRECIP_BLANK 4 -#define PRECIP_STORM_NORAIN 5 -#define PRECIP_STORM_NOSTRIKES 6 +typedef enum +{ + PRECIP_NONE = 0, + PRECIP_RAIN, + PRECIP_SNOW, + PRECIP_BLIZZARD, + PRECIP_STORM, + PRECIP_STORM_NORAIN, + PRECIP_STORM_NOSTRIKES, + MAXPRECIP +} preciptype_t; + +typedef enum +{ + PRECIPFX_THUNDER = 1, + PRECIPFX_LIGHTNING = 1<<1 +} precipeffect_t; + +typedef struct +{ + mobjtype_t type; + precipeffect_t effects; +} precipprops_t; + +extern precipprops_t precipprops[MAXPRECIP]; // Set if homebrew PWAD stuff has been added. extern boolean modifiedgame; @@ -227,7 +245,7 @@ typedef struct UINT32 muspos; ///< Music position to jump to. char forcecharacter[17]; ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable. UINT8 weather; ///< 0 = sunny day, 1 = storm, 2 = snow, 3 = rain, 4 = blank, 5 = thunder w/o rain, 6 = rain w/o lightning, 7 = heat wave. - INT16 skynum; ///< Sky number to use. + char skytexture[9]; ///< Sky texture to use. INT16 skybox_scalex; ///< Skybox X axis scale. (0 = no movement, 1 = 1:1 movement, 16 = 16:1 slow movement, -4 = 1:4 fast movement, etc.) INT16 skybox_scaley; ///< Skybox Y axis scale. INT16 skybox_scalez; ///< Skybox Z axis scale. @@ -435,6 +453,7 @@ extern INT32 sneakertime; extern INT32 itemtime; extern INT32 comebacktime; extern INT32 bumptime; +extern INT32 greasetics; extern INT32 wipeoutslowtime; extern INT32 wantedreduce; extern INT32 wantedfrequency; @@ -474,7 +493,6 @@ extern tic_t wantedcalcdelay; extern tic_t indirectitemcooldown; extern tic_t hyubgone; extern tic_t mapreset; -extern UINT8 nospectategrief; extern boolean thwompsactive; extern SINT8 spbplace; @@ -486,6 +504,15 @@ extern INT16 votelevels[5][2]; extern SINT8 votes[MAXPLAYERS]; extern SINT8 pickedvote; +/** Battle overtime information + */ +extern struct battleovertime +{ + UINT16 enabled; ///< Has this been initalized yet? + fixed_t radius, minradius; ///< Radius of kill field + fixed_t x, y, z; ///< Position to center on +} battleovertime; + extern tic_t hidetime; extern UINT32 timesBeaten; // # of times the game has been beaten. diff --git a/src/f_finale.c b/src/f_finale.c index 9a4be070c..c620c3ffd 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -220,7 +220,7 @@ void F_StartIntro(void) F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_level_final], false); + F_RunWipe(wipedefs[wipe_intro_toblack], false, "FADEMAP0", false, false); } if (introtoplay) @@ -306,7 +306,7 @@ void F_IntroDrawer(void) F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeEndScreen(); - F_RunWipe(99,true); + F_RunWipe(99, true, "FADEMAP0", false, false); } // Stay on black for a bit. =) @@ -1420,7 +1420,7 @@ void F_CutsceneDrawer(void) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, cutscenes[cutnum]->scene[scenenum].fadecolor); F_WipeEndScreen(); - F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeinid, true); + F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeinid, true, NULL, false, false); F_WipeStartScreen(); } @@ -1440,7 +1440,7 @@ void F_CutsceneDrawer(void) if (dofadenow && rendermode != render_none) { F_WipeEndScreen(); - F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeoutid, true); + F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeoutid, true, NULL, false, false); } V_DrawString(textxpos, textypos, 0, cutscene_disptext); diff --git a/src/f_finale.h b/src/f_finale.h index 45166e03d..d1efa6ce2 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -74,12 +74,13 @@ extern INT32 lastwipetic; void F_WipeStartScreen(void); void F_WipeEndScreen(void); -void F_RunWipe(UINT8 wipetype, boolean drawMenu); +void F_RunWipe(UINT8 wipetype, boolean drawMenu, const char *colormap, boolean reverse, boolean encorewiggle); enum { wipe_credits_intermediate, // makes a good 0 I guess. + // Gamestate wipes wipe_level_toblack, wipe_intermission_toblack, wipe_voting_toblack, @@ -92,11 +93,11 @@ enum wipe_intro_toblack, wipe_cutscene_toblack, - // custom intermissions - wipe_specinter_toblack, - wipe_multinter_toblack, - wipe_speclevel_towhite, + // Specialized wipes + wipe_encore_toinvert, + wipe_encore_towhite, + // "From black" wipes wipe_level_final, wipe_intermission_final, wipe_voting_final, @@ -109,10 +110,6 @@ enum wipe_intro_final, wipe_cutscene_final, - // custom intermissions - wipe_specinter_final, - wipe_multinter_final, - NUMWIPEDEFS, WIPEFINALSHIFT = wipe_level_final - wipe_level_toblack }; diff --git a/src/f_wipe.c b/src/f_wipe.c index 3c8713d18..8d73c1fe7 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -16,8 +16,10 @@ #include "i_video.h" #include "v_video.h" +#include "r_data.h" // NearestColor #include "r_draw.h" // transtable #include "p_pspr.h" // tr_transxxx + #include "w_wad.h" #include "z_zone.h" @@ -47,35 +49,31 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 99, // wipe_credits_intermediate (0) 0, // wipe_level_toblack - UINT8_MAX, // wipe_intermission_toblack + 0, // wipe_intermission_toblack 0, // wipe_voting_toblack, - UINT8_MAX, // wipe_continuing_toblack - 3, // wipe_titlescreen_toblack + 0, // wipe_continuing_toblack + 0, // wipe_titlescreen_toblack 0, // wipe_timeattack_toblack 99, // wipe_credits_toblack 0, // wipe_evaluation_toblack 0, // wipe_gameend_toblack UINT8_MAX, // wipe_intro_toblack (hardcoded) - UINT8_MAX, // wipe_cutscene_toblack (hardcoded) + 99, // wipe_cutscene_toblack (hardcoded) - UINT8_MAX, // wipe_specinter_toblack - UINT8_MAX, // wipe_multinter_toblack - 99, // wipe_speclevel_towhite + 72, // wipe_encore_toinvert + 99, // wipe_encore_towhite - 3, // wipe_level_final + UINT8_MAX, // wipe_level_final 0, // wipe_intermission_final 0, // wipe_voting_final 0, // wipe_continuing_final - 3, // wipe_titlescreen_final + 0, // wipe_titlescreen_final 0, // wipe_timeattack_final 99, // wipe_credits_final 0, // wipe_evaluation_final 0, // wipe_gameend_final 99, // wipe_intro_final (hardcoded) - 99, // wipe_cutscene_final (hardcoded) - - 0, // wipe_specinter_final - 0 // wipe_multinter_final + 99 // wipe_cutscene_final (hardcoded) }; //-------------------------------------------------------------------------- @@ -86,9 +84,13 @@ boolean WipeInAction = false; INT32 lastwipetic = 0; #ifndef NOWIPE + +#define GENLEN 31 + static UINT8 *wipe_scr_start; //screen 3 static UINT8 *wipe_scr_end; //screen 4 static UINT8 *wipe_scr; //screen 0 (main drawing) +static UINT8 pallen; static fixed_t paldiv; /** Create fademask_t from lump @@ -181,7 +183,7 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) { * * \param fademask pixels to change */ -static void F_DoWipe(fademask_t *fademask) +static void F_DoWipe(fademask_t *fademask, lighttable_t *fadecolormap, boolean reverse) { // Software mask wipe -- optimized; though it might not look like it! // Okay, to save you wondering *how* this is more optimized than the simpler @@ -199,6 +201,10 @@ static void F_DoWipe(fademask_t *fademask) // look a little messy; sorry!) but it simultaneously runs at twice the speed. // In addition, we precalculate all the X and Y positions that we need to draw // from and to, so it uses a little extra memory, but again, helps it run faster. + // --- + // Sal: I kinda destroyed some of this code by introducing Genesis-style fades. + // A colormap can be provided in F_RunWipe, which the white/black values will be + // remapped to the appropriate entry in the fade colormap. { // wipe screen, start, end UINT8 *w = wipe_scr; @@ -242,6 +248,8 @@ static void F_DoWipe(fademask_t *fademask) maskx = masky = 0; do { + UINT8 m = *mask; + draw_rowstart = scrxpos[maskx]; draw_rowend = scrxpos[maskx + 1]; draw_linestart = scrypos[masky]; @@ -250,28 +258,31 @@ static void F_DoWipe(fademask_t *fademask) relativepos = (draw_linestart * vid.width) + draw_rowstart; draw_linestogo = draw_lineend - draw_linestart; - if (*mask == 0) + if (reverse) + m = ((pallen-1) - m); + + if (m == 0) { // shortcut - memcpy source to work while (draw_linestogo--) { - M_Memcpy(w_base+relativepos, s_base+relativepos, draw_rowend-draw_rowstart); + M_Memcpy(w_base+relativepos, (reverse ? e_base : s_base)+relativepos, draw_rowend-draw_rowstart); relativepos += vid.width; } } - else if (*mask == 10) + else if (m >= (pallen-1)) { // shortcut - memcpy target to work while (draw_linestogo--) { - M_Memcpy(w_base+relativepos, e_base+relativepos, draw_rowend-draw_rowstart); + M_Memcpy(w_base+relativepos, (reverse ? s_base : e_base)+relativepos, draw_rowend-draw_rowstart); relativepos += vid.width; } } else { // pointer to transtable that this mask would use - transtbl = transtables + ((9 - *mask)<>FRACBITS; + scanline = y / vid.dupy; + if (scanline & 1) + sine = -sine; + newpix = abs(sine); + + if (sine < 0) + { + M_Memcpy(&tmpscr[(y*vid.width)+newpix], &srcscr[(y*vid.width)], vid.width-newpix); + + // Cleanup edge + while (newpix) + { + tmpscr[(y*vid.width)+newpix] = srcscr[(y*vid.width)]; + newpix--; + } + } + else + { + M_Memcpy(&tmpscr[(y*vid.width)], &srcscr[(y*vid.width) + sine], vid.width-newpix); + + // Cleanup edge + while (newpix) + { + tmpscr[(y*vid.width) + vid.width - newpix] = srcscr[(y*vid.width) + (vid.width-1)]; + newpix--; + } + } + + disStart += (time*8); //the offset into the displacement map, increment each game loop + disStart &= FINEMASK; //clip it to FINEMASK + } +} + /** After setting up the screens you want to wipe, * calling this will do a 'typical' wipe. */ -void F_RunWipe(UINT8 wipetype, boolean drawMenu) +void F_RunWipe(UINT8 wipetype, boolean drawMenu, const char *colormap, boolean reverse, boolean encorewiggle) { #ifdef NOWIPE (void)wipetype; (void)drawMenu; + (void)colormap; + (void)reverse; + (void)encorewiggle; #else tic_t nowtime; UINT8 wipeframe = 0; fademask_t *fmask; - paldiv = FixedDiv(257<buttons & BT_FORWARD) - players[i].kartstuff[k_throwdir] = 1; - else if (cmd->buttons & BT_BACKWARD) - players[i].kartstuff[k_throwdir] = -1; - else - players[i].kartstuff[k_throwdir] = 0; - G_CopyTiccmd(cmd, &netcmds[buf][i], 1); - // Use the leveltime sent in the player's ticcmd to determine control lag cmd->latency = modeattacking ? 0 : min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max } @@ -3041,8 +3046,17 @@ mapthing_t *G_FindRaceStart(INT32 playernum) continue; if (j == i) continue; - if (players[j].score == players[i].score) - num++; + + if (netgame && cv_kartusepwrlv.value) + { + if (clientpowerlevels[j][PWRLV_RACE] == clientpowerlevels[i][PWRLV_RACE]) + num++; + } + else + { + if (players[j].score == players[i].score) + num++; + } } if (num > 1) // found dupes @@ -3050,8 +3064,21 @@ mapthing_t *G_FindRaceStart(INT32 playernum) } else { - if (players[i].score > players[playernum].score || i < playernum) + if (i < playernum) pos++; + else + { + if (netgame && cv_kartusepwrlv.value) + { + if (clientpowerlevels[i][PWRLV_RACE] > clientpowerlevels[playernum][PWRLV_RACE]) + pos++; + } + else + { + if (players[i].score > players[playernum].score) + pos++; + } + } } } @@ -3381,9 +3408,10 @@ boolean G_BattleGametype(void) // INT16 G_SometimesGetDifferentGametype(void) { - boolean encorepossible = (M_SecretUnlocked(SECRET_ENCORE) && G_RaceGametype()); + boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1) && G_RaceGametype()); - if (!cv_kartvoterulechanges.value) // never + if (!cv_kartvoterulechanges.value // never + && encorescramble != 1) // destroying the code for this one instance return gametype; if (randmapbuffer[NUMMAPS] > 0 && (encorepossible || cv_kartvoterulechanges.value != 3)) @@ -3391,25 +3419,33 @@ INT16 G_SometimesGetDifferentGametype(void) randmapbuffer[NUMMAPS]--; if (encorepossible) { - switch (cv_kartvoterulechanges.value) + if (encorescramble != -1) + encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission + else { - case 3: // always - randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set - break; - case 2: // frequent - encorepossible = M_RandomChance(FRACUNIT>>1); - break; - case 1: // sometimes - default: - encorepossible = M_RandomChance(FRACUNIT>>2); - break; + switch (cv_kartvoterulechanges.value) + { + case 3: // always + randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set + break; + case 2: // frequent + encorepossible = M_RandomChance(FRACUNIT>>1); + break; + case 1: // sometimes + default: + encorepossible = M_RandomChance(FRACUNIT>>2); + break; + } } - if (encorepossible != (boolean)cv_kartencore.value) + if (encorepossible != (cv_kartencore.value == 1)) return (gametype|0x80); } return gametype; } + if (!cv_kartvoterulechanges.value) // never (again) + return gametype; + switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv? { case 3: // always @@ -3638,7 +3674,7 @@ tryagain: void G_AddMapToBuffer(INT16 map) { - INT16 bufx, refreshnum = (TOLMaps(G_TOLFlag(gametype)) / 2) + 1; + INT16 bufx, refreshnum = max(0, TOLMaps(G_TOLFlag(gametype))-3); // Add the map to the buffer. for (bufx = NUMMAPS-1; bufx > 0; bufx--) @@ -3662,6 +3698,7 @@ static void G_DoCompleted(void) { INT32 i, j = 0; boolean gottoken = false; + SINT8 powertype = PWRLV_DISABLED; tokenlist = 0; // Reset the list @@ -3800,13 +3837,23 @@ static void G_DoCompleted(void) nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, false, 0, false, NULL); } - // We are committed to this map now. // We may as well allocate its header if it doesn't exist // (That is, if it's a real map) if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) P_AllocMapHeader(nextmap); + // Set up power level gametype scrambles + if (netgame && cv_kartusepwrlv.value) + { + if (G_RaceGametype()) + powertype = PWRLV_RACE; + else if (G_BattleGametype()) + powertype = PWRLV_BATTLE; + } + + K_SetPowerLevelScrambles(powertype); + demointermission: if (skipstats && !modeattacking) // Don't skip stats if we're in record attack @@ -3878,7 +3925,7 @@ void G_NextLevel(void) } forceresetplayers = false; - deferencoremode = (boolean)cv_kartencore.value; + deferencoremode = (cv_kartencore.value == 1); } gameaction = ga_worlddone; @@ -4041,15 +4088,24 @@ void G_LoadGameData(void) // to new gamedata G_ClearRecords(); // main and nights records M_ClearSecrets(); // emblems, unlocks, maps visited, etc + totalplaytime = 0; // total play time (separate from all) matchesplayed = 0; // SRB2Kart: matches played & finished + for (i = 0; i < PWRLV_NUMTYPES; i++) // SRB2Kart: online rank system + vspowerlevel[i] = PWRLVRECORD_START; + if (M_CheckParm("-nodata")) return; // Don't load. // Allow saving of gamedata beyond this point gamedataloaded = true; + if (M_CheckParm("-gamedata") && M_IsNextParm()) + { + strlcpy(gamedatafilename, M_GetNextParm(), sizeof gamedatafilename); + } + if (M_CheckParm("-resetdata")) return; // Don't load (essentially, reset). @@ -4074,6 +4130,13 @@ void G_LoadGameData(void) totalplaytime = READUINT32(save_p); matchesplayed = READUINT32(save_p); + for (i = 0; i < PWRLV_NUMTYPES; i++) + { + vspowerlevel[i] = READUINT16(save_p); + if (vspowerlevel[i] < PWRLVRECORD_MIN || vspowerlevel[i] > PWRLVRECORD_MAX) + goto datacorrupt; + } + modded = READUINT8(save_p); // Aha! Someone's been screwing with the save file! @@ -4219,6 +4282,9 @@ void G_SaveGameData(boolean force) WRITEUINT32(save_p, totalplaytime); WRITEUINT32(save_p, matchesplayed); + for (i = 0; i < PWRLV_NUMTYPES; i++) + WRITEUINT16(save_p, vspowerlevel[i]); + btemp = (UINT8)(savemoddata); // what used to be here was profoundly dunderheaded WRITEUINT8(save_p, btemp); @@ -4731,7 +4797,7 @@ char *G_BuildMapTitle(INT32 mapnum) // DEMO RECORDING // -#define DEMOVERSION 0x0002 +#define DEMOVERSION 0x0003 #define DEMOHEADER "\xF0" "KartReplay" "\x0F" #define DF_GHOST 0x01 // This demo contains ghost data too! @@ -4742,12 +4808,6 @@ char *G_BuildMapTitle(INT32 mapnum) #define DF_ENCORE 0x40 #define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode! -#ifdef DEMO_COMPAT_100 -#define DF_FILELIST 0x08 // This demo contains an extra files list -#define DF_GAMETYPEMASK 0x30 -#define DF_GAMESHIFT 4 -#endif - #define DEMO_SPECTATOR 0x40 // For demos @@ -4889,7 +4949,6 @@ void G_ReadDemoExtraData(void) kartspeed = READUINT8(demo_p); kartweight = READUINT8(demo_p); - if (stricmp(skins[players[p].skin].name, name) != 0) FindClosestSkinForStats(p, kartspeed, kartweight); @@ -5607,16 +5666,9 @@ void G_ConsGhostTic(INT32 playernum) else ghostext[playernum].desyncframes = 0; - if ( -#ifdef DEMO_COMPAT_100 - demo.version != 0x0001 && -#endif - ( - players[playernum].kartstuff[k_itemtype] != ghostext[playernum].kartitem || - players[playernum].kartstuff[k_itemamount] != ghostext[playernum].kartamount || - players[playernum].kartstuff[k_bumper] != ghostext[playernum].kartbumpers - ) - ) + if (players[playernum].kartstuff[k_itemtype] != ghostext[playernum].kartitem + || players[playernum].kartstuff[k_itemamount] != ghostext[playernum].kartamount + || players[playernum].kartstuff[k_bumper] != ghostext[playernum].kartbumpers) { if (demosynced) CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); @@ -5644,10 +5696,6 @@ void G_GhostTicker(void) // Skip normal demo data. UINT8 ziptic = READUINT8(g->p); -#ifdef DEMO_COMPAT_100 - if (g->version != 0x0001) - { -#endif while (ziptic != DW_END) // Get rid of extradata stuff { if (ziptic == 0) // Only support player 0 info for now @@ -5671,9 +5719,6 @@ void G_GhostTicker(void) } ziptic = READUINT8(g->p); // Back to actual ziptic stuff -#ifdef DEMO_COMPAT_100 - } -#endif if (ziptic & ZT_FWD) g->p++; @@ -5693,18 +5738,12 @@ void G_GhostTicker(void) // Grab ghost data. ziptic = READUINT8(g->p); -#ifdef DEMO_COMPAT_100 - if (g->version != 0x0001) - { -#endif if (ziptic == 0xFF) goto skippedghosttic; // Didn't write ghost info this frame else if (ziptic != 0) I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this ziptic = READUINT8(g->p); -#ifdef DEMO_COMPAT_100 - } -#endif + if (ziptic & GZT_XYZ) { g->oldmo.x = READFIXED(g->p); @@ -5845,15 +5884,8 @@ void G_GhostTicker(void) g->p += 12; // kartitem, kartamount, kartbumpers } -#ifdef DEMO_COMPAT_100 - if (g->version != 0x0001) - { -#endif if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here. I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this -#ifdef DEMO_COMPAT_100 - } -#endif skippedghosttic: // Tick ghost colors (Super and Mario Invincibility flashing) @@ -6363,20 +6395,15 @@ void G_BeginRecording(void) switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - demotime_p = demo_p; - WRITEUINT32(demo_p,UINT32_MAX); // time - WRITEUINT32(demo_p,UINT32_MAX); // lap - break; - /*case ATTACKING_NIGHTS: // 2 - demotime_p = demo_p; - WRITEUINT32(demo_p,UINT32_MAX); // time - WRITEUINT32(demo_p,0); // score - break;*/ - default: // 3 - break; + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + demotime_p = demo_p; + WRITEUINT32(demo_p,UINT32_MAX); // time + WRITEUINT32(demo_p,UINT32_MAX); // lap + break; + default: // 3 + break; } WRITEUINT32(demo_p,P_GetInitSeed()); @@ -6416,6 +6443,9 @@ void G_BeginRecording(void) // Score, since Kart uses this to determine where you start on the map WRITEUINT32(demo_p, player->score); + // Power Levels + WRITEUINT16(demo_p, clientpowerlevels[p][G_BattleGametype() ? PWRLV_BATTLE : PWRLV_RACE]); + // Kart speed and weight WRITEUINT8(demo_p, skins[player->skin].kartspeed); WRITEUINT8(demo_p, skins[player->skin].kartweight); @@ -6514,18 +6544,13 @@ void G_SetDemoTime(UINT32 ptime, UINT32 plap) { if (!demo.recording || !demotime_p) return; + if (demoflags & DF_RECORDATTACK) { WRITEUINT32(demotime_p, ptime); WRITEUINT32(demotime_p, plap); demotime_p = NULL; } - /*else if (demoflags & DF_NIGHTSATTACK) - { - WRITEUINT32(demotime_p, ptime); - WRITEUINT32(demotime_p, pscore); - demotime_p = NULL; - }*/ } static void G_LoadDemoExtraFiles(UINT8 **pp) @@ -6768,13 +6793,6 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) case DEMOVERSION: // latest always supported p += 64; // full demo title break; -#ifdef DEMO_COMPAT_100 - case 0x0001: - // Old replays gotta go :] - CONS_Alert(CONS_NOTICE, M_GetText("File '%s' outdated version. It will be overwritten. Nyeheheh.\n"), oldname); - Z_Free(buffer); - return UINT8_MAX; -#endif // too old, cannot support. default: CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); @@ -6866,12 +6884,6 @@ void G_LoadDemoInfo(menudemo_t *pdemo) info_p += 64; break; -#ifdef DEMO_COMPAT_100 - case 0x0001: - pdemo->type = MD_OUTDATED; - sprintf(pdemo->title, "Legacy Replay"); - break; -#endif // too old, cannot support. default: CONS_Alert(CONS_ERROR, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemo->filepath); @@ -6906,16 +6918,6 @@ void G_LoadDemoInfo(menudemo_t *pdemo) Z_Free(infobuffer); return; } -#ifdef DEMO_COMPAT_100 - else if (pdemoversion == 0x0001) - { - CONS_Alert(CONS_ERROR, M_GetText("%s is a legacy multiplayer replay and cannot be played.\n"), pdemo->filepath); - pdemo->type = MD_INVALID; - sprintf(pdemo->title, "INVALID REPLAY"); - Z_Free(infobuffer); - return; - } -#endif pdemo->gametype = READUINT8(info_p); @@ -6925,7 +6927,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo) extrainfo_p = infobuffer + READUINT32(info_p); // Pared down version of CV_LoadNetVars to find the kart speed - pdemo->kartspeed = 1; // Default to normal speed + pdemo->kartspeed = KARTSPEED_NORMAL; // Default to normal speed count = READUINT16(info_p); while (count--) { @@ -7116,10 +7118,6 @@ void G_DoPlayDemo(char *defdemoname) demo_p += 64; break; -#ifdef DEMO_COMPAT_100 - case 0x0001: - break; -#endif // too old, cannot support. default: snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname); @@ -7148,24 +7146,6 @@ void G_DoPlayDemo(char *defdemoname) demo_p += 16; // mapmd5 demoflags = READUINT8(demo_p); -#ifdef DEMO_COMPAT_100 - if (demo.version == 0x0001) - { - if (demoflags & DF_MULTIPLAYER) - { - snprintf(msg, 1024, M_GetText("%s is an alpha multiplayer replay and cannot be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - } - else - { -#endif gametype = READUINT8(demo_p); if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running. @@ -7223,9 +7203,6 @@ void G_DoPlayDemo(char *defdemoname) return; } } -#ifdef DEMO_COMPAT_100 - } -#endif modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT; multiplayer = !!(demoflags & DF_MULTIPLAYER); @@ -7253,109 +7230,20 @@ void G_DoPlayDemo(char *defdemoname) // Random seed randseed = READUINT32(demo_p); -#ifdef DEMO_COMPAT_100 - if (demo.version != 0x0001) -#endif demo_p += 4; // Extrainfo location -#ifdef DEMO_COMPAT_100 - if (demo.version == 0x0001) + // ...*map* not loaded? + if (!gamemap || (gamemap > NUMMAPS) || !mapheaderinfo[gamemap-1] || !(mapheaderinfo[gamemap-1]->menuflags & LF2_EXISTSHACK)) { - // Player name - M_Memcpy(player_names[0],demo_p,16); - demo_p += 16; - - // Skin - M_Memcpy(skin,demo_p,16); - demo_p += 16; - - // Color - M_Memcpy(color,demo_p,16); - demo_p += 16; - - demo_p += 5; // Backwards compat - some stats - // SRB2kart - kartspeed[0] = READUINT8(demo_p); - kartweight[0] = READUINT8(demo_p); - // - demo_p += 9; // Backwards compat - more stats - - // Skin not loaded? - if (!SetPlayerSkin(0, skin)) - { - snprintf(msg, 1024, M_GetText("%s features a character that is not currently loaded.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - - // ...*map* not loaded? - if (!gamemap || (gamemap > NUMMAPS) || !mapheaderinfo[gamemap-1] || !(mapheaderinfo[gamemap-1]->menuflags & LF2_EXISTSHACK)) - { - snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - - // Set color - for (i = 0; i < MAXSKINCOLORS; i++) - if (!stricmp(KartColor_Names[i],color)) // SRB2kart - { - players[0].skincolor = i; - break; - } - - // net var data - CV_LoadNetVars(&demo_p); - - // Sigh ... it's an empty demo. - if (*demo_p == DEMOMARKER) - { - snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - + snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); - - memset(&oldcmd,0,sizeof(oldcmd)); - memset(&oldghost,0,sizeof(oldghost)); - memset(&ghostext,0,sizeof(ghostext)); - - CONS_Alert(CONS_WARNING, M_GetText("Demo version does not match game version. Desyncs may occur.\n")); - - // console warning messages -#if defined(SKIPERRORS) && !defined(DEVELOP) - demosynced = (!skiperrors); -#else - demosynced = true; -#endif - - // didn't start recording right away. - demo.deferstart = false; - - consoleplayer = 0; - memset(displayplayers, 0, sizeof(displayplayers)); - memset(playeringame, 0, sizeof(playeringame)); - playeringame[0] = true; - - goto post_compat; + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; } -#endif // net var data CV_LoadNetVars(&demo_p); @@ -7470,6 +7358,9 @@ void G_DoPlayDemo(char *defdemoname) // Score, since Kart uses this to determine where you start on the map players[p].score = READUINT32(demo_p); + // Power Levels + clientpowerlevels[p][G_BattleGametype() ? PWRLV_BATTLE : PWRLV_RACE] = READUINT16(demo_p); + // Kart stats, temporarily kartspeed[p] = READUINT8(demo_p); kartweight[p] = READUINT8(demo_p); @@ -7494,10 +7385,6 @@ void G_DoPlayDemo(char *defdemoname) R_ExecuteSetViewSize(); -#ifdef DEMO_COMPAT_100 -post_compat: -#endif - P_SetRandSeed(randseed); G_InitNew(demoflags & DF_ENCORE, G_BuildMapName(gamemap), true, true); // Doesn't matter whether you reset or not here, given changes to resetplayer. @@ -7588,10 +7475,6 @@ void G_AddGhost(char *defdemoname) case DEMOVERSION: // latest always supported p += 64; // title break; -#ifdef DEMO_COMPAT_100 - case 0x0001: - break; -#endif // too old, cannot support. default: CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo version incompatible.\n"), pdemoname); @@ -7632,15 +7515,9 @@ void G_AddGhost(char *defdemoname) return; } -#ifdef DEMO_COMPAT_100 - if (ghostversion != 0x0001) -#endif - p++; // gametype + p++; // gametype + G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts. -#ifdef DEMO_COMPAT_100 - if (ghostversion != 0x0001) -#endif - G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts. switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { case ATTACKING_NONE: // 0 @@ -7656,41 +7533,6 @@ void G_AddGhost(char *defdemoname) } p += 4; // random seed - -#ifdef DEMO_COMPAT_100 - if (ghostversion == 0x0001) - { - // Player name (TODO: Display this somehow if it doesn't match cv_playername!) - M_Memcpy(name, p,16); - p += 16; - - // Skin - M_Memcpy(skin, p,16); - p += 16; - - // Color - M_Memcpy(color, p,16); - p += 16; - - // Ghosts do not have a player structure to put this in. - p++; // charability - p++; // charability2 - p++; // actionspd - p++; // mindash - p++; // maxdash - // SRB2kart - p++; // kartspeed - p++; // kartweight - // - p++; // normalspeed - p++; // runspeed - p++; // thrustfactor - p++; // accelstart - p++; // acceleration - p += 4; // jumpfactor - } - else -#endif p += 4; // Extra data location reference // net var data @@ -7710,10 +7552,6 @@ void G_AddGhost(char *defdemoname) return; } -#ifdef DEMO_COMPAT_100 - if (ghostversion != 0x0001) - { -#endif if (READUINT8(p) != 0) { CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); @@ -7735,6 +7573,7 @@ void G_AddGhost(char *defdemoname) p += 16; p += 4; // score + p += 2; // powerlevel kartspeed = READUINT8(p); kartweight = READUINT8(p); @@ -7746,9 +7585,6 @@ void G_AddGhost(char *defdemoname) Z_Free(buffer); return; } -#ifdef DEMO_COMPAT_100 - } -#endif for (i = 0; i < numskins; i++) if (!stricmp(skins[i].name,skin)) @@ -7848,18 +7684,13 @@ void G_UpdateStaffGhostName(lumpnum_t l) ghostversion = READUINT16(p); switch(ghostversion) { - case DEMOVERSION: // latest always supported - p += 64; // full demo title - break; + case DEMOVERSION: // latest always supported + p += 64; // full demo title + break; -#ifdef DEMO_COMPAT_100 - case 0x0001: - break; -#endif - - // too old, cannot support. - default: - goto fail; + // too old, cannot support. + default: + goto fail; } p += 16; // demo checksum @@ -7879,43 +7710,22 @@ void G_UpdateStaffGhostName(lumpnum_t l) goto fail; // we don't NEED to do it here, but whatever } -#ifdef DEMO_COMPAT_100 - if (ghostversion != 0x0001) -#endif p++; // Gametype -#ifdef DEMO_COMPAT_100 - if (ghostversion != 0x0001) -#endif G_SkipDemoExtraFiles(&p); switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - p += 8; // demo time, lap - break; - /*case ATTACKING_NIGHTS: // 2 - p += 8; // demo time left, score - break;*/ - default: // 3 - break; + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + p += 8; // demo time, lap + break; + default: // 3 + break; } p += 4; // random seed - - -#ifdef DEMO_COMPAT_100 - if (ghostversion == 0x0001) - { - // Player name - M_Memcpy(dummystaffname, p,16); - dummystaffname[16] = '\0'; - goto fail; // Not really a failure but whatever - } -#endif - p += 4; // Extrainfo location marker // Ehhhh don't need ghostversion here (?) so I'll reuse the var here @@ -8000,10 +7810,6 @@ void G_DoPlayMetal(void) { case DEMOVERSION: // latest always supported break; -#ifdef DEMO_COMPAT_100 - case 0x0001: - I_Error("You need to implement demo compat here, doofus! %s:%d", __FILE__, __LINE__); -#endif // too old, cannot support. default: CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, format version incompatible.\n")); diff --git a/src/g_game.h b/src/g_game.h index a69f91421..de482fe7f 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -80,7 +80,7 @@ typedef struct menudemo_s { UINT16 map; UINT8 addonstatus; // What do we need to do addon-wise to play this demo? UINT8 gametype; - UINT8 kartspeed; // Add OR DF_ENCORE for encore mode, idk + SINT8 kartspeed; // Add OR DF_ENCORE for encore mode, idk UINT8 numlaps; struct { diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 0d024dc65..6ccaf1bc5 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -39,6 +39,7 @@ #include "../st_stuff.h" #include "../i_system.h" #include "../m_cheat.h" +#include "../r_things.h" // R_GetShadowZ #ifdef ESLOPE #include "../p_slopes.h" #endif @@ -3563,6 +3564,9 @@ static void HWR_Subsector(size_t num) sub->sector->extra_colormap = gr_frontsector->extra_colormap; + //R_PlaneLightOverride(gr_frontsector, false, &floorlightlevel); + //R_PlaneLightOverride(gr_frontsector, true, &ceilinglightlevel); + // render floor ? #ifdef DOPLANES // yeah, easy backface cull! :) @@ -4058,37 +4062,6 @@ static gr_vissprite_t *HWR_NewVisSprite(void) return HWR_GetVisSprite(gr_visspritecount++); } -// Finds a floor through which light does not pass. -static fixed_t HWR_OpaqueFloorAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height) -{ - const sector_t *sec = R_PointInSubsector(x, y)->sector; - fixed_t floorz = sec->floorheight; - - if (sec->ffloors) - { - ffloor_t *rover; - fixed_t delta1, delta2; - const fixed_t thingtop = z + height; - - for (rover = sec->ffloors; rover; rover = rover->next) - { - if (!(rover->flags & FF_EXISTS) - || !(rover->flags & FF_RENDERPLANES) - || rover->flags & FF_TRANSLUCENT - || rover->flags & FF_FOG - || rover->flags & FF_INVERTPLANES) - continue; - - delta1 = z - (*rover->bottomheight + ((*rover->topheight - *rover->bottomheight)/2)); - delta2 = thingtop - (*rover->bottomheight + ((*rover->topheight - *rover->bottomheight)/2)); - if (*rover->topheight > floorz && abs(delta1) < abs(delta2)) - floorz = *rover->topheight; - } - } - - return floorz; -} - // // HWR_DoCulling // Hardware version of R_DoCulling @@ -4129,185 +4102,116 @@ static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float v return false; } -static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float this_scale) +static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) { - FOutVector swallVerts[4]; + GLPatch_t *gpatch; + FOutVector shadowVerts[4]; FSurfaceInfo sSurf; - fixed_t floorheight, mobjfloor; - float offset = 0; + float fscale; float fx; float fy; float offset; + UINT8 lightlevel = 0; + extracolormap_t *colormap = NULL; + UINT8 i; - mobjfloor = HWR_OpaqueFloorAtPos( - spr->mobj->x, spr->mobj->y, - spr->mobj->z, spr->mobj->height); - if (cv_shadowoffs.value) + INT32 light; + fixed_t scalemul; + UINT16 alpha; + fixed_t floordiff; + fixed_t floorz; + fixed_t slopez; + pslope_t *floorslope; + + floorz = R_GetShadowZ(thing, &floorslope); + floordiff = abs(thing->z - floorz); + + alpha = floordiff / (4*FRACUNIT) + 75; + if (alpha >= 255) return; + alpha = 255 - alpha; + + if (thing->whiteshadow) { - angle_t shadowdir; - - // Set direction - if (splitscreen && stplyr == &players[displayplayers[1]]) - shadowdir = localangle[1] + FixedAngle(cv_cam2_rotate.value); - else if (splitscreen > 1 && stplyr == &players[displayplayers[2]]) - shadowdir = localangle[2] + FixedAngle(cv_cam3_rotate.value); - else if (splitscreen > 2 && stplyr == &players[displayplayers[3]]) - shadowdir = localangle[3] + FixedAngle(cv_cam4_rotate.value); - else - shadowdir = localangle[0] + FixedAngle(cv_cam_rotate.value); - - // Find floorheight - floorheight = HWR_OpaqueFloorAtPos( - spr->mobj->x + P_ReturnThrustX(spr->mobj, shadowdir, spr->mobj->z - mobjfloor), - spr->mobj->y + P_ReturnThrustY(spr->mobj, shadowdir, spr->mobj->z - mobjfloor), - spr->mobj->z, spr->mobj->height); - - // The shadow is falling ABOVE it's mobj? - // Don't draw it, then! - if (spr->mobj->z < floorheight) - return; - else - { - fixed_t floorz; - floorz = HWR_OpaqueFloorAtPos( - spr->mobj->x + P_ReturnThrustX(spr->mobj, shadowdir, spr->mobj->z - floorheight), - spr->mobj->y + P_ReturnThrustY(spr->mobj, shadowdir, spr->mobj->z - floorheight), - spr->mobj->z, spr->mobj->height); - // The shadow would be falling on a wall? Don't draw it, then. - // Would draw midair otherwise. - if (floorz < floorheight) - return; - } - - floorheight = FixedInt(spr->mobj->z - floorheight); - - offset = floorheight; + gpatch = (GLPatch_t *)W_CachePatchName("LSHADOW", PU_CACHE); + lightlevel = 255; } else - floorheight = FixedInt(spr->mobj->z - mobjfloor); + { + gpatch = (GLPatch_t *)W_CachePatchName("DSHADOW", PU_CACHE); + lightlevel = 0; + } + + if (!(gpatch && gpatch->mipmap.grInfo.format)) return; + HWR_GetPatch(gpatch); + + scalemul = FixedMul(FRACUNIT - floordiff/640, scale); + scalemul = FixedMul(scalemul, (thing->radius*2) / gpatch->height); + + fscale = FIXED_TO_FLOAT(scalemul); + fx = FIXED_TO_FLOAT(thing->x); + fy = FIXED_TO_FLOAT(thing->y); - // create the sprite billboard - // // 3--2 // | /| // |/ | // 0--1 - // x1/x2 were already scaled in HWR_ProjectSprite - // First match the normal sprite - swallVerts[0].x = swallVerts[3].x = spr->x1; - swallVerts[2].x = swallVerts[1].x = spr->x2; - swallVerts[0].z = swallVerts[3].z = spr->z1; - swallVerts[2].z = swallVerts[1].z = spr->z2; - - if (spr->mobj && fabsf(this_scale - 1.0f) > 1.0E-36f) - { - // Always a pixel above the floor, perfectly flat. - swallVerts[0].y = swallVerts[1].y = swallVerts[2].y = swallVerts[3].y = spr->ty - gpatch->topoffset * this_scale - (floorheight+3); - - // Now transform the TOP vertices along the floor in the direction of the camera - swallVerts[3].x = spr->x1 + ((gpatch->height * this_scale) + offset) * gr_viewcos; - swallVerts[2].x = spr->x2 + ((gpatch->height * this_scale) + offset) * gr_viewcos; - swallVerts[3].z = spr->z1 + ((gpatch->height * this_scale) + offset) * gr_viewsin; - swallVerts[2].z = spr->z2 + ((gpatch->height * this_scale) + offset) * gr_viewsin; - } + if (thing && fabsf(fscale - 1.0f) > 1.0E-36f) + offset = (gpatch->height/2) * fscale; else - { - // Always a pixel above the floor, perfectly flat. - swallVerts[0].y = swallVerts[1].y = swallVerts[2].y = swallVerts[3].y = spr->ty - gpatch->topoffset - (floorheight+3); + offset = (float)(gpatch->height/2); - // Now transform the TOP vertices along the floor in the direction of the camera - swallVerts[3].x = spr->x1 + (gpatch->height + offset) * gr_viewcos; - swallVerts[2].x = spr->x2 + (gpatch->height + offset) * gr_viewcos; - swallVerts[3].z = spr->z1 + (gpatch->height + offset) * gr_viewsin; - swallVerts[2].z = spr->z2 + (gpatch->height + offset) * gr_viewsin; + shadowVerts[2].x = shadowVerts[3].x = fx + offset; + shadowVerts[1].x = shadowVerts[0].x = fx - offset; + shadowVerts[1].z = shadowVerts[2].z = fy - offset; + shadowVerts[0].z = shadowVerts[3].z = fy + offset; + + for (i = 0; i < 4; i++) + { + float oldx = shadowVerts[i].x; + float oldy = shadowVerts[i].z; + shadowVerts[i].x = fx + ((oldx - fx) * gr_viewcos) - ((oldy - fy) * gr_viewsin); + shadowVerts[i].z = fy + ((oldx - fx) * gr_viewsin) + ((oldy - fy) * gr_viewcos); } - // We also need to move the bottom ones away when shadowoffs is on - if (cv_shadowoffs.value) + if (floorslope) { - swallVerts[0].x = spr->x1 + offset * gr_viewcos; - swallVerts[1].x = spr->x2 + offset * gr_viewcos; - swallVerts[0].z = spr->z1 + offset * gr_viewsin; - swallVerts[1].z = spr->z2 + offset * gr_viewsin; - } - - if (spr->flip) - { - swallVerts[0].sow = swallVerts[3].sow = gpatch->max_s; - swallVerts[2].sow = swallVerts[1].sow = 0; - } - else - { - swallVerts[0].sow = swallVerts[3].sow = 0; - swallVerts[2].sow = swallVerts[1].sow = gpatch->max_s; - } - - // flip the texture coords (look familiar?) - if (spr->vflip) - { - swallVerts[3].tow = swallVerts[2].tow = gpatch->max_t; - swallVerts[0].tow = swallVerts[1].tow = 0; - } - else - { - swallVerts[3].tow = swallVerts[2].tow = 0; - swallVerts[0].tow = swallVerts[1].tow = gpatch->max_t; - } - - sSurf.FlatColor.s.red = 0x00; - sSurf.FlatColor.s.blue = 0x00; - sSurf.FlatColor.s.green = 0x00; - - /*if (spr->mobj->frame & FF_TRANSMASK || spr->mobj->flags2 & MF2_SHADOW) - { - sector_t *sector = spr->mobj->subsector->sector; - UINT8 lightlevel = 255; - extracolormap_t *colormap = sector->extra_colormap; - - if (sector->numlights) + for (i = 0; i < 4; i++) { - INT32 light = R_GetPlaneLight(sector, spr->mobj->floorz, false); - - if (!(spr->mobj->frame & FF_FULLBRIGHT)) - { - lightlevel = *sector->lightlist[light].lightlevel; - if (spr->mobj->frame & FF_SEMIBRIGHT) - lightlevel = 128 + (lightlevel>>1); - } - - if (sector->lightlist[light].extra_colormap) - colormap = sector->lightlist[light].extra_colormap; + slopez = P_GetZAt(floorslope, FLOAT_TO_FIXED(shadowVerts[i].x), FLOAT_TO_FIXED(shadowVerts[i].z)); + shadowVerts[i].y = FIXED_TO_FLOAT(slopez) + 0.05f; } - else - { - lightlevel = sector->lightlevel; - - if (sector->extra_colormap) - colormap = sector->extra_colormap; - } - - if (colormap) - sSurf.FlatColor.rgba = HWR_Lighting(lightlevel/2, colormap->rgba, colormap->fadergba, false, true); - else - sSurf.FlatColor.rgba = HWR_Lighting(lightlevel/2, NORMALFOG, FADEFOG, false, true); - }*/ - - // shadow is always half as translucent as the sprite itself - if (!cv_translucency.value) // use default translucency (main sprite won't have any translucency) - sSurf.FlatColor.s.alpha = 0x80; // default - else if (spr->mobj->flags2 & MF2_SHADOW) - sSurf.FlatColor.s.alpha = 0x20; - else if (spr->mobj->frame & FF_TRANSMASK) - { - HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &sSurf); - sSurf.FlatColor.s.alpha /= 2; //cut alpha in half! } else - sSurf.FlatColor.s.alpha = 0x80; // default - - if (sSurf.FlatColor.s.alpha > floorheight/4) { - sSurf.FlatColor.s.alpha = (UINT8)(sSurf.FlatColor.s.alpha - floorheight/4); - HWD.pfnDrawPolygon(&sSurf, swallVerts, 4, PF_Translucent|PF_Modulated|PF_Clip); + for (i = 0; i < 4; i++) + shadowVerts[i].y = FIXED_TO_FLOAT(floorz) + 0.05f; } + + shadowVerts[0].sow = shadowVerts[3].sow = 0; + shadowVerts[2].sow = shadowVerts[1].sow = gpatch->max_s; + + shadowVerts[3].tow = shadowVerts[2].tow = 0; + shadowVerts[0].tow = shadowVerts[1].tow = gpatch->max_t; + + if (thing->subsector->sector->numlights) + { + light = R_GetPlaneLight(thing->subsector->sector, floorz, false); // Always use the light at the top instead of whatever I was doing before + + if (thing->subsector->sector->lightlist[light].extra_colormap) + colormap = thing->subsector->sector->lightlist[light].extra_colormap; + } + else + { + if (thing->subsector->sector->extra_colormap) + colormap = thing->subsector->sector->extra_colormap; + } + + if (colormap) + sSurf.FlatColor.rgba = HWR_Lighting(lightlevel, colormap->rgba, colormap->fadergba, false, false); + else + sSurf.FlatColor.rgba = HWR_Lighting(lightlevel, NORMALFOG, FADEFOG, false, false); + + sSurf.FlatColor.s.alpha = alpha; + + HWD.pfnDrawPolygon(&sSurf, shadowVerts, 4, PF_Translucent|PF_Modulated|PF_Clip); } // This is expecting a pointer to an array containing 4 wallVerts for a sprite @@ -4384,22 +4288,6 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) //Hurdler: 25/04/2000: now support colormap in hardware mode HWR_GetMappedPatch(gpatch, spr->colormap); - // Draw shadow BEFORE sprite - if (cv_shadow.value // Shadows enabled - && (spr->mobj->flags & (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY)) != (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY) // Ceiling scenery have no shadow. - && !(spr->mobj->flags2 & MF2_DEBRIS) // Debris have no corona or shadow. -#ifdef ALAM_LIGHTING - && !(t_lspr[spr->mobj->sprite]->type // Things with dynamic lights have no shadow. - && (!spr->mobj->player || spr->mobj->player->powers[pw_super])) // Except for non-super players. -#endif - && (spr->mobj->z >= spr->mobj->floorz)) // Without this, your shadow shows on the floor, even after you die and fall through the ground. - { - //////////////////// - // SHADOW SPRITE! // - //////////////////// - HWR_DrawSpriteShadow(spr, gpatch, this_scale); - } - baseWallVerts[0].x = baseWallVerts[3].x = spr->x1; baseWallVerts[2].x = baseWallVerts[1].x = spr->x2; baseWallVerts[0].z = baseWallVerts[3].z = spr->z1; @@ -4802,22 +4690,6 @@ static void HWR_DrawSprite(gr_vissprite_t *spr) //Hurdler: 25/04/2000: now support colormap in hardware mode HWR_GetMappedPatch(gpatch, spr->colormap); - // Draw shadow BEFORE sprite - if (cv_shadow.value // Shadows enabled - && (spr->mobj->flags & (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY)) != (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY) // Ceiling scenery have no shadow. - && !(spr->mobj->flags2 & MF2_DEBRIS) // Debris have no corona or shadow. -#ifdef ALAM_LIGHTING - && !(t_lspr[spr->mobj->sprite]->type // Things with dynamic lights have no shadow. - && (!spr->mobj->player || spr->mobj->player->powers[pw_super])) // Except for non-super players. -#endif - && (spr->mobj->z >= spr->mobj->floorz)) // Without this, your shadow shows on the floor, even after you die and fall through the ground. - { - //////////////////// - // SHADOW SPRITE! // - //////////////////// - HWR_DrawSpriteShadow(spr, gpatch, this_scale); - } - // if it has a dispoffset, push it a little towards the camera if (spr->dispoffset) { float co = -gr_viewcos*(0.05f*spr->dispoffset); @@ -5430,6 +5302,12 @@ static void HWR_DrawSprites(void) HWR_DrawPrecipitationSprite(spr); else #endif + { + if (spr->mobj && spr->mobj->shadowscale && cv_shadow.value) + { + HWR_DrawDropShadow(spr->mobj, spr->mobj->shadowscale); + } + if (spr->mobj && spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) { // 8/1/19: Only don't display player models if no default SPR_PLAY is found. @@ -5445,6 +5323,7 @@ static void HWR_DrawSprites(void) else HWR_DrawMD2(spr); } + } } } } @@ -5882,10 +5761,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) // okay, we can't return now... this is a hack, but weather isn't networked, so it should be ok if (!(thing->precipflags & PCF_THUNK)) { - if (thing->precipflags & PCF_RAIN) - P_RainThinker(thing); - else - P_SnowThinker(thing); + P_PrecipThinker(thing); thing->precipflags |= PCF_THUNK; } @@ -5922,13 +5798,15 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) static void HWR_DrawSkyBackground(void) { FOutVector v[4]; + texture_t *tex; angle_t angle; float dimensionmultiply; float aspectratio; float angleturn; + tex = textures[texturetranslation[skytexture]]; HWR_GetTexture(texturetranslation[skytexture]); - aspectratio = (float)vid.width/(float)vid.height; + aspectratio = (float)vid.width / (float)vid.height; //Hurdler: the sky is the only texture who need 4.0f instead of 1.0 // because it's called just after clearing the screen @@ -5952,22 +5830,22 @@ static void HWR_DrawSkyBackground(void) // software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly // The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture - angle = (dup_viewangle + gr_xtoviewangle[0]); - dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f); + angle = -(dup_viewangle + gr_xtoviewangle[0]); + dimensionmultiply = ((float)tex->width/256.0f); if (atransform.mirror) { angle = InvAngle(angle); - dimensionmultiply *= -1; + dimensionmultiply = -dimensionmultiply; } v[0].sow = v[3].sow = ((float) angle / ((ANGLE_90-1)*dimensionmultiply)); - v[2].sow = v[1].sow = (-1.0f/dimensionmultiply)+((float) angle / ((ANGLE_90-1)*dimensionmultiply)); + v[2].sow = v[1].sow = (1.0f/dimensionmultiply)+((float) angle / ((ANGLE_90-1)*dimensionmultiply)); // Y angle = aimingangle; - dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio)); + dimensionmultiply = ((float)tex->height/(128.0f*aspectratio)); if (splitscreen == 1) { diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index d217f4094..21deed84b 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -653,13 +653,12 @@ spritemd2found: // 0.2126 to red // 0.7152 to green // 0.0722 to blue -// (See this same define in k_kart.c!) +// (See this same define in hw_md2.c!) #define SETBRIGHTNESS(brightness,r,g,b) \ - brightness = (UINT8)(((1063*((UINT16)r)/5000) + (3576*((UINT16)g)/5000) + (361*((UINT16)b)/5000)) / 3) + brightness = (UINT8)(((1063*(UINT16)(r))/5000) + ((3576*(UINT16)(g))/5000) + ((361*(UINT16)(b))/5000)) static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, GLMipmap_t *grmip, INT32 skinnum, skincolors_t color) { - UINT8 i; UINT16 w = gpatch->width, h = gpatch->height; UINT32 size = w*h; RGBA_t *image, *blendimage, *cur, blendcolor; @@ -684,102 +683,188 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, image = gpatch->mipmap.grInfo.data; blendimage = blendgpatch->mipmap.grInfo.data; + blendcolor = V_GetColor(0); // initialize - // Average all of the translation's colors + while (size--) { - const UINT8 div = 6; - const UINT8 start = 4; - UINT32 r, g, b; + UINT16 brightness; - blendcolor = V_GetColor(colortranslations[color][start]); - r = (UINT32)(blendcolor.s.red*blendcolor.s.red); - g = (UINT32)(blendcolor.s.green*blendcolor.s.green); - b = (UINT32)(blendcolor.s.blue*blendcolor.s.blue); - - for (i = 1; i < div; i++) - { - RGBA_t nextcolor = V_GetColor(colortranslations[color][start+i]); - r += (UINT32)(nextcolor.s.red*nextcolor.s.red); - g += (UINT32)(nextcolor.s.green*nextcolor.s.green); - b += (UINT32)(nextcolor.s.blue*nextcolor.s.blue); - } - - blendcolor.s.red = (UINT8)(FixedSqrt((r/div)<>FRACBITS); - blendcolor.s.green = (UINT8)(FixedSqrt((g/div)<>FRACBITS); - blendcolor.s.blue = (UINT8)(FixedSqrt((b/div)<>FRACBITS); - } - - // rainbow support, could theoretically support boss ones too - if (skinnum == TC_RAINBOW) - { - while (size--) + // Don't bother with blending the pixel if the alpha of the blend pixel is 0 + if (skinnum == TC_RAINBOW) { if (image->s.alpha == 0 && blendimage->s.alpha == 0) { - // Don't bother with blending the pixel if the alpha of the blend pixel is 0 cur->rgba = image->rgba; + cur++; image++; blendimage++; + continue; } else { - UINT32 tempcolor; - UINT16 imagebright, blendbright, finalbright, colorbright; + UINT16 imagebright, blendbright; SETBRIGHTNESS(imagebright,image->s.red,image->s.green,image->s.blue); SETBRIGHTNESS(blendbright,blendimage->s.red,blendimage->s.green,blendimage->s.blue); // slightly dumb average between the blend image color and base image colour, usually one or the other will be fully opaque anyway - finalbright = (imagebright*(255-blendimage->s.alpha))/255 + (blendbright*blendimage->s.alpha)/255; - SETBRIGHTNESS(colorbright,blendcolor.s.red,blendcolor.s.green,blendcolor.s.blue); - - tempcolor = (finalbright*blendcolor.s.red)/colorbright; - tempcolor = min(255, tempcolor); - cur->s.red = (UINT8)tempcolor; - tempcolor = (finalbright*blendcolor.s.green)/colorbright; - tempcolor = min(255, tempcolor); - cur->s.green = (UINT8)tempcolor; - tempcolor = (finalbright*blendcolor.s.blue)/colorbright; - tempcolor = min(255, tempcolor); - cur->s.blue = (UINT8)tempcolor; - cur->s.alpha = image->s.alpha; + brightness = (imagebright*(255-blendimage->s.alpha))/255 + (blendbright*blendimage->s.alpha)/255; } - - cur++; image++; blendimage++; } - } - else - { - while (size--) + else { if (blendimage->s.alpha == 0) { - // Don't bother with blending the pixel if the alpha of the blend pixel is 0 cur->rgba = image->rgba; + cur++; image++; blendimage++; + continue; } else { - INT32 tempcolor; - INT16 tempmult, tempalpha; - tempalpha = -(abs(blendimage->s.red-127)-127)*2; - if (tempalpha > 255) - tempalpha = 255; - else if (tempalpha < 0) - tempalpha = 0; + SETBRIGHTNESS(brightness,blendimage->s.red,blendimage->s.green,blendimage->s.blue); + } + } - tempmult = (blendimage->s.red-127)*2; - if (tempmult > 255) - tempmult = 255; - else if (tempmult < 0) - tempmult = 0; + // Calculate a sort of "gradient" for the skincolor + // (Me splitting this into a function didn't work, so I had to ruin this entire function's groove...) + { + RGBA_t nextcolor; + UINT8 firsti, secondi, mul; + UINT32 r, g, b; - tempcolor = (image->s.red*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.red)/255)) * blendimage->s.alpha)/255; - cur->s.red = (UINT8)tempcolor; - tempcolor = (image->s.green*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.green)/255)) * blendimage->s.alpha)/255; - cur->s.green = (UINT8)tempcolor; - tempcolor = (image->s.blue*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.blue)/255)) * blendimage->s.alpha)/255; - cur->s.blue = (UINT8)tempcolor; - cur->s.alpha = image->s.alpha; + // Rainbow needs to find the closest match to the textures themselves, instead of matching brightnesses to other colors. + // Ensue horrible mess. + if (skinnum == TC_RAINBOW) + { + UINT16 brightdif = 256; + UINT8 colorbrightnesses[16]; + INT32 compare, m, d; + UINT8 i; + + // Ignore pure white & pitch black + if (brightness > 253 || brightness < 2) + { + cur->rgba = image->rgba; + cur++; image++; blendimage++; + continue; + } + + firsti = 0; + mul = 0; + + for (i = 0; i < 16; i++) + { + RGBA_t tempc = V_GetColor(colortranslations[color][i]); + SETBRIGHTNESS(colorbrightnesses[i], tempc.s.red, tempc.s.green, tempc.s.blue); // store brightnesses for comparison + } + + for (i = 0; i < 16; i++) + { + if (brightness > colorbrightnesses[i]) // don't allow greater matches (because calculating a makeshift gradient for this is already a huge mess as is) + continue; + compare = abs((INT16)(colorbrightnesses[i]) - (INT16)(brightness)); + if (compare < brightdif) + { + brightdif = (UINT16)compare; + firsti = i; // best matching color that's equal brightness or darker + } + } + + secondi = firsti+1; // next color in line + if (secondi == 16) + { + m = (INT16)brightness; // - 0; + d = (INT16)colorbrightnesses[firsti]; // - 0; + } + else + { + m = (INT16)brightness - (INT16)colorbrightnesses[secondi]; + d = (INT16)colorbrightnesses[firsti] - (INT16)colorbrightnesses[secondi]; + } + + if (m >= d) + m = d-1; + + // calculate the "gradient" multiplier based on how close this color is to the one next in line + if (m <= 0 || d <= 0) + mul = 0; + else + mul = 15 - ((m * 16) / d); + } + else + { + // Thankfully, it's normally way more simple. + // Just convert brightness to a skincolor value, use remainder to find the gradient multipler + firsti = ((UINT8)(255-brightness) / 16); + secondi = firsti+1; + mul = ((UINT8)(255-brightness) % 16); } - cur++; image++; blendimage++; + blendcolor = V_GetColor(colortranslations[color][firsti]); + + if (mul > 0 // If it's 0, then we only need the first color. + && colortranslations[color][firsti] != colortranslations[color][secondi]) // Some colors have duplicate colors in a row, so let's just save the process + { + if (secondi == 16) // blend to black + nextcolor = V_GetColor(31); + else + nextcolor = V_GetColor(colortranslations[color][secondi]); + + // Find difference between points + r = (UINT32)(nextcolor.s.red - blendcolor.s.red); + g = (UINT32)(nextcolor.s.green - blendcolor.s.green); + b = (UINT32)(nextcolor.s.blue - blendcolor.s.blue); + + // Find the gradient of the two points + r = ((mul * r) / 16); + g = ((mul * g) / 16); + b = ((mul * b) / 16); + + // Add gradient value to color + blendcolor.s.red += r; + blendcolor.s.green += g; + blendcolor.s.blue += b; + } } + + if (skinnum == TC_RAINBOW) + { + UINT32 tempcolor; + UINT16 colorbright; + + SETBRIGHTNESS(colorbright,blendcolor.s.red,blendcolor.s.green,blendcolor.s.blue); + if (colorbright == 0) + colorbright = 1; // no dividing by 0 please + + tempcolor = (brightness * blendcolor.s.red) / colorbright; + tempcolor = min(255, tempcolor); + cur->s.red = (UINT8)tempcolor; + + tempcolor = (brightness * blendcolor.s.green) / colorbright; + tempcolor = min(255, tempcolor); + cur->s.green = (UINT8)tempcolor; + + tempcolor = (brightness * blendcolor.s.blue) / colorbright; + tempcolor = min(255, tempcolor); + cur->s.blue = (UINT8)tempcolor; + cur->s.alpha = image->s.alpha; + } + else + { + // Color strength depends on image alpha + INT32 tempcolor; + + tempcolor = ((image->s.red * (255-blendimage->s.alpha)) / 255) + ((blendcolor.s.red * blendimage->s.alpha) / 255); + tempcolor = min(255, tempcolor); + cur->s.red = (UINT8)tempcolor; + + tempcolor = ((image->s.green * (255-blendimage->s.alpha)) / 255) + ((blendcolor.s.green * blendimage->s.alpha) / 255); + tempcolor = min(255, tempcolor); + cur->s.green = (UINT8)tempcolor; + + tempcolor = ((image->s.blue * (255-blendimage->s.alpha)) / 255) + ((blendcolor.s.blue * blendimage->s.alpha) / 255); + tempcolor = min(255, tempcolor); + cur->s.blue = (UINT8)tempcolor; + cur->s.alpha = image->s.alpha; + } + + cur++; image++; blendimage++; } return; @@ -1091,11 +1176,11 @@ void HWR_DrawMD2(gr_vissprite_t *spr) #ifdef USE_FTRANSFORM_ANGLEZ // Slope rotation from Kart p.anglez = 0.0f; - if (spr->mobj->standingslope) + if (spr->mobj->modeltilt) { - fixed_t tempz = spr->mobj->standingslope->normal.z; - fixed_t tempy = spr->mobj->standingslope->normal.y; - fixed_t tempx = spr->mobj->standingslope->normal.x; + fixed_t tempz = spr->mobj->modeltilt->normal.z; + fixed_t tempy = spr->mobj->modeltilt->normal.y; + fixed_t tempx = spr->mobj->modeltilt->normal.x; fixed_t tempangle = AngleFixed(R_PointToAngle2(0, 0, FixedSqrt(FixedMul(tempy, tempy) + FixedMul(tempz, tempz)), tempx)); p.anglez = FIXED_TO_FLOAT(tempangle); tempangle = -AngleFixed(R_PointToAngle2(0, 0, tempz, tempy)); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 38324dbd9..1afa133b3 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -3017,7 +3017,7 @@ static void HU_DrawRankings(void) } V_DrawCenteredString(256, 8, 0, "GAME SPEED"); - V_DrawCenteredString(256, 16, hilicol, cv_kartspeed.string); + V_DrawCenteredString(256, 16, hilicol, kartspeed_cons_t[1+gamespeed].strvalue); } // When you play, you quickly see your score because your name is displayed in white. diff --git a/src/info.c b/src/info.c index f4da59d78..9b6ccab8d 100644 --- a/src/info.c +++ b/src/info.c @@ -43,33 +43,34 @@ char sprnames[NUMSPRITES + 1][5] = "DFLM","XMS1","XMS2","XMS3","BSZ1","BSZ2","BSZ3","BSZ4","BSZ5","BSZ6", "BSZ7","BSZ8","STLG","DBAL","RCRY","ARMA","ARMF","ARMB","WIND","MAGN", "ELEM","FORC","PITY","IVSP","SSPK","GOAL","BIRD","BUNY","MOUS","CHIC", - "COWZ","RBRD","SPRY","SPRR","SPRB","YSPR","RSPR","RAIN","SNO1","SPLH", - "SPLA","SMOK","BUBP","BUBO","BUBN","BUBM","POPP","TFOG","SEED","PRTL", - "SCOR","DRWN","TTAG","GFLG","RRNG","RNGB","RNGR","RNGI","RNGA","RNGE", - "RNGS","RNGG","PIKB","PIKR","PIKA","PIKE","PIKS","PIKG","TAUT","TGRE", - "TSCR","COIN","CPRK","GOOM","BGOM","FFWR","FBLL","SHLL","PUMA","HAMM", - "KOOP","BFLM","MAXE","MUS1","MUS2","TOAD","NDRN","SUPE","SUPZ","NDRL", - "NSPK","NBMP","HOOP","NSCR","NPRU","CAPS","SUPT","SPRK","BOM1","BOM2", - "BOM3","BOM4","ROIA","ROIB","ROIC","ROID","ROIE","ROIF","ROIG","ROIH", - "ROII","ROIJ","ROIK","ROIL","ROIM","ROIN","ROIO","ROIP","BBAL","GWLG", - "GWLR","SRBA","SRBB","SRBC","SRBD","SRBE","SRBF","SRBG","SRBH","SRBI", - "SRBJ","SRBK","SRBL","SRBM","SRBN","SRBO", + "COWZ","RBRD","SPVY","SPVR","SPVB","SPVG","SPDY","SPDR","SPDB","SPDG", + "SPHY","SPHR","SPHB","SPHG","RAIN","SNO1","SNO2","SPLH","SPLA","SMOK", + "BUBP","BUBO","BUBN","BUBM","POPP","TFOG","SEED","PRTL","SCOR","DRWN", + "TTAG","GFLG","RRNG","RNGB","RNGR","RNGI","RNGA","RNGE","RNGS","RNGG", + "PIKB","PIKR","PIKA","PIKE","PIKS","PIKG","TAUT","TGRE","TSCR","COIN", + "CPRK","GOOM","BGOM","FFWR","FBLL","SHLL","PUMA","HAMM","KOOP","BFLM", + "MAXE","MUS1","MUS2","TOAD","NDRN","SUPE","SUPZ","NDRL","NSPK","NBMP", + "HOOP","NSCR","NPRU","CAPS","SUPT","SPRK","BOM1","BOM2","BOM3","BOM4", + "ROIA","ROIB","ROIC","ROID","ROIE","ROIF","ROIG","ROIH","ROII","ROIJ", + "ROIK","ROIL","ROIM","ROIN","ROIO","ROIP","BBAL","GWLG","GWLR","SRBA", + "SRBB","SRBC","SRBD","SRBE","SRBF","SRBG","SRBH","SRBI","SRBJ","SRBK", + "SRBL","SRBM","SRBN","SRBO", //SRB2kart Sprites - "SPRG","BSPR","RNDM","RPOP","SGNS","FAST","DSHR","BOST","BOSM","KFRE", - "KINV","KINF","WIPD","DRIF","BDRF","DUST","RSHE","FITM","BANA","ORBN", - "JAWZ","SSMN","KRBM","BHOG","BHBM","SPBM","THNS","SINK","SITR","KBLN", - "DEZL","POKE","AUDI","DECO","DOOD","SNES","GBAS","SPRS","BUZB","CHOM", - "SACO","CRAB","SHAD","BRNG","BUMP","FLEN","CLAS","PSHW","ISTA","ISTB", - "ARRO","ITEM","ITMO","ITMI","ITMN","WANT","PBOM","HIT1","HIT2","HIT3", - "RETI","AIDU","KSPK","LZI1","LZI2","KLIT","FZSM","FZBM","FPRT","SBUS", - "MARB","FUFO","RUST","BLON","VAPE","HTZA","HTZB","SGVA","SGVB","SGVC", - "PGTR","PGF1","PGF2","PGF3","PGBH","DPLR","SPTL","ENM1","GARU","MARR", - "REAP","JITB","CDMO","CDBU","PINE","PPLR","DPPT","AATR","COCO","BDST", - "FROG","CBRA","HOLE","BBRA","EGFG","SMKP","MTYM","THWP","SNOB","ICEB", - "CNDL","DOCH","DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS","ZTCH", - "MKMA","MKMP","RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH","BFRT", - "OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN","FWRK", - "MXCL","RGSP","DRAF","XMS4","XMS5","VIEW" + "RNDM","RPOP","SGNS","FAST","DSHR","BOST","BOSM","KFRE","KINV","KINF", + "WIPD","DRIF","BDRF","DUST","RSHE","FITM","BANA","ORBN","JAWZ","SSMN", + "KRBM","BHOG","BHBM","SPBM","THNS","SINK","SITR","KBLN","DEZL","POKE", + "AUDI","DECO","DOOD","SNES","GBAS","SPRS","BUZB","CHOM","SACO","CRAB", + "BRNG","BUMP","FLEN","CLAS","PSHW","ISTA","ISTB","ARRO","ITEM", + "ITMO","ITMI","ITMN","WANT","PBOM","HIT1","HIT2","HIT3","RETI","AIDU", + "KSPK","LZI1","LZI2","KLIT","FZSM","FZBM","FPRT","SBUS","MARB","FUFO", + "RUST","BLON","VAPE","HTZA","HTZB","SGVA","SGVB","SGVC","PGTR","PGF1", + "PGF2","PGF3","PGBH","DPLR","SPTL","ENM1","GARU","MARR","REAP","JITB", + "CDMO","CDBU","PINE","PPLR","DPPT","AATR","COCO","BDST","FROG","CBRA", + "HOLE","BBRA","EGFG","SMKP","MTYM","THWP","SNOB","ICEB","CNDL","DOCH", + "DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS","ZTCH","MKMA","MKMP", + "RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH","BFRT","OFRT","RFRT", + "PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN","FWRK","MXCL","RGSP", + "DRAF","GRES","OTFG","XMS4","XMS5","VIEW" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -192,7 +193,7 @@ state_t states[NUMSTATES] = {SPR_NULL, 0, 18, {NULL}, 0, 4, S_NULL}, // S_PLAY_ICON3 // Level end sign (uses player sprite) - {SPR_PLAY, 18, 1, {NULL}, 0, 24, S_PLAY_SIGN}, // S_PLAY_SIGN S + {SPR_PLAY, 18|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_PLAY_SIGN}, // S_PLAY_SIGN // Blue Crawla {SPR_POSS, 0, 5, {A_Look}, 0, 0, S_POSS_STND}, // S_POSS_STND @@ -1062,27 +1063,10 @@ state_t states[NUMSTATES] = {SPR_BUBL, 1, 8, {A_BubbleCheck}, 0, 0, S_BUBBLES1}, // S_BUBBLES2 // Level End Sign - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN2}, // S_SIGN1 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN3}, // S_SIGN2 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN4}, // S_SIGN3 - {SPR_SIGN, 3, 1, {NULL}, 0, 0, S_SIGN5}, // S_SIGN4 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN6}, // S_SIGN5 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN7}, // S_SIGN6 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN8}, // S_SIGN7 - {SPR_SIGN, 4, 1, {NULL}, 0, 0, S_SIGN9}, // S_SIGN8 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN10}, // S_SIGN9 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN11}, // S_SIGN10 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN12}, // S_SIGN11 - {SPR_SIGN, 5, 1, {NULL}, 0, 0, S_SIGN13}, // S_SIGN12 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN14}, // S_SIGN13 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN15}, // S_SIGN14 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN16}, // S_SIGN15 - {SPR_SIGN, 6, 1, {NULL}, 0, 0, S_SIGN17}, // S_SIGN16 - {SPR_SIGN, 0, 1, {NULL}, 0, 0, S_SIGN18}, // S_SIGN17 - {SPR_SIGN, 1, 1, {NULL}, 0, 0, S_SIGN19}, // S_SIGN18 - {SPR_SIGN, 2, 1, {NULL}, 0, 0, S_SIGN20}, // S_SIGN19 - {SPR_SIGN, 7, 1, {NULL}, 0, 0, S_SIGN1}, // S_SIGN20 - {SPR_SIGN, 8, -1, {A_SignPlayer}, 0, 0, S_NULL}, // S_SIGN_END + {SPR_SIGN, 0, -1, {NULL}, 0, 0, S_SIGN_POLE}, // S_SIGN_POLE + {SPR_SIGN, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_SIGN_BACK}, // S_SIGN_BACK + {SPR_SIGN, 2|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_SIGN_SIDE}, // S_SIGN_SIDE + {SPR_SIGN, 3|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_SIGN_FACE}, // S_SIGN_FACE // Steam Riser {SPR_STEM, 0, 2, {A_SetSolidSteam}, 0, 0, S_STEAM2}, // S_STEAM1 @@ -1795,55 +1779,91 @@ state_t states[NUMSTATES] = {SPR_RBRD, 1, 4, {A_Chase}, 0, 0, S_RBIRD2}, // S_RBIRD3 // Yellow Spring - {SPR_SPRY, 0, -1, {NULL}, 0, 0, S_NULL}, // S_YELLOWSPRING - {SPR_SPRY, 4, 4, {A_Pain}, 0, 0, S_YELLOWSPRING3}, // S_YELLOWSPRING2 - {SPR_SPRY, 3, 1, {NULL}, 0, 0, S_YELLOWSPRING4}, // S_YELLOWSPRING3 - {SPR_SPRY, 2, 1, {NULL}, 0, 0, S_YELLOWSPRING5}, // S_YELLOWSPRING4 - {SPR_SPRY, 1, 1, {NULL}, 0, 0, S_YELLOWSPRING}, // S_YELLOWSPRING5 + {SPR_SPVY, 0, -1, {NULL}, 0, 0, S_NULL}, // S_YELLOWSPRING1 + {SPR_SPVY, 1, 1, {A_Pain}, 0, 0, S_YELLOWSPRING3}, // S_YELLOWSPRING2 + {SPR_SPVY, 0, 1, {NULL}, 0, 0, S_YELLOWSPRING4}, // S_YELLOWSPRING3 + {SPR_SPVY, 2, 4, {NULL}, 0, 0, S_YELLOWSPRING1}, // S_YELLOWSPRING4 // Red Spring - {SPR_SPRR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_REDSPRING - {SPR_SPRR, 4, 4, {A_Pain}, 0, 0, S_REDSPRING3}, // S_REDSPRING2 - {SPR_SPRR, 3, 1, {NULL}, 0, 0, S_REDSPRING4}, // S_REDSPRING3 - {SPR_SPRR, 2, 1, {NULL}, 0, 0, S_REDSPRING5}, // S_REDSPRING4 - {SPR_SPRR, 1, 1, {NULL}, 0, 0, S_REDSPRING}, // S_REDSPRING5 + {SPR_SPVR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_REDSPRING1 + {SPR_SPVR, 1, 1, {A_Pain}, 0, 0, S_REDSPRING3}, // S_REDSPRING2 + {SPR_SPVR, 0, 1, {NULL}, 0, 0, S_REDSPRING4}, // S_REDSPRING3 + {SPR_SPVR, 2, 4, {NULL}, 0, 0, S_REDSPRING1}, // S_REDSPRING4 // Blue Spring - {SPR_SPRB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLUESPRING - {SPR_SPRB, 4, 4, {A_Pain}, 0, 0, S_BLUESPRING3}, // S_BLUESPRING2 - {SPR_SPRB, 3, 1, {NULL}, 0, 0, S_BLUESPRING4}, // S_BLUESPRING3 - {SPR_SPRB, 2, 1, {NULL}, 0, 0, S_BLUESPRING5}, // S_BLUESPRING4 - {SPR_SPRB, 1, 1, {NULL}, 0, 0, S_BLUESPRING}, // S_BLUESPRING5 + {SPR_SPVB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLUESPRING1 + {SPR_SPVB, 1, 1, {A_Pain}, 0, 0, S_BLUESPRING3}, // S_BLUESPRING2 + {SPR_SPVB, 0, 1, {NULL}, 0, 0, S_BLUESPRING4}, // S_BLUESPRING3 + {SPR_SPVB, 2, 4, {NULL}, 0, 0, S_BLUESPRING1}, // S_BLUESPRING4 + + // Grey Spring + {SPR_SPVG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GREYSPRING1 + {SPR_SPVG, 1, 1, {A_Pain}, 0, 0, S_GREYSPRING3}, // S_GREYSPRING2 + {SPR_SPVG, 0, 1, {NULL}, 0, 0, S_GREYSPRING4}, // S_GREYSPRING3 + {SPR_SPVG, 2, 4, {NULL}, 0, 0, S_GREYSPRING1}, // S_GREYSPRING4 // Yellow Diagonal Spring - {SPR_YSPR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_YDIAG1 - {SPR_YSPR, 1, 1, {A_Pain}, 0, 0, S_YDIAG3}, // S_YDIAG2 - {SPR_YSPR, 2, 1, {NULL}, 0, 0, S_YDIAG4}, // S_YDIAG3 - {SPR_YSPR, 3, 1, {NULL}, 0, 0, S_YDIAG5}, // S_YDIAG4 - {SPR_YSPR, 4, 1, {NULL}, 0, 0, S_YDIAG6}, // S_YDIAG5 - {SPR_YSPR, 3, 1, {NULL}, 0, 0, S_YDIAG7}, // S_YDIAG6 - {SPR_YSPR, 2, 1, {NULL}, 0, 0, S_YDIAG8}, // S_YDIAG7 - {SPR_YSPR, 1, 1, {NULL}, 0, 0, S_YDIAG1}, // S_YDIAG8 + {SPR_SPDY, 0, -1, {NULL}, 0, 0, S_NULL}, // S_YDIAG1 + {SPR_SPDY, 1, 1, {A_Pain}, 0, 0, S_YDIAG3}, // S_YDIAG2 + {SPR_SPDY, 0, 1, {NULL}, 0, 0, S_YDIAG4}, // S_YDIAG3 + {SPR_SPDY, 2, 4, {NULL}, 0, 0, S_YDIAG1}, // S_YDIAG4 // Red Diagonal Spring - {SPR_RSPR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_RDIAG1 - {SPR_RSPR, 1, 1, {A_Pain}, 0, 0, S_RDIAG3}, // S_RDIAG2 - {SPR_RSPR, 2, 1, {NULL}, 0, 0, S_RDIAG4}, // S_RDIAG3 - {SPR_RSPR, 3, 1, {NULL}, 0, 0, S_RDIAG5}, // S_RDIAG4 - {SPR_RSPR, 4, 1, {NULL}, 0, 0, S_RDIAG6}, // S_RDIAG5 - {SPR_RSPR, 3, 1, {NULL}, 0, 0, S_RDIAG7}, // S_RDIAG6 - {SPR_RSPR, 2, 1, {NULL}, 0, 0, S_RDIAG8}, // S_RDIAG7 - {SPR_RSPR, 1, 1, {NULL}, 0, 0, S_RDIAG1}, // S_RDIAG8 + {SPR_SPDR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_RDIAG1 + {SPR_SPDR, 1, 1, {A_Pain}, 0, 0, S_RDIAG3}, // S_RDIAG2 + {SPR_SPDR, 0, 1, {NULL}, 0, 0, S_RDIAG4}, // S_RDIAG3 + {SPR_SPDR, 2, 4, {NULL}, 0, 0, S_RDIAG1}, // S_RDIAG4 + + // Blue Diagonal Spring + {SPR_SPDB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BDIAG1 + {SPR_SPDB, 1, 1, {A_Pain}, 0, 0, S_BDIAG3}, // S_BDIAG2 + {SPR_SPDB, 0, 1, {NULL}, 0, 0, S_BDIAG4}, // S_BDIAG3 + {SPR_SPDB, 2, 4, {NULL}, 0, 0, S_BDIAG1}, // S_BDIAG4 + + // Grey Diagonal Spring + {SPR_SPDG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GDIAG1 + {SPR_SPDG, 1, 1, {A_Pain}, 0, 0, S_GDIAG3}, // S_GDIAG2 + {SPR_SPDG, 0, 1, {NULL}, 0, 0, S_GDIAG4}, // S_GDIAG3 + {SPR_SPDG, 2, 4, {NULL}, 0, 0, S_GDIAG1}, // S_GDIAG4 + + // Yellow Horizontal Spring + {SPR_SPHY, 0, -1, {NULL}, 0, 0, S_NULL}, // S_YHORIZ1 + {SPR_SPHY, 1, 1, {A_Pain}, 0, 0, S_YHORIZ3}, // S_YHORIZ2 + {SPR_SPHY, 0, 1, {NULL}, 0, 0, S_YHORIZ4}, // S_YHORIZ3 + {SPR_SPHY, 2, 4, {NULL}, 0, 0, S_YHORIZ1}, // S_YHORIZ4 + + // Red Horizontal Spring + {SPR_SPHR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_RHORIZ1 + {SPR_SPHR, 1, 1, {A_Pain}, 0, 0, S_RHORIZ3}, // S_RHORIZ2 + {SPR_SPHR, 0, 1, {NULL}, 0, 0, S_RHORIZ4}, // S_RHORIZ3 + {SPR_SPHR, 2, 4, {NULL}, 0, 0, S_RHORIZ1}, // S_RHORIZ4 + + // Blue Horizontal Spring + {SPR_SPHB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BHORIZ1 + {SPR_SPHB, 1, 1, {A_Pain}, 0, 0, S_BHORIZ3}, // S_BHORIZ2 + {SPR_SPHB, 0, 1, {NULL}, 0, 0, S_BHORIZ4}, // S_BHORIZ3 + {SPR_SPHB, 2, 4, {NULL}, 0, 0, S_BHORIZ1}, // S_BHORIZ4 + + // Grey Horizontal Spring + {SPR_SPHG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GHORIZ1 + {SPR_SPHG, 1, 1, {A_Pain}, 0, 0, S_GHORIZ3}, // S_GHORIZ2 + {SPR_SPHG, 0, 1, {NULL}, 0, 0, S_GHORIZ4}, // S_GHORIZ3 + {SPR_SPHG, 2, 4, {NULL}, 0, 0, S_GHORIZ1}, // S_GHORIZ4 // Rain {SPR_RAIN, FF_TRANS50, -1, {NULL}, 0, 0, S_NULL}, // S_RAIN1 - {SPR_RAIN, FF_TRANS50, 1, {NULL}, 0, 0, S_RAIN1}, // S_RAINRETURN + {SPR_NULL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_RAINRETURN // Snowflake {SPR_SNO1, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SNOW1 {SPR_SNO1, 1, -1, {NULL}, 0, 0, S_NULL}, // S_SNOW2 {SPR_SNO1, 2, -1, {NULL}, 0, 0, S_NULL}, // S_SNOW3 + // Blizzard Snowball + {SPR_SNO2, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLIZZARDSNOW1 + {SPR_SNO2, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BLIZZARDSNOW2 + {SPR_SNO2, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BLIZZARDSNOW3 + // Water Splish {SPR_SPLH, FF_TRANS50 , 2, {NULL}, 0, 0, S_SPLISH2}, // S_SPLISH1 {SPR_SPLH, FF_TRANS50|1, 2, {NULL}, 0, 0, S_SPLISH3}, // S_SPLISH2 @@ -1856,9 +1876,9 @@ state_t states[NUMSTATES] = {SPR_SPLH, FF_TRANS50|8, 2, {NULL}, 0, 0, S_NULL}, // S_SPLISH9 // Water Splash - {SPR_SPLA, FF_TRANS50 , 3, {NULL}, 0, 0, S_SPLASH2}, // S_SPLASH1 - {SPR_SPLA, FF_TRANS70|1, 3, {NULL}, 0, 0, S_SPLASH3}, // S_SPLASH2 - {SPR_SPLA, FF_TRANS90|2, 3, {NULL}, 0, 0, S_RAINRETURN}, // S_SPLASH3 + {SPR_SPLA, FF_TRANS50 , 3, {NULL}, 0, 0, S_SPLASH2}, // S_SPLASH1 + {SPR_SPLA, FF_TRANS70|1, 3, {NULL}, 0, 0, S_SPLASH3}, // S_SPLASH2 + {SPR_SPLA, FF_TRANS90|2, 3, {NULL}, 0, 0, S_NULL}, // S_SPLASH3 // Smoke {SPR_SMOK, FF_TRANS50 , 4, {NULL}, 0, 0, S_SMOKE2}, // S_SMOKE1 @@ -2512,23 +2532,6 @@ state_t states[NUMSTATES] = {SPR_SRBO, 0, 2, {A_BuzzFly}, 0, 0, S_SRB1_GENREX2}, // S_SRB1_GENREX2 // SRB2kart - {SPR_SPRG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GRAYSPRING - {SPR_SPRG, 4, 4, {A_Pain}, 0, 0, S_GRAYSPRING3}, // S_GRAYSPRING2 - {SPR_SPRG, 3, 1, {NULL}, 0, 0, S_GRAYSPRING4}, // S_GRAYSPRING3 - {SPR_SPRG, 2, 1, {NULL}, 0, 0, S_GRAYSPRING5}, // S_GRAYSPRING4 - {SPR_SPRG, 1, 1, {NULL}, 0, 0, S_GRAYSPRING}, // S_GRAYSPRING5 - - {SPR_NULL, 0, 1, {A_Pain}, 0, 0, S_INVISIBLE}, // S_INVISSPRING - - {SPR_BSPR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BDIAG1 - {SPR_BSPR, 1, 1, {A_Pain}, 0, 0, S_BDIAG3}, // S_BDIAG2 - {SPR_BSPR, 2, 1, {NULL}, 0, 0, S_BDIAG4}, // S_BDIAG3 - {SPR_BSPR, 3, 1, {NULL}, 0, 0, S_BDIAG5}, // S_BDIAG4 - {SPR_BSPR, 4, 1, {NULL}, 0, 0, S_BDIAG6}, // S_BDIAG5 - {SPR_BSPR, 3, 1, {NULL}, 0, 0, S_BDIAG7}, // S_BDIAG6 - {SPR_BSPR, 2, 1, {NULL}, 0, 0, S_BDIAG8}, // S_BDIAG7 - {SPR_BSPR, 1, 1, {NULL}, 0, 0, S_BDIAG1}, // S_BDIAG8 - {SPR_RNDM, 0|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_RANDOMITEM2}, // S_RANDOMITEM1 {SPR_RNDM, 1|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_RANDOMITEM3}, // S_RANDOMITEM2 {SPR_RNDM, 2|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_RANDOMITEM4}, // S_RANDOMITEM3 @@ -2973,10 +2976,6 @@ state_t states[NUMSTATES] = {SPR_CRAB, 10, -1, {NULL}, 0, 0, S_NULL}, // S_LAMPPOST {SPR_CRAB, 11, -1, {NULL}, 0, 0, S_NULL}, // S_MOSSYTREE - // Fake Shadow - {SPR_SHAD, FF_TRANS50, -1, {NULL}, 0, 0, S_NULL}, // S_SHADOW - {SPR_SHAD, FF_FULLBRIGHT|FF_TRANS50|1, -1, {NULL}, 0, 0, S_NULL}, // S_WHITESHADOW - {SPR_BUMP, FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_BUMP2}, // S_BUMP1 {SPR_BUMP, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_BUMP3}, // S_BUMP2 {SPR_BUMP, FF_FULLBRIGHT|2, 3, {NULL}, 0, 0, S_NULL}, // S_BUMP3 @@ -3437,6 +3436,12 @@ state_t states[NUMSTATES] = {SPR_DRAF, 3, 1, {NULL}, 0, 0, S_DRAFTDUST5}, // S_DRAFTDUST4 {SPR_DRAF, 4, 1, {NULL}, 0, 0, S_NULL}, // S_DRAFTDUST5 + {SPR_GRES, FF_ANIMATE|FF_PAPERSPRITE, -1, {NULL}, 2, 4, S_NULL}, // S_TIREGREASE + + {SPR_OTFG, FF_FULLBRIGHT|FF_TRANS50, TICRATE, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEFOG + {SPR_OTFG, 2|FF_FULLBRIGHT|FF_PAPERSPRITE, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEORB + {SPR_OTFG, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEBEAM + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -4236,8 +4241,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 32, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound + SKINCOLOR_TEA, // painchance + sfx_s3kb1, // painsound S_NULL, // meleestate S_NULL, // missilestate S_XPLD1, // deathstate @@ -4247,7 +4252,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 24*FRACUNIT, // radius 40*FRACUNIT, // height 0, // display offset - 13*FRACUNIT, // mass + 15*FRACUNIT, // mass 0, // damage sfx_None, // activesound MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags @@ -4263,8 +4268,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 32, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound + SKINCOLOR_YELLOW, // painchance + sfx_s3kb1, // painsound S_NULL, // meleestate S_NULL, // missilestate S_XPLD1, // deathstate @@ -4274,7 +4279,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 24*FRACUNIT, // radius 40*FRACUNIT, // height 0, // display offset - 26*FRACUNIT, // mass + 25*FRACUNIT, // mass 0, // damage sfx_None, // activesound MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE, // flags @@ -4681,7 +4686,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 0, // mass 0, // damage - sfx_spring, // activesound + sfx_s3kb1, // activesound MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING, // flags S_EGGMOBILE2_POGO5 // raisestate }, @@ -6058,54 +6063,27 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_BLUESPRING - 552, // doomednum - S_BLUESPRING, // spawnstate - 1000, // spawnhealth - S_BLUESPRING2, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 20*FRACUNIT, // radius - 16*FRACUNIT, // height - 0, // display offset - 14*FRACUNIT, // mass - 0, // damage - sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags - S_BLUESPRING2 // raisestate - }, - { // MT_YELLOWSPRING 550, // doomednum - S_YELLOWSPRING, // spawnstate + S_YELLOWSPRING1,// spawnstate 1000, // spawnhealth S_YELLOWSPRING2,// seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound + SKINCOLOR_YELLOW, // painchance + sfx_s3kb1, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 20*FRACUNIT, // radius - 16*FRACUNIT, // height + 48*FRACUNIT, // radius + 32*FRACUNIT, // height 0, // display offset - 26*FRACUNIT, // mass + 25*FRACUNIT, // mass 0, // damage sfx_None, // activesound MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags @@ -6114,23 +6092,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_REDSPRING 551, // doomednum - S_REDSPRING, // spawnstate + S_REDSPRING1, // spawnstate 1000, // spawnhealth S_REDSPRING2, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound + SKINCOLOR_SALMON, // painchance + sfx_s3kb1, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 20*FRACUNIT, // radius - 16*FRACUNIT, // height + 48*FRACUNIT, // radius + 32*FRACUNIT, // height 0, // display offset 40*FRACUNIT, // mass 0, // damage @@ -6139,8 +6117,62 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_REDSPRING2 // raisestate }, + { // MT_BLUESPRING + 552, // doomednum + S_BLUESPRING1, // spawnstate + 1000, // spawnhealth + S_BLUESPRING2, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + SKINCOLOR_PASTEL, // painchance + sfx_s3kb1, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 64*FRACUNIT, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + S_BLUESPRING2 // raisestate + }, + + { // MT_GREYSPRING + 553, // doomednum + S_GREYSPRING1, // spawnstate + 1000, // spawnhealth + S_GREYSPRING2, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + SKINCOLOR_POPCORN, // painchance + sfx_s3kb1, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 15*FRACUNIT, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + S_GREYSPRING2 // raisestate + }, + { // MT_YELLOWDIAG - 555, // doomednum + 554, // doomednum S_YDIAG1, // spawnstate 1, // spawnhealth S_YDIAG2, // seestate @@ -6148,26 +6180,26 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound + SKINCOLOR_YELLOW, // painchance + sfx_s3kb1, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 16*FRACUNIT, // height + 48*FRACUNIT, // radius + 56*FRACUNIT, // height 0, // display offset - 26*FRACUNIT, // mass - 26*FRACUNIT, // damage + 25*FRACUNIT, // mass + 25*FRACUNIT, // damage sfx_None, // activesound MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags S_YDIAG2 // raisestate }, { // MT_REDDIAG - 556, // doomednum + 555, // doomednum S_RDIAG1, // spawnstate 1, // spawnhealth S_RDIAG2, // seestate @@ -6175,16 +6207,16 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound + SKINCOLOR_SALMON, // painchance + sfx_s3kb1, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 16*FRACUNIT, // radius - 16*FRACUNIT, // height + 48*FRACUNIT, // radius + 56*FRACUNIT, // height 0, // display offset 40*FRACUNIT, // mass 40*FRACUNIT, // damage @@ -6193,6 +6225,168 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_RDIAG2 // raisestate }, + { // MT_BLUEDIAG + 556, // doomednum + S_BDIAG1, // spawnstate + 1, // spawnhealth + S_BDIAG2, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + SKINCOLOR_PASTEL, // painchance + sfx_s3kb1, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 56*FRACUNIT, // height + 0, // display offset + 64*FRACUNIT, // mass + 64*FRACUNIT, // damage + sfx_None, // activesound + MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + S_BDIAG2 // raisestate + }, + + { // MT_GREYDIAG + 557, // doomednum + S_GDIAG1, // spawnstate + 1, // spawnhealth + S_GDIAG2, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + SKINCOLOR_POPCORN, // painchance + sfx_s3kb1, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 56*FRACUNIT, // height + 0, // display offset + 15*FRACUNIT, // mass + 15*FRACUNIT, // damage + sfx_None, // activesound + MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + S_GDIAG2 // raisestate + }, + + { // MT_YELLOWHORIZ + 558, // doomednum + S_YHORIZ1, // spawnstate + 1, // spawnhealth + S_YHORIZ2, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + SKINCOLOR_YELLOW, // painchance + sfx_s3kb1, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 56*FRACUNIT, // height + 0, // display offset + 0, // mass + 45*FRACUNIT, // damage + sfx_None, // activesound + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_YHORIZ2 // raisestate + }, + + { // MT_REDHORIZ + 559, // doomednum + S_RHORIZ1, // spawnstate + 1, // spawnhealth + S_RHORIZ2, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + SKINCOLOR_SALMON, // painchance + sfx_s3kb1, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 56*FRACUNIT, // height + 0, // display offset + 0, // mass + 72*FRACUNIT, // damage + sfx_None, // activesound + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_RHORIZ2 // raisestate + }, + + { // MT_BLUEHORIZ + 560, // doomednum + S_BHORIZ1, // spawnstate + 1, // spawnhealth + S_BHORIZ2, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + SKINCOLOR_PASTEL, // painchance + sfx_s3kb1, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 56*FRACUNIT, // height + 0, // display offset + 0, // mass + 115*FRACUNIT, // damage + sfx_None, // activesound + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_BHORIZ2 // raisestate + }, + + { // MT_GREYHORIZ + 561, // doomednum + S_GHORIZ1, // spawnstate + 1, // spawnhealth + S_GHORIZ2, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + SKINCOLOR_POPCORN, // painchance + sfx_s3kb1, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 56*FRACUNIT, // height + 0, // display offset + 0, // mass + 27*FRACUNIT, // damage + sfx_None, // activesound + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_GHORIZ2 // raisestate + }, + { // MT_BUBBLES 500, // doomednum S_BUBBLES1, // spawnstate @@ -6224,7 +6418,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 501, // doomednum S_INVISIBLE, // spawnstate 1000, // spawnhealth - S_PLAY_SIGN, // seestate + S_NULL, // seestate sfx_s3kb8, // seesound 8, // reactiontime sfx_s3k7e, // attacksound @@ -6238,7 +6432,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 8, // speed 8*FRACUNIT, // radius - 32*FRACUNIT, // height + 48*FRACUNIT, // height 0, // display offset 16, // mass 0, // damage @@ -6247,6 +6441,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SIGN_PIECE + -1, // doomednum + S_INVISIBLE, // 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 + 8*FRACUNIT, // radius + 48*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NOTHINK|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_SPIKEBALL -1, // doomednum S_SPIKEBALL1, // spawnstate @@ -11228,22 +11449,22 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_RAIN1, // spawnstate 1000, // spawnhealth S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime + sfx_rainin, // seesound + 0, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_NULL, // deathstate + S_SPLASH1, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound -72*FRACUNIT, // speed -- -24*FRACUNIT originally, srb2kart x3 (nya) 1*FRACUNIT, // radius 8*FRACUNIT, // height 0, // display offset - 4, // mass + 80, // mass 0, // damage sfx_None, // activesound MF_NOBLOCKMAP, // flags @@ -11256,7 +11477,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound - 8, // reactiontime + 0, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance @@ -11270,8 +11491,35 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 4*FRACUNIT, // radius 4*FRACUNIT, // height 0, // display offset - 4, // mass - 0, // damage + 0, // mass + 2, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_BLIZZARDSNOW + -1, // doomednum + S_BLIZZARDSNOW1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // 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 + -24*FRACUNIT, // speed + 4*FRACUNIT, // radius + 4*FRACUNIT, // height + 0, // display offset + 0, // mass + 2, // damage sfx_None, // activesound MF_NOBLOCKMAP, // flags S_NULL // raisestate @@ -14800,88 +15048,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, // SRB2kart MT's - - { // MT_GRAYSPRING - 553, // doomednum - S_GRAYSPRING, // spawnstate - 100, // spawnhealth - S_GRAYSPRING2, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 20*FRACUNIT, // radius - 16*FRACUNIT, // height - 0, // display offset - 6*FRACUNIT, // mass - 0, // damage - sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags - S_GRAYSPRING2 // raisestate - }, - - { // MT_INVISSPRING - 554, // doomednum - S_INVISIBLE, // spawnstate - 100, // spawnhealth - S_INVISSPRING, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 20*FRACUNIT, // radius - 16*FRACUNIT, // height - 0, // display offset - 6*FRACUNIT, // mass - 0, // damage - sfx_None, // activesound - MF_SOLID|MF_SPRING, // flags - S_INVISSPRING // raisestate - }, - - { // MT_BLUEDIAG - 557, // doomednum - S_BDIAG1, // spawnstate - 1, // spawnhealth - S_BDIAG2, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_spring, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 0, // speed - 16*FRACUNIT, // radius - 16*FRACUNIT, // height - 0, // display offset - 14*FRACUNIT, // mass - 14*FRACUNIT, // damage - sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags - S_BDIAG2 // raisestate - }, - { // MT_RANDOMITEM 2000, // doomednum S_RANDOMITEM1, // spawnstate @@ -17123,33 +17289,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_SHADOW - -1, // doomednum - S_SHADOW, // 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 - 60*FRACUNIT, // speed - 50*FRACUNIT, // radius - 1*FRACUNIT, // height - -1, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags - S_NULL // raisestate - }, - { // MT_BUMP -1, // doomednum S_BUMP1, // spawnstate @@ -20149,6 +20288,114 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_TIREGREASE + -1, // doomednum + S_TIREGREASE, // 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 + 0, // speed + 8<special, 1) == i) - || (P_IsObjectOnRealGround(mo, sec) && GETSECSPECIAL(sec->special, 1) == i)) + if (P_MobjTouchingSectorSpecial(mo, 1, i, true)) return i-1; } @@ -1563,25 +1559,16 @@ static UINT8 K_CheckOffroadCollide(mobj_t *mo, sector_t *sec) */ static void K_UpdateOffroad(player_t *player) { - fixed_t offroad; - sector_t *nextsector = R_PointInSubsector( - player->mo->x + player->mo->momx*2, player->mo->y + player->mo->momy*2)->sector; - UINT8 offroadstrength = K_CheckOffroadCollide(player->mo, nextsector); + fixed_t offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); // If you are in offroad, a timer starts. if (offroadstrength) { - if (K_CheckOffroadCollide(player->mo, player->mo->subsector->sector) && player->kartstuff[k_offroad] == 0) - player->kartstuff[k_offroad] = TICRATE; + if (player->kartstuff[k_offroad] < offroadstrength) + player->kartstuff[k_offroad] += offroadstrength / TICRATE; - if (player->kartstuff[k_offroad] > 0) - { - offroad = (offroadstrength << FRACBITS) / TICRATE; - player->kartstuff[k_offroad] += offroad; - } - - if (player->kartstuff[k_offroad] > (offroadstrength << FRACBITS)) - player->kartstuff[k_offroad] = (offroadstrength << FRACBITS); + if (player->kartstuff[k_offroad] > offroadstrength) + player->kartstuff[k_offroad] = offroadstrength; } else player->kartstuff[k_offroad] = 0; @@ -1625,7 +1612,7 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur cury + (P_RandomRange(-12,12)*mapobjectscale), curz + (P_RandomRange(24,48)*mapobjectscale), MT_SIGNSPARKLE); - P_SetMobjState(band, S_SIGNSPARK1 + (abs(leveltime+offset) % 11)); + P_SetMobjState(band, S_SIGNSPARK1 + (leveltime % 11)); P_SetScale(band, (band->destscale = (3*player->mo->scale)/2)); band->color = colors[c]; band->colorized = true; @@ -2322,6 +2309,9 @@ fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove // ACCELCODE!!!1!11! oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // FixedMul(P_AproxDistance(player->rmomx, player->rmomy), player->mo->scale); + // Don't calculate the acceleration as ever being above top speed + if (oldspeed > p_speed) + oldspeed = p_speed; newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), ORIG_FRICTION); if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust @@ -4344,6 +4334,167 @@ void K_RepairOrbitChain(mobj_t *orbit) } } +// Simplified version of a code bit in P_MobjFloorZ +static fixed_t K_BananaSlopeZ(pslope_t *slope, fixed_t x, fixed_t y, fixed_t radius, boolean ceiling) +{ + fixed_t testx, testy; + + if (slope->d.x < 0) + testx = radius; + else + testx = -radius; + + if (slope->d.y < 0) + testy = radius; + else + testy = -radius; + + if ((slope->zdelta > 0) ^ !!(ceiling)) + { + testx = -testx; + testy = -testy; + } + + testx += x; + testy += y; + + return P_GetZAt(slope, testx, testy); +} + +static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player) +{ + fixed_t newz; + sector_t *sec; +#ifdef ESLOPE + pslope_t *slope = NULL; +#endif + + sec = R_PointInSubsector(x, y)->sector; + + if (flip) + { +#ifdef ESLOPE + if (sec->c_slope) + { + slope = sec->c_slope; + newz = K_BananaSlopeZ(slope, x, y, radius, true); + } + else +#endif + newz = sec->ceilingheight; + } + else + { +#ifdef ESLOPE + if (sec->f_slope) + { + slope = sec->f_slope; + newz = K_BananaSlopeZ(slope, x, y, radius, false); + } + else +#endif + newz = sec->floorheight; + } + + // Check FOFs for a better suited slope + if (sec->ffloors) + { + ffloor_t *rover; + + for (rover = sec->ffloors; rover; rover = rover->next) + { + fixed_t top, bottom; + fixed_t d1, d2; + + if (!(rover->flags & FF_EXISTS)) + continue; + + if ((!(((rover->flags & FF_BLOCKPLAYER && player) + || (rover->flags & FF_BLOCKOTHERS && !player)) + || (rover->flags & FF_QUICKSAND)) + || (rover->flags & FF_SWIMMABLE))) + continue; + +#ifdef ESLOPE + if (*rover->t_slope) + top = K_BananaSlopeZ(*rover->t_slope, x, y, radius, false); + else +#endif + top = *rover->topheight; + +#ifdef ESLOPE + if (*rover->b_slope) + bottom = K_BananaSlopeZ(*rover->b_slope, x, y, radius, true); + else +#endif + bottom = *rover->bottomheight; + + if (flip) + { + if (rover->flags & FF_QUICKSAND) + { + if (z < top && (z + height) > bottom) + { + if (newz > (z + height)) + { + newz = (z + height); + slope = NULL; + } + } + continue; + } + + d1 = (z + height) - (top + ((bottom - top)/2)); + d2 = z - (top + ((bottom - top)/2)); + + if (bottom < newz && abs(d1) < abs(d2)) + { + newz = bottom; +#ifdef ESLOPE + if (*rover->b_slope) + slope = *rover->b_slope; +#endif + } + } + else + { + if (rover->flags & FF_QUICKSAND) + { + if (z < top && (z + height) > bottom) + { + if (newz < z) + { + newz = z; + slope = NULL; + } + } + continue; + } + + d1 = z - (bottom + ((top - bottom)/2)); + d2 = (z + height) - (bottom + ((top - bottom)/2)); + + if (top > newz && abs(d1) < abs(d2)) + { + newz = top; +#ifdef ESLOPE + if (*rover->t_slope) + slope = *rover->t_slope; +#endif + } + } + } + } + +#if 0 + mobj->standingslope = slope; +#endif + +#ifdef HWRENDER + mobj->modeltilt = slope; +#endif +} + // Move the hnext chain! static void K_MoveHeldObjects(player_t *player) { @@ -4528,6 +4679,14 @@ static void K_MoveHeldObjects(player_t *player) if (R_PointToDist2(cur->x, cur->y, targx, targy) > 768*FRACUNIT) P_TeleportMove(cur, targx, targy, cur->z); +#ifdef ESLOPE + if (P_IsObjectOnGround(cur)) + { + K_CalculateBananaSlope(cur, cur->x, cur->y, cur->z, + cur->radius, cur->height, (cur->eflags & MFE_VERTICALFLIP), false); + } +#endif + cur = cur->hnext; } } @@ -4617,6 +4776,9 @@ static void K_MoveHeldObjects(player_t *player) P_TeleportMove(cur, targx, targy, targz); K_FlipFromObject(cur, player->mo); // Update graviflip in real time thanks. +#ifdef HWRENDER + cur->modeltilt = player->mo->modeltilt; +#endif num = (num+1) % 2; cur = cur->hnext; } @@ -5060,6 +5222,29 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (P_IsDisplayPlayer(player)) debtflag->flags2 |= MF2_DONTDRAW; } + + if (player->kartstuff[k_springstars] && (leveltime & 1)) + { + fixed_t randx = P_RandomRange(-40, 40) * player->mo->scale; + fixed_t randy = P_RandomRange(-40, 40) * player->mo->scale; + fixed_t randz = P_RandomRange(0, player->mo->height >> FRACBITS) << FRACBITS; + mobj_t *star = P_SpawnMobj( + player->mo->x + randx, + player->mo->y + randy, + player->mo->z + randz, + MT_KARMAFIREWORK); + + star->color = player->kartstuff[k_springcolor]; + star->flags |= MF_NOGRAVITY; + star->momx = player->mo->momx / 2; + star->momy = player->mo->momy / 2; + star->momz = player->mo->momz / 2; + star->fuse = 12; + star->scale = player->mo->scale; + star->destscale = star->scale / 2; + + player->kartstuff[k_springstars]--; + } } if (player->playerstate == PST_DEAD || player->kartstuff[k_respawn] > 1) // Ensure these are set correctly here @@ -5103,6 +5288,25 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->mo->color = player->skincolor; } } + else if (player->kartstuff[k_killfield]) // You're gonna REALLY diiiiie + { + const INT32 flashtime = 4<<(4-(player->kartstuff[k_killfield]/TICRATE)); + if (player->kartstuff[k_killfield] == 1 || (player->kartstuff[k_killfield] % (flashtime/2) != 0)) + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + else if (player->kartstuff[k_killfield] % flashtime == 0) + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_BYZANTIUM; + } + else + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_RUBY; + } + } else if (player->kartstuff[k_ringboost] && (leveltime & 1)) // ring boosting { player->mo->colorized = true; @@ -5262,6 +5466,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_justbumped]) player->kartstuff[k_justbumped]--; + if (player->kartstuff[k_tiregrease]) + player->kartstuff[k_tiregrease]--; + // This doesn't go in HUD update because it has potential gameplay ramifications if (player->karthud[khud_itemblink] && player->karthud[khud_itemblink]-- <= 0) { @@ -5271,8 +5478,28 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_KartPlayerHUDUpdate(player); - if (G_BattleGametype() && player->kartstuff[k_bumper] > 0) + if (G_BattleGametype() && player->kartstuff[k_bumper] > 0 + && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_squishedtimer] + && !player->kartstuff[k_respawn] && !player->powers[pw_flashing]) + { player->kartstuff[k_wanted]++; + if (battleovertime.enabled >= 10*TICRATE) + { + if (P_AproxDistance(player->mo->x - battleovertime.x, player->mo->y - battleovertime.y) > battleovertime.radius) + { + player->kartstuff[k_killfield]++; + if (player->kartstuff[k_killfield] > 4*TICRATE) + { + K_SpinPlayer(player, NULL, 0, NULL, false); + //player->kartstuff[k_killfield] = 1; + } + } + else if (player->kartstuff[k_killfield] > 0) + player->kartstuff[k_killfield]--; + } + } + else if (player->kartstuff[k_killfield] > 0) + player->kartstuff[k_killfield]--; if (P_IsObjectOnGround(player->mo)) player->kartstuff[k_waterskip] = 0; @@ -5302,17 +5529,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } - // ??? - /* - if (player->kartstuff[k_jmp] > 1 && onground) - { - S_StartSound(player->mo, sfx_spring); - P_DoJump(player, false); - player->mo->momz *= player->kartstuff[k_jmp]; - player->kartstuff[k_jmp] = 0; - } - */ - if (player->kartstuff[k_comebacktimer]) player->kartstuff[k_comebackmode] = 0; @@ -5446,15 +5662,16 @@ static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) return 0; if (player->kartstuff[k_driftend] != 0) - { return -266*player->kartstuff[k_drift]; // Drift has ended and we are tweaking their angle back a bit - } //basedrift = 90*player->kartstuff[k_drift]; // 450 //basedrift = 93*player->kartstuff[k_drift] - driftweight*3*player->kartstuff[k_drift]/10; // 447 - 303 basedrift = 83*player->kartstuff[k_drift] - (driftweight - 14)*player->kartstuff[k_drift]/5; // 415 - 303 driftangle = abs((252 - driftweight)*player->kartstuff[k_drift]/5); + if (player->kartstuff[k_tiregrease] > 0) // Buff drift-steering while in greasemode + basedrift += (basedrift / greasetics) * player->kartstuff[k_tiregrease]; + return basedrift + FixedMul(driftangle, countersteer); } @@ -6146,7 +6363,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) && NO_HYUDORO && !(HOLDING_ITEM || player->kartstuff[k_itemamount] || player->kartstuff[k_itemroulette] - || player->kartstuff[k_growshrinktimer] // Being disabled during Shrink was unintended but people seemed to be okay with it sooo... + || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_rocketsneakertimer] || player->kartstuff[k_eggmanexplode])) player->kartstuff[k_userings] = 1; @@ -6206,6 +6423,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetMobjState(ring, S_FASTRING1); ring->extravalue1 = 1; // Ring use animation timer ring->extravalue2 = 1; // Ring use animation flag + ring->shadowscale = 0; P_SetTarget(&ring->target, player->mo); // user player->kartstuff[k_rings]--; player->kartstuff[k_ringdelay] = 3; @@ -6714,6 +6932,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (onground) { + fixed_t prevfriction = player->mo->friction; + + // Reduce friction after hitting a horizontal spring + if (player->kartstuff[k_tiregrease]) + player->mo->friction += ((FRACUNIT - prevfriction) / greasetics) * player->kartstuff[k_tiregrease]; + // Friction if (!player->kartstuff[k_offroad]) { @@ -6726,9 +6950,20 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // Karma ice physics if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) - { player->mo->friction += 1228; + // Wipeout slowdown + if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow]) + { + if (player->kartstuff[k_offroad]) + player->mo->friction -= 4912; + if (player->kartstuff[k_wipeoutslow] == 1) + player->mo->friction -= 9824; + } + + // Friction was changed, so we must recalculate a bunch of stuff + if (player->mo->friction != prevfriction) + { if (player->mo->friction > FRACUNIT) player->mo->friction = FRACUNIT; if (player->mo->friction < 0) @@ -6739,23 +6974,14 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->mo->movefactor < FRACUNIT) player->mo->movefactor = 19*player->mo->movefactor - 18*FRACUNIT; else - player->mo->movefactor = FRACUNIT; //player->mo->movefactor = ((player->mo->friction - 0xDB34)*(0xA))/0x80; + player->mo->movefactor = FRACUNIT; if (player->mo->movefactor < 32) player->mo->movefactor = 32; } - - // Wipeout slowdown - if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow]) - { - if (player->kartstuff[k_offroad]) - player->mo->friction -= 4912; - if (player->kartstuff[k_wipeoutslow] == 1) - player->mo->friction -= 9824; - } } - K_KartDrift(player, onground); + K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads // Quick Turning // You can't turn your kart when you're not moving. @@ -7229,6 +7455,10 @@ static patch_t *kp_lapanim_emblem[2]; static patch_t *kp_lapanim_hand[3]; static patch_t *kp_yougotem; +static patch_t *kp_itemminimap; + +static patch_t *kp_alagles[10]; +static patch_t *kp_blagles[6]; void K_LoadKartHUDGraphics(void) { @@ -7529,6 +7759,21 @@ void K_LoadKartHUDGraphics(void) } kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); + kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX); + + sprintf(buffer, "ALAGLESx"); + for (i = 0; i < 10; ++i) + { + buffer[7] = '0'+i; + kp_alagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "BLAGLESx"); + for (i = 0; i < 6; ++i) + { + buffer[7] = '0'+i; + kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } } // For the item toggle menu @@ -8501,9 +8746,11 @@ static boolean K_drawKartPositionFaces(void) // void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol) { + static tic_t alagles_timer = 9; INT32 i, rightoffset = 240; const UINT8 *colormap; INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; + int y2; //this function is designed for 9 or less score lines only //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up @@ -8524,15 +8771,39 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I continue; //ignore them. if (netgame // don't draw it offline - && tab[i].num != serverplayer) + && ( tab[i].num != serverplayer || ! server_lagless )) HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); STRBUFCPY(strtime, tab[i].name); + y2 = y; + + if (tab[i].num == 0 && server_lagless) + { + y2 = ( y - 4 ); + + V_DrawScaledPatch(x + 20, y2, 0, kp_blagles[(leveltime / 3) % 6]); + // every 70 tics + if (( leveltime % 70 ) == 0) + { + alagles_timer = 9; + } + if (alagles_timer > 0) + { + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[alagles_timer]); + if (( leveltime % 2 ) == 0) + alagles_timer--; + } + else + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[0]); + + y2 += SHORT (kp_alagles[0]->height) + 1; + } + if (scorelines > 8) - V_DrawThinString(x + 20, y, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); else - V_DrawString(x + 20, y, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); + V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); if (players[tab[i].num].mo->color) { @@ -9054,7 +9325,7 @@ static void K_drawKartPlayerCheck(void) } } -static void K_drawKartMinimapHead(mobj_t *mo, INT32 x, INT32 y, INT32 flags, patch_t *AutomapPic) +static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic) { // amnum xpos & ypos are the icon's speed around the HUD. // The number being divided by is for how fast it moves. @@ -9063,8 +9334,6 @@ static void K_drawKartMinimapHead(mobj_t *mo, INT32 x, INT32 y, INT32 flags, pat // am xpos & ypos are the icon's starting position. Withouht // it, they wouldn't 'spawn' on the top-right side of the HUD. - UINT8 skin = 0; - fixed_t amnumxpos, amnumypos; INT32 amxpos, amypos; @@ -9075,9 +9344,6 @@ static void K_drawKartMinimapHead(mobj_t *mo, INT32 x, INT32 y, INT32 flags, pat fixed_t xoffset, yoffset; fixed_t xscale, yscale, zoom; - if (mo->skin) - skin = ((skin_t*)mo->skin)-skins; - maxx = maxy = INT32_MAX; minx = miny = INT32_MIN; minx = bsp->bbox[0][BOXLEFT]; @@ -9114,39 +9380,23 @@ static void K_drawKartMinimapHead(mobj_t *mo, INT32 x, INT32 y, INT32 flags, pat yscale = FixedDiv(AutomapPic->height, mapheight); zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); - amnumxpos = (FixedMul(mo->x, zoom) - FixedMul(xoffset, zoom)); - amnumypos = -(FixedMul(mo->y, zoom) - FixedMul(yoffset, zoom)); + amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); + amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); if (encoremode) amnumxpos = -amnumxpos; - amxpos = amnumxpos + ((x + AutomapPic->width/2 - (facemmapprefix[skin]->width/2))<height/2 - (facemmapprefix[skin]->height/2))<width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (facemmapprefix[skin]->width/2))<width/2 + (icon->width/2))<color) // 'default' color - V_DrawSciencePatch(amxpos, amypos, flags, facemmapprefix[skin], FRACUNIT); - else - { - UINT8 *colormap; - if (mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, mo->color, GTC_CACHE); - V_DrawFixedPatch(amxpos, amypos, FRACUNIT, flags, facemmapprefix[skin], colormap); - if (mo->player - && ((G_RaceGametype() && mo->player->kartstuff[k_position] == spbplace) - || (G_BattleGametype() && K_IsPlayerWanted(mo->player)))) - { - V_DrawFixedPatch(amxpos - (4<leftoffset); y -= SHORT(AutomapPic->topoffset); + // Draw the super item in Battle + if (G_BattleGametype() && battleovertime.enabled) + { + if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) + { + const INT32 prevsplitflags = splitflags; + splitflags &= ~V_HUDTRANSHALF; + splitflags |= V_HUDTRANS; + colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); + K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); + splitflags = prevsplitflags; + } + } + // initialize for (i = 0; i < 4; i++) localplayers[i] = -1; @@ -9219,7 +9487,20 @@ static void K_drawKartMinimap(void) demoghost *g = ghosts; while (g) { - K_drawKartMinimapHead(g->mo, x, y, splitflags, AutomapPic); + if (g->mo->skin) + skin = ((skin_t*)g->mo->skin)-skins; + else + skin = 0; + if (g->mo->color) + { + if (g->mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE); + } + else + colormap = NULL; + K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); g = g->next; } @@ -9252,7 +9533,7 @@ static void K_drawKartMinimap(void) } } - if (P_IsDisplayPlayer(&players[i])) + if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) { // Draw display players on top of everything else localplayers[numlocalplayers] = i; @@ -9260,7 +9541,26 @@ static void K_drawKartMinimap(void) continue; } - K_drawKartMinimapHead(players[i].mo, x, y, splitflags, AutomapPic); + if (players[i].mo->skin) + skin = ((skin_t*)players[i].mo->skin)-skins; + else + skin = 0; + + if (players[i].mo->color) + { + if (players[i].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE); + } + else + colormap = NULL; + + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + // Target reticule + if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) + || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); } } @@ -9272,7 +9572,28 @@ static void K_drawKartMinimap(void) { if (i == -1) continue; // this doesn't interest us - K_drawKartMinimapHead(players[localplayers[i]].mo, x, y, splitflags, AutomapPic); + + if (players[localplayers[i]].mo->skin) + skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins; + else + skin = 0; + + if (players[localplayers[i]].mo->color) + { + if (players[localplayers[i]].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE); + } + else + colormap = NULL; + + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + + // Target reticule + if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) + || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); } } @@ -9784,7 +10105,7 @@ void K_drawKartFreePlay(UINT32 flashtime) return; V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy - LAPS_Y+3, V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); + LAPS_Y+3, V_HUDTRANS|V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); } static void K_drawDistributionDebugger(void) diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c new file mode 100644 index 000000000..94bdb13a4 --- /dev/null +++ b/src/k_pwrlv.c @@ -0,0 +1,333 @@ +/// \file k_pwrlv.c +/// \brief SRB2Kart Power Levels + +#include "k_pwrlv.h" +#include "d_netcmd.h" +#include "g_game.h" +#include "s_sound.h" +#include "m_random.h" +#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems +#include "p_tick.h" // leveltime + +// Online rankings for the main gametypes. +// This array is saved to the gamedata. +UINT16 vspowerlevel[PWRLV_NUMTYPES]; + +// Client-sided calculations done for Power Levels. +// This is done so that clients will never be able to hack someone else's score over the server. +UINT16 clientpowerlevels[MAXPLAYERS][PWRLV_NUMTYPES]; + +// Which players spec-scummed, and their power level before scumming. +// On race finish, everyone is considered to have "won" against these people. +INT16 nospectategrief[MAXPLAYERS]; + +// Game setting scrambles based on server Power Level +SINT8 speedscramble = -1; +SINT8 encorescramble = -1; + +void K_ClearClientPowerLevels(void) +{ + UINT8 i, j; + for (i = 0; i < MAXPLAYERS; i++) + for (j = 0; j < PWRLV_NUMTYPES; j++) + clientpowerlevels[i][j] = 0; +} + +// Adapted from this: http://wiki.tockdom.com/wiki/Player_Rating +INT16 K_CalculatePowerLevelInc(INT16 diff) +{ + INT16 control[10] = {0,0,0,1,8,50,125,125,125,125}; + fixed_t increment = 0; + fixed_t x; + UINT8 j; + +#define MAXDIFF (PWRLVRECORD_MAX - 1) + if (diff > MAXDIFF) + diff = MAXDIFF; + if (diff < -MAXDIFF) + diff = -MAXDIFF; +#undef MAXDIFF + + x = ((diff-2)<= (2<= (1<> FRACBITS); +} + +INT16 K_CalculatePowerLevelAvg(void) +{ + fixed_t avg = 0; + UINT8 div = 0; + SINT8 t = PWRLV_DISABLED; + UINT8 i; + + if (!netgame || !cv_kartusepwrlv.value) + { + CONS_Debug(DBG_GAMELOGIC, "Not in a netgame, or not using power levels -- no average.\n"); + return 0; // No average. + } + + if (G_RaceGametype()) + t = PWRLV_RACE; + else if (G_BattleGametype()) + t = PWRLV_BATTLE; + + if (t == PWRLV_DISABLED) + { + CONS_Debug(DBG_GAMELOGIC, "Could not set a power level type -- no average.\n"); + return 0; // Hmm?! + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator + || clientpowerlevels[i][t] == 0) // splitscreen player + continue; + + avg += (clientpowerlevels[i][t] << FRACBITS); + div++; + } + + if (!div) + { + CONS_Debug(DBG_GAMELOGIC, "Found no players -- no average.\n"); + return 0; // No average. + } + + avg /= div; + + return (INT16)(avg >> FRACBITS); +} + +// -- K_UpdatePowerLevels could not be moved here due to usage of y_data, unfortunately. -- + +void K_SetPowerLevelScrambles(SINT8 powertype) +{ + switch (powertype) + { + case PWRLV_RACE: + if (cv_kartspeed.value == -1 || cv_kartencore.value == -1) + { + UINT8 speed = KARTSPEED_NORMAL; + boolean encore = false; + INT16 avg = 0, min = 0; + UINT8 i, t = 1; + + avg = K_CalculatePowerLevelAvg(); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator + || clientpowerlevels[i][t] == 0) // splitscreen player + continue; + + if (min == 0 || clientpowerlevels[i][0] < min) + min = clientpowerlevels[i][0]; + } + + CONS_Debug(DBG_GAMELOGIC, "Min: %d, Avg: %d\n", min, avg); + + if (avg == 0 || min == 0) + { + CONS_Debug(DBG_GAMELOGIC, "No average/minimum, no scramblin'.\n"); + speedscramble = encorescramble = -1; + return; + } + + if (min >= 7800) + { + if (avg >= 8200) + t = 5; + else + t = 4; + } + else if (min >= 6800) + { + if (avg >= 7200) + t = 4; + else + t = 3; + } + else if (min >= 5800) + { + if (avg >= 6200) + t = 3; + else + t = 2; + } + else if (min >= 3800) + { + if (avg >= 4200) + t = 2; + else + t = 1; + } +#if 1 + else + t = 1; +#else + else if (min >= 1800) + { + if (avg >= 2200) + t = 1; + else + t = 0; + } + else + t = 0; +#endif + + CONS_Debug(DBG_GAMELOGIC, "Table position: %d\n", t); + + switch (t) + { + case 5: + speed = KARTSPEED_HARD; + encore = true; + break; + case 4: + speed = P_RandomChance((7<>1); + break; + case 3: + speed = P_RandomChance((3<>2); + break; + case 2: + speed = 1; + encore = P_RandomChance(FRACUNIT>>3); + break; + case 1: default: + speed = KARTSPEED_NORMAL; + encore = false; + break; + case 0: + speed = P_RandomChance((3< PWRLVRECORD_MAX) // I mean... we're subtracting... but y'know how it is :V + inc -= ((yourpower + inc) - PWRLVRECORD_MAX); + if (yourpower + inc < PWRLVRECORD_MIN) + inc -= ((yourpower + inc) - PWRLVRECORD_MIN); + + clientpowerlevels[playernum][powertype] += inc; + + if (playernum == consoleplayer) + { + vspowerlevel[powertype] = clientpowerlevels[playernum][powertype]; + if (M_UpdateUnlockablesAndExtraEmblems(true)) + S_StartSound(NULL, sfx_ncitem); + G_SaveGameData(true); // save your punishment! + } +} diff --git a/src/k_pwrlv.h b/src/k_pwrlv.h new file mode 100644 index 000000000..05fbf1c8a --- /dev/null +++ b/src/k_pwrlv.h @@ -0,0 +1,31 @@ +#ifndef __K_PWRLV__ +#define __K_PWRLV__ + +#include "doomtype.h" +#include "doomdef.h" + +#define PWRLV_DISABLED -1 +#define PWRLV_RACE 0 +#define PWRLV_BATTLE 1 +#define PWRLV_NUMTYPES 2 + +#define PWRLVRECORD_START 1000 // 5000? +#define PWRLVRECORD_DEF 5000 +#define PWRLVRECORD_MIN 1 +#define PWRLVRECORD_MAX 9999 + +extern SINT8 speedscramble; +extern SINT8 encorescramble; + +extern UINT16 vspowerlevel[PWRLV_NUMTYPES]; +extern UINT16 clientpowerlevels[MAXPLAYERS][PWRLV_NUMTYPES]; +extern INT16 nospectategrief[MAXPLAYERS]; + +void K_ClearClientPowerLevels(void); +INT16 K_CalculatePowerLevelInc(INT16 diff); +INT16 K_CalculatePowerLevelAvg(void); +//void K_UpdatePowerLevels(void); +void K_SetPowerLevelScrambles(SINT8 powertype); +void K_PlayerForfeit(UINT8 playernum, boolean nopointloss); + +#endif diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 38af4d2e9..3288219cd 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -639,16 +639,6 @@ static int lib_pCheckSolidLava(lua_State *L) return 1; } -static int lib_pSpawnShadowMobj(lua_State *L) -{ - mobj_t *caster = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - NOHUD - if (!caster) - return LUA_ErrInvalid(L, "mobj_t"); - P_SpawnShadowMobj(caster); - return 0; -} - // P_USER //////////// @@ -1313,15 +1303,16 @@ static int lib_pExplodeMissile(lua_State *L) return 0; } -static int lib_pPlayerTouchingSectorSpecial(lua_State *L) +static int lib_pMobjTouchingSectorSpecial(lua_State *L) { - player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); INT32 section = (INT32)luaL_checkinteger(L, 2); INT32 number = (INT32)luaL_checkinteger(L, 3); + boolean touchground = lua_optboolean(L, 4); //HUDSAFE - if (!player) - return LUA_ErrInvalid(L, "player_t"); - LUA_PushUserdata(L, P_PlayerTouchingSectorSpecial(player, section, number), META_SECTOR); + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + LUA_PushUserdata(L, P_MobjTouchingSectorSpecial(mo, section, number, touchground), META_SECTOR); return 1; } @@ -1405,15 +1396,15 @@ static int lib_pIsFlagAtBase(lua_State *L) static int lib_pSetupLevelSky(lua_State *L) { - INT32 skynum = (INT32)luaL_checkinteger(L, 1); + const char *skytexname = luaL_checkstring(L, 1); player_t *user = NULL; NOHUD 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 - P_SetupLevelSky(skynum, true); + P_SetupLevelSky(skytexname, true); else if (P_IsLocalPlayer(user)) - P_SetupLevelSky(skynum, false); + P_SetupLevelSky(skytexname, false); return 0; } @@ -2672,7 +2663,6 @@ static luaL_Reg lib[] = { {"P_InsideANonSolidFFloor",lib_pInsideANonSolidFFloor}, {"P_CheckDeathPitCollide",lib_pCheckDeathPitCollide}, {"P_CheckSolidLava",lib_pCheckSolidLava}, - {"P_SpawnShadowMobj",lib_pSpawnShadowMobj}, // p_user {"P_GetPlayerHeight",lib_pGetPlayerHeight}, @@ -2736,7 +2726,7 @@ static luaL_Reg lib[] = { {"P_SetMobjStateNF",lib_pSetMobjStateNF}, {"P_DoSuperTransformation",lib_pDoSuperTransformation}, {"P_ExplodeMissile",lib_pExplodeMissile}, - {"P_PlayerTouchingSectorSpecial",lib_pPlayerTouchingSectorSpecial}, + {"P_MobjTouchingSectorSpecial",lib_pMobjTouchingSectorSpecial}, {"P_FindSpecialLineFromTag",lib_pFindSpecialLineFromTag}, {"P_SwitchWeather",lib_pSwitchWeather}, {"P_LinedefExecute",lib_pLinedefExecute}, diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 19292b3d6..0522cb737 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -1487,8 +1487,8 @@ static int mapheaderinfo_get(lua_State *L) lua_pushstring(L, header->forcecharacter); else if (fastcmp(field,"weather")) lua_pushinteger(L, header->weather); - else if (fastcmp(field,"skynum")) - lua_pushinteger(L, header->skynum); + else if (fastcmp(field,"skytexture")) + lua_pushstring(L, header->skytexture); else if (fastcmp(field,"skybox_scalex")) lua_pushinteger(L, header->skybox_scalex); else if (fastcmp(field,"skybox_scaley")) diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index dfb344e34..47efd729f 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -85,7 +85,9 @@ enum mobj_e { #ifdef ESLOPE mobj_standingslope, #endif - mobj_colorized + mobj_colorized, + mobj_shadowscale, + mobj_whiteshadow }; static const char *const mobj_opt[] = { @@ -149,6 +151,8 @@ static const char *const mobj_opt[] = { "standingslope", #endif "colorized", + "shadowscale", + "whiteshadow", NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field]) @@ -360,6 +364,12 @@ static int mobj_get(lua_State *L) case mobj_colorized: lua_pushboolean(L, mo->colorized); break; + case mobj_shadowscale: + lua_pushfixed(L, mo->shadowscale); + break; + case mobj_whiteshadow: + lua_pushboolean(L, mo->whiteshadow); + break; default: // extra custom variables in Lua memory lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); @@ -677,6 +687,12 @@ static int mobj_set(lua_State *L) case mobj_colorized: mo->colorized = luaL_checkboolean(L, 3); break; + case mobj_shadowscale: + mo->shadowscale = luaL_checkfixed(L, 3); + break; + case mobj_whiteshadow: + mo->whiteshadow = luaL_checkboolean(L, 3); + break; default: lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/m_anigif.c b/src/m_anigif.c index 4e68819bc..4dfc77cb3 100644 --- a/src/m_anigif.c +++ b/src/m_anigif.c @@ -422,7 +422,7 @@ static void GIF_headwrite(void) WRITEUINT16(p, rheight); // colors, aspect, etc - WRITEUINT8(p, 0xF7); + WRITEUINT8(p, 0xFF); // TRANSPARENTPIXEL WRITEUINT8(p, 0x00); WRITEUINT8(p, 0x00); diff --git a/src/m_cheat.c b/src/m_cheat.c index e7e877ada..c24a8014b 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -577,14 +577,14 @@ void Command_Skynum_f(void) if (COM_Argc() != 2) { - CONS_Printf(M_GetText("skynum : change the sky\n")); - CONS_Printf(M_GetText("Current sky is %d\n"), levelskynum); + CONS_Printf(M_GetText("skynum : change the sky\n")); + CONS_Printf(M_GetText("Current sky is %s\n"), levelskytexture); return; } CONS_Printf(M_GetText("Previewing sky %s...\n"), COM_Argv(1)); - P_SetupLevelSky(atoi(COM_Argv(1)), false); + P_SetupLevelSky(COM_Argv(1), false); } void Command_Weather_f(void) diff --git a/src/m_cond.c b/src/m_cond.c index b777e7d22..b56882c90 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -21,6 +21,7 @@ #include "r_things.h" // numskins //#include "r_draw.h" // R_GetColorByName #include "k_kart.h" // K_GetKartColorByName +#include "k_pwrlv.h" // Map triggers for linedef executors // 32 triggers, one bit each @@ -289,6 +290,8 @@ UINT8 M_CheckCondition(condition_t *cn) return (totalplaytime >= (unsigned)cn->requirement); case UC_MATCHESPLAYED: // Requires any level completed >= x times return (matchesplayed >= (unsigned)cn->requirement); + case UC_POWERLEVEL: // Requires power level >= x on a certain gametype + return (vspowerlevel[cn->extrainfo1] >= (unsigned)cn->requirement); case UC_GAMECLEAR: // Requires game beaten >= x times return (timesBeaten >= (unsigned)cn->requirement); case UC_ALLEMERALDS: // Requires game beaten with all 7 emeralds >= x times diff --git a/src/m_cond.h b/src/m_cond.h index 07e4480d1..c6362ec5d 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -22,28 +22,29 @@ typedef enum { UC_PLAYTIME, // PLAYTIME [tics] UC_MATCHESPLAYED, // SRB2Kart: MATCHESPLAYED [x played] - UC_GAMECLEAR, // GAMECLEAR - UC_ALLEMERALDS, // ALLEMERALDS - //UC_ULTIMATECLEAR, // ULTIMATECLEAR - //UC_OVERALLSCORE, // OVERALLSCORE [score to beat] - UC_OVERALLTIME, // OVERALLTIME [time to beat, tics] - //UC_OVERALLRINGS, // OVERALLRINGS [rings to beat] - UC_MAPVISITED, // MAPVISITED [map number] - UC_MAPBEATEN, // MAPBEATEN [map number] - UC_MAPALLEMERALDS, // MAPALLEMERALDS [map number] - //UC_MAPULTIMATE, // MAPULTIMATE [map number] - //UC_MAPPERFECT, // MAPPERFECT [map number] - //UC_MAPSCORE, // MAPSCORE [map number] [score to beat] - UC_MAPTIME, // MAPTIME [map number] [time to beat, tics] - //UC_MAPRINGS, // MAPRINGS [map number] [rings to beat] - //UC_NIGHTSSCORE, // NIGHTSSCORE [map number] [score to beat] - //UC_NIGHTSTIME, // NIGHTSTIME [map number] [time to beat, tics] - //UC_NIGHTSGRADE, // NIGHTSGRADE [map number] [grade] - UC_TRIGGER, // TRIGGER [trigger number] - UC_TOTALEMBLEMS, // TOTALEMBLEMS [number of emblems] - UC_EMBLEM, // EMBLEM [emblem number] - UC_EXTRAEMBLEM, // EXTRAEMBLEM [extra emblem number] - UC_CONDITIONSET, // CONDITIONSET [condition set number] + UC_POWERLEVEL, // SRB2Kart: POWERLEVEL [power level to reach] [gametype, "0" for race, "1" for battle] + UC_GAMECLEAR, // GAMECLEAR + UC_ALLEMERALDS, // ALLEMERALDS + //UC_ULTIMATECLEAR, // ULTIMATECLEAR + //UC_OVERALLSCORE, // OVERALLSCORE [score to beat] + UC_OVERALLTIME, // OVERALLTIME [time to beat, tics] + //UC_OVERALLRINGS, // OVERALLRINGS [rings to beat] + UC_MAPVISITED, // MAPVISITED [map number] + UC_MAPBEATEN, // MAPBEATEN [map number] + UC_MAPALLEMERALDS, // MAPALLEMERALDS [map number] + //UC_MAPULTIMATE, // MAPULTIMATE [map number] + //UC_MAPPERFECT, // MAPPERFECT [map number] + //UC_MAPSCORE, // MAPSCORE [map number] [score to beat] + UC_MAPTIME, // MAPTIME [map number] [time to beat, tics] + //UC_MAPRINGS, // MAPRINGS [map number] [rings to beat] + //UC_NIGHTSSCORE, // NIGHTSSCORE [map number] [score to beat] + //UC_NIGHTSTIME, // NIGHTSTIME [map number] [time to beat, tics] + //UC_NIGHTSGRADE, // NIGHTSGRADE [map number] [grade] + UC_TRIGGER, // TRIGGER [trigger number] + UC_TOTALEMBLEMS, // TOTALEMBLEMS [number of emblems] + UC_EMBLEM, // EMBLEM [emblem number] + UC_EXTRAEMBLEM, // EXTRAEMBLEM [extra emblem number] + UC_CONDITIONSET, // CONDITIONSET [condition set number] } conditiontype_t; // Condition Set information diff --git a/src/m_menu.c b/src/m_menu.c index 166b115d6..d5084dff2 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -57,6 +57,7 @@ #include "st_stuff.h" #include "i_sound.h" #include "k_kart.h" // SRB2kart +#include "k_pwrlv.h" #include "d_player.h" // KITEM_ constants #include "i_joy.h" // for joystick menu controls @@ -89,6 +90,10 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #define SLIDER_WIDTH (8*SLIDER_RANGE+6) #define SERVERS_PER_PAGE 11 +#if defined (NONET) || defined (TESTERS) +#define NOMENUHOST +#endif + typedef enum { QUITMSG = 0, @@ -241,14 +246,18 @@ static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef; // Multiplayer #ifndef NONET +#ifndef TESTERS static void M_StartServerMenu(INT32 choice); +#endif static void M_ConnectMenu(INT32 choice); static void M_ConnectMenuModChecks(INT32 choice); static void M_Refresh(INT32 choice); static void M_Connect(INT32 choice); static void M_ChooseRoom(INT32 choice); #endif +#ifndef TESTERS static void M_StartOfflineServerMenu(INT32 choice); +#endif static void M_StartServer(INT32 choice); static void M_SetupMultiPlayer(INT32 choice); static void M_SetupMultiPlayer2(INT32 choice); @@ -474,9 +483,14 @@ static menuitem_t MainMenu[] = { {IT_SUBMENU|IT_STRING, NULL, "Extras", &SR_MainDef, 76}, //{IT_CALL |IT_STRING, NULL, "1 Player", M_SinglePlayerMenu, 84}, +#ifdef TESTERS + {IT_GRAYEDOUT, NULL, "Time Attack", NULL, 84}, +#else {IT_CALL |IT_STRING, NULL, "Time Attack", M_TimeAttack, 84}, +#endif {IT_SUBMENU|IT_STRING, NULL, "Multiplayer", &MP_MainDef, 92}, {IT_CALL |IT_STRING, NULL, "Options", M_Options, 100}, + /* I don't think is useful at all... */ {IT_CALL |IT_STRING, NULL, "Addons", M_Addons, 108}, {IT_CALL |IT_STRING, NULL, "Quit Game", M_QuitSRB2, 116}, }; @@ -727,7 +741,9 @@ static menuitem_t SR_PandorasBox[] = // Sky Room Custom Unlocks static menuitem_t SR_MainMenu[] = { +#ifndef TESTERS {IT_STRING|IT_SUBMENU, NULL, "Unlockables", &SR_UnlockChecklistDef, 100}, +#endif {IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 108}, {IT_CALL|IT_STRING, NULL, "Replay Hut", M_ReplayHut, 116}, {IT_DISABLED, NULL, "", NULL, 0}, // Custom1 @@ -961,12 +977,16 @@ static menuitem_t MP_MainMenu[] = {IT_STRING|IT_KEYHANDLER,NULL, "Player setup...", M_SetupMultiHandler,18}, {IT_HEADER, NULL, "Host a game", NULL, 100-24}, -#ifndef NONET +#ifndef NOMENUHOST {IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 110-24}, #else {IT_GRAYEDOUT, NULL, "Internet/LAN...", NULL, 110-24}, #endif +#ifdef TESTERS + {IT_GRAYEDOUT, NULL, "Offline...", NULL, 118-24}, +#else {IT_STRING|IT_CALL, NULL, "Offline...", M_StartOfflineServerMenu, 118-24}, +#endif {IT_HEADER, NULL, "Join a game", NULL, 132-24}, #ifndef NONET @@ -1482,8 +1502,7 @@ static menuitem_t OP_GameOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Starting Bumpers", &cv_kartbumpers, 110}, {IT_STRING | IT_CVAR, NULL, "Karma Comeback", &cv_kartcomeback, 120}, - {IT_STRING | IT_CVAR, NULL, "Force Character", &cv_forceskin, 140}, - {IT_STRING | IT_CVAR, NULL, "Restrict Character Changes", &cv_restrictskinchange, 150}, + {IT_STRING | IT_CVAR, NULL, "Track Power Levels", &cv_kartusepwrlv, 140}, }; static menuitem_t OP_ServerOptionsMenu[] = @@ -3029,7 +3048,11 @@ void M_StartControlPanel(void) //MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); currentMenu = &MainDef; +#ifdef TESTERS + itemOn = multiplr; +#else itemOn = singleplr; +#endif } else if (modeattacking) { @@ -4092,6 +4115,14 @@ static void M_DrawCenteredMenu(void) W_CachePatchName(currentMenu->menuitems[i].patch,PU_CACHE), graymap); y += LINEHEIGHT; break; + case IT_TRANSTEXT: + if (currentMenu->menuitems[i].alphaKey) + y = currentMenu->y+currentMenu->menuitems[i].alphaKey; + /* FALLTHRU */ + case IT_TRANSTEXT2: + V_DrawCenteredString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text); + y += SMALLLINEHEIGHT; + break; } } @@ -5381,7 +5412,7 @@ static void DrawReplayHutReplayInfo(void) V_DrawThinString(x, y+9, V_SNAPTOTOP|V_ALLOWLOWERCASE, va("(%d laps)", demolist[dir_on[menudepthleft]].numlaps)); V_DrawString(x, y+20, V_SNAPTOTOP|V_ALLOWLOWERCASE, demolist[dir_on[menudepthleft]].gametype == GT_RACE ? - va("Race (%s speed)", kartspeed_cons_t[demolist[dir_on[menudepthleft]].kartspeed & ~DF_ENCORE].strvalue) : + va("Race (%s speed)", kartspeed_cons_t[(demolist[dir_on[menudepthleft]].kartspeed & ~DF_ENCORE) + 1].strvalue) : "Battle Mode"); if (!demolist[dir_on[menudepthleft]].standings[0].ranking) @@ -6210,6 +6241,8 @@ static char *M_GetConditionString(condition_t cond) G_TicsToSeconds(cond.requirement)); case UC_MATCHESPLAYED: return va("Play %d matches", cond.requirement); + case UC_POWERLEVEL: + return va("Reach power level %d in %s", cond.requirement, (cond.extrainfo1 == PWRLV_BATTLE ? "Battle" : "Race")); case UC_GAMECLEAR: if (cond.requirement > 1) return va("Beat game %d times", cond.requirement); @@ -7294,7 +7327,7 @@ static void M_Statistics(INT32 choice) static void M_DrawStatsMaps(int location) { - INT32 y = 80, i = -1; + INT32 y = 88, i = -1; INT16 mnum; extraemblem_t *exemblem; boolean dotopname = true, dobottomarrow = (location < statsMax); @@ -7403,9 +7436,14 @@ static void M_DrawLevelStats(void) G_TicsToHours(totalplaytime), G_TicsToMinutes(totalplaytime, false), G_TicsToSeconds(totalplaytime))); + V_DrawString(20, 42, highlightflags, "Total Matches:"); V_DrawRightAlignedString(BASEVIDWIDTH-16, 42, 0, va("%i played", matchesplayed)); + V_DrawString(20, 52, highlightflags, "Online Power Level:"); + V_DrawRightAlignedString(BASEVIDWIDTH-16, 52, 0, va("Race: %i", vspowerlevel[PWRLV_RACE])); + V_DrawRightAlignedString(BASEVIDWIDTH-16, 60, 0, va("Battle: %i", vspowerlevel[PWRLV_BATTLE])); + for (i = 0; i < NUMMAPS; i++) { if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK)) @@ -7420,18 +7458,18 @@ static void M_DrawLevelStats(void) besttime += mainrecords[i]->time; } - V_DrawString(20, 62, highlightflags, "Combined time records:"); + V_DrawString(20, 70, highlightflags, "Combined time records:"); sprintf(beststr, "%i:%02i:%02i.%02i", G_TicsToHours(besttime), G_TicsToMinutes(besttime, false), G_TicsToSeconds(besttime), G_TicsToCentiseconds(besttime)); - V_DrawRightAlignedString(BASEVIDWIDTH-16, 62, (mapsunfinished ? warningflags : 0), beststr); + V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, (mapsunfinished ? warningflags : 0), beststr); if (mapsunfinished) - V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, warningflags, va("(%d unfinished)", mapsunfinished)); + V_DrawRightAlignedString(BASEVIDWIDTH-16, 78, warningflags, va("(%d unfinished)", mapsunfinished)); else - V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, recommendedflags, "(complete)"); + V_DrawRightAlignedString(BASEVIDWIDTH-16, 78, recommendedflags, "(complete)"); - V_DrawString(32, 70, 0, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems)); - V_DrawSmallScaledPatch(20, 70, 0, W_CachePatchName("GOTITA", PU_STATIC)); + V_DrawString(32, 78, V_ALLOWLOWERCASE, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems)); + V_DrawSmallScaledPatch(20, 78, 0, W_CachePatchName("GOTITA", PU_STATIC)); M_DrawStatsMaps(statsLocation); } @@ -8261,6 +8299,7 @@ static void M_DrawConnectMenu(void) UINT16 i; const char *gt = "Unknown"; const char *spd = ""; + const char *pwr = "----"; INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE; for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++) @@ -8295,33 +8334,38 @@ static void M_DrawConnectMenu(void) V_DrawString(currentMenu->x, S_LINEY(i), globalflags, serverlist[slindex].info.servername); - // Don't use color flags intentionally, the global yellow color will auto override the text color code - if (serverlist[slindex].info.modifiedgame) - V_DrawSmallString(currentMenu->x+202, S_LINEY(i)+8, globalflags, "\x85" "Mod"); - if (serverlist[slindex].info.cheatsenabled) - V_DrawSmallString(currentMenu->x+222, S_LINEY(i)+8, globalflags, "\x83" "Cheats"); + if (serverlist[slindex].info.kartvars & SV_PASSWORD) + V_DrawFixedPatch((currentMenu->x - 9) << FRACBITS, (S_LINEY(i)) << FRACBITS, FRACUNIT, globalflags & (~V_ALLOWLOWERCASE), W_CachePatchName("SERVLOCK", PU_CACHE), NULL); V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags, va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time))); + V_DrawSmallString(currentMenu->x+44,S_LINEY(i)+8, globalflags, + va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer)); + gt = "Unknown"; if (serverlist[slindex].info.gametype < NUMGAMETYPES) gt = Gametype_Names[serverlist[slindex].info.gametype]; - - V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags, - va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer)); - - V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, gt); + V_DrawSmallString(currentMenu->x+108, S_LINEY(i)+8, globalflags, gt); if (serverlist[slindex].info.gametype == GT_RACE) { - spd = kartspeed_cons_t[serverlist[slindex].info.kartvars & SV_SPEEDMASK].strvalue; - - V_DrawSmallString(currentMenu->x+132, S_LINEY(i)+8, globalflags, va("(%s Speed)", spd)); + spd = kartspeed_cons_t[(serverlist[slindex].info.kartvars & SV_SPEEDMASK)+1].strvalue; + V_DrawSmallString(currentMenu->x+128, S_LINEY(i)+8, globalflags, va("(%s)", spd)); } - if (serverlist[slindex].info.kartvars & SV_PASSWORD) - V_DrawFixedPatch((currentMenu->x - 9) << FRACBITS, (S_LINEY(i)) << FRACBITS, FRACUNIT, globalflags & (~V_ALLOWLOWERCASE), W_CachePatchName("SERVLOCK", PU_CACHE), NULL); + pwr = "----"; + if (serverlist[slindex].info.avgpwrlv == -1) + pwr = "Off"; + else if (serverlist[slindex].info.avgpwrlv > 0) + pwr = va("%04d", serverlist[slindex].info.avgpwrlv); + V_DrawSmallString(currentMenu->x+171, S_LINEY(i)+8, globalflags, va("Power Level: %s", pwr)); + + // Don't use color flags intentionally, the global yellow color will auto override the text color code + if (serverlist[slindex].info.modifiedgame) + V_DrawSmallString(currentMenu->x+245, S_LINEY(i)+8, globalflags, "\x85" "Mod"); + if (serverlist[slindex].info.cheatsenabled) + V_DrawSmallString(currentMenu->x+265, S_LINEY(i)+8, globalflags, "\x83" "Cheats"); MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL; } @@ -8600,11 +8644,11 @@ static void M_StartServer(INT32 choice) paused = false; SV_StartSinglePlayerServer(); multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... - D_MapChange(cv_nextmap.value, cv_newgametype.value, (boolean)cv_kartencore.value, 1, 1, false, false); + D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false); } else { - D_MapChange(cv_nextmap.value, cv_newgametype.value, (boolean)cv_kartencore.value, 1, 1, false, false); + D_MapChange(cv_nextmap.value, cv_newgametype.value, (cv_kartencore.value == 1), 1, 1, false, false); COM_BufAddText("dummyconsvar 1\n"); } @@ -8641,7 +8685,7 @@ static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade) V_DrawFill(x-1, y-1, w+2, i+2, trans); // variable reuse... - if (!cv_kartencore.value || gamestate == GS_TIMEATTACK || cv_newgametype.value != GT_RACE) + if ((cv_kartencore.value != 1) || gamestate == GS_TIMEATTACK || cv_newgametype.value != GT_RACE) V_DrawSmallScaledPatch(x, y, 0, PictureOfLevel); else { @@ -8774,6 +8818,7 @@ static void M_MapChange(INT32 choice) M_SetupNextMenu(&MISC_ChangeLevelDef); } +#ifndef TESTERS static void M_StartOfflineServerMenu(INT32 choice) { (void)choice; @@ -8781,8 +8826,10 @@ static void M_StartOfflineServerMenu(INT32 choice) M_PrepareLevelSelect(); M_SetupNextMenu(&MP_OfflineServerDef); } +#endif #ifndef NONET +#ifndef TESTERS static void M_StartServerMenu(INT32 choice) { (void)choice; @@ -8792,6 +8839,7 @@ static void M_StartServerMenu(INT32 choice) M_SetupNextMenu(&MP_ServerDef); } +#endif // ============== // CONNECT VIA IP @@ -8811,7 +8859,7 @@ static void M_DrawMPMainMenu(void) // use generic drawer for cursor, items and title M_DrawGenericMenu(); -#ifndef NONET +#ifndef NOMENUHOST #if MAXPLAYERS != 16 Update the maxplayers label... #endif @@ -8819,10 +8867,12 @@ Update the maxplayers label... ((itemOn == 4) ? highlightflags : 0), "(2-16 players)"); #endif +#ifndef TESTERS V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[5].alphaKey, ((itemOn == 5) ? highlightflags : 0), "(2-4 players)" ); +#endif #ifndef NONET y += MP_MainMenu[8].alphaKey; @@ -9583,6 +9633,8 @@ static UINT8 erasecontext = 0; static void M_EraseDataResponse(INT32 ch) { + UINT8 i; + if (ch != 'y' && ch != KEY_ENTER) return; @@ -9594,6 +9646,8 @@ static void M_EraseDataResponse(INT32 ch) // SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets totalplaytime = 0; matchesplayed = 0; + for (i = 0; i < PWRLV_NUMTYPES; i++) + vspowerlevel[i] = PWRLVRECORD_START; F_StartIntro(); } if (erasecontext != 1) diff --git a/src/p_enemy.c b/src/p_enemy.c index e1a928b86..96423f6da 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -118,7 +118,6 @@ void A_ThrownRing(mobj_t *actor); void A_GrenadeRing(mobj_t *actor); void A_SetSolidSteam(mobj_t *actor); void A_UnsetSolidSteam(mobj_t *actor); -void A_SignPlayer(mobj_t *actor); void A_OverlayThink(mobj_t *actor); void A_JetChase(mobj_t *actor); void A_JetbThink(mobj_t *actor); @@ -3559,8 +3558,6 @@ void A_BubbleCheck(mobj_t *actor) // void A_AttractChase(mobj_t *actor) { - fixed_t z; - #ifdef HAVE_BLUA if (LUA_CallAction("A_AttractChase", actor)) return; @@ -3604,12 +3601,11 @@ void A_AttractChase(mobj_t *actor) { fixed_t offz = FixedMul(80*actor->target->scale, FINESINE(FixedAngle((90 - (9 * abs(10 - actor->extravalue1))) << FRACBITS) >> ANGLETOFINESHIFT)); //P_SetScale(actor, (actor->destscale = actor->target->scale)); - z = actor->target->z; - if (( actor->eflags & MFE_VERTICALFLIP )) - z -= actor->height + offz; - else - z += actor->target->height + offz; - P_TeleportMove(actor, actor->target->x, actor->target->y, z); + actor->z = actor->target->z; + K_MatchGenericExtraFlags(actor, actor->target); + P_TeleportMove(actor, actor->target->x, actor->target->y, + actor->z + + ( actor->target->height + offz )* P_MobjFlip(actor)); actor->extravalue1++; } } @@ -3636,15 +3632,12 @@ void A_AttractChase(mobj_t *actor) fixed_t dist = (actor->target->radius/4) * (16 - actor->extravalue1); P_SetScale(actor, (actor->destscale = actor->target->scale - ((actor->target->scale/14) * actor->extravalue1))); - z = actor->target->z; - if (( actor->eflags & MFE_VERTICALFLIP )) - z += actor->target->height - actor->height - 24 * actor->target->scale; - else - z += 24 * actor->target->scale; + actor->z = actor->target->z; + K_MatchGenericExtraFlags(actor, actor->target); P_TeleportMove(actor, actor->target->x + FixedMul(dist, FINECOSINE(actor->angle >> ANGLETOFINESHIFT)), actor->target->y + FixedMul(dist, FINESINE(actor->angle >> ANGLETOFINESHIFT)), - z); + actor->z + actor->target->scale * 24 * P_MobjFlip(actor)); actor->angle += ANG30; actor->extravalue1++; @@ -4187,38 +4180,6 @@ void A_UnsetSolidSteam(mobj_t *actor) actor->flags |= MF_NOCLIP; } -// Function: A_SignPlayer -// -// Description: Changes the state of a level end sign to reflect the player that hit it. -// -// var1 = unused -// var2 = unused -// -void A_SignPlayer(mobj_t *actor) -{ - mobj_t *ov; -#ifdef HAVE_BLUA - if (LUA_CallAction("A_SignPlayer", actor)) - return; -#endif - if (!actor->target) - return; - - if (!actor->target->player) - return; - - // Set the sign to be an appropriate background color for this player's skincolor. - actor->color = KartColor_Opposite[actor->target->player->skincolor*2]; - actor->frame += KartColor_Opposite[actor->target->player->skincolor*2+1]; - - // spawn an overlay of the player's face. - ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); - P_SetTarget(&ov->target, actor); - ov->color = actor->target->player->skincolor; - ov->skin = &skins[actor->target->player->skin]; - P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN -} - // Function: A_OverlayThink // // Description: Moves the overlay to the position of its target. @@ -8300,7 +8261,7 @@ void A_ItemPop(mobj_t *actor) remains->flags = actor->flags; // Transfer flags remains->flags2 = actor->flags2; // Transfer flags2 remains->fuse = actor->fuse; // Transfer respawn timer - remains->threshold = (actor->threshold == 69 ? 69 : 68); + remains->threshold = (actor->threshold == 70 ? 70 : (actor->threshold == 69 ? 69 : 68)); remains->skin = NULL; remains->spawnpoint = actor->spawnpoint; @@ -8314,7 +8275,7 @@ void A_ItemPop(mobj_t *actor) remains->flags2 &= ~MF2_AMBUSH; - if (G_BattleGametype() && actor->threshold != 69) + if (G_BattleGametype() && (actor->threshold != 69 && actor->threshold != 70)) numgotboxes++; P_RemoveMobj(actor); @@ -8322,7 +8283,11 @@ void A_ItemPop(mobj_t *actor) void A_JawzChase(mobj_t *actor) { + const fixed_t currentspeed = R_PointToDist2(0, 0, actor->momx, actor->momy); player_t *player; + fixed_t thrustamount = 0; + fixed_t frictionsafety = (actor->friction == 0) ? 1 : actor->friction; + fixed_t topspeed = actor->movefactor; #ifdef HAVE_BLUA if (LUA_CallAction("A_JawzChase", actor)) return; @@ -8335,20 +8300,100 @@ void A_JawzChase(mobj_t *actor) if (actor->tracer->health) { + const angle_t targetangle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); mobj_t *ret; + angle_t angledelta = actor->angle - targetangle; + boolean turnclockwise = true; + + if (G_RaceGametype()) + { + const fixed_t distbarrier = FixedMul(512*mapobjectscale, FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))); + const fixed_t distaway = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); + if (distaway < distbarrier) + { + if (actor->tracer->player) + { + fixed_t speeddifference = abs(topspeed - min(actor->tracer->player->speed, K_GetKartSpeed(actor->tracer->player, false))); + topspeed = topspeed - FixedMul(speeddifference, FRACUNIT-FixedDiv(distaway, distbarrier)); + } + } + } + + if (angledelta != 0) + { + angle_t MAX_JAWZ_TURN = ANGLE_90/15; // We can turn a maximum of 6 degrees per frame at regular max speed + // MAX_JAWZ_TURN gets stronger the slower the top speed of jawz + if (topspeed < actor->movefactor) + { + if (topspeed == 0) + { + MAX_JAWZ_TURN = ANGLE_180; + } + else + { + fixed_t anglemultiplier = FixedDiv(actor->movefactor, topspeed); + MAX_JAWZ_TURN += FixedAngle(FixedMul(AngleFixed(MAX_JAWZ_TURN), anglemultiplier)); + } + } + + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angledelta); + turnclockwise = false; + } + + if (angledelta > MAX_JAWZ_TURN) + { + angledelta = MAX_JAWZ_TURN; + } + + if (turnclockwise) + { + actor->angle -= angledelta; + } + else + { + actor->angle += angledelta; + } + } ret = P_SpawnMobj(actor->tracer->x, actor->tracer->y, actor->tracer->z, MT_PLAYERRETICULE); P_SetTarget(&ret->target, actor->tracer); ret->frame |= ((leveltime % 10) / 2) + 5; ret->color = actor->cvmem; - - P_Thrust(actor, R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y), (7*actor->movefactor)/64); - return; } else P_SetTarget(&actor->tracer, NULL); } + if (!P_IsObjectOnGround(actor)) + { + // No friction in the air + frictionsafety = FRACUNIT; + } + + if (currentspeed >= topspeed) + { + // Thrust as if you were at top speed, slow down naturally + thrustamount = FixedDiv(topspeed, frictionsafety) - topspeed; + } + else + { + const fixed_t beatfriction = FixedDiv(currentspeed, frictionsafety) - currentspeed; + // Thrust to immediately get to top speed + thrustamount = beatfriction + FixedDiv(topspeed - currentspeed, frictionsafety); + } + + if (!actor->tracer) + { + actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy); + } + + P_Thrust(actor, actor->angle, thrustamount); + + if ((actor->tracer != NULL) && (actor->tracer->health > 0)) + return; + if (actor->extravalue1) // Disable looking by setting this return; diff --git a/src/p_floor.c b/src/p_floor.c index ccbfd6eae..737a8810b 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -1769,6 +1769,7 @@ static mobj_t *SearchMarioNode(msecnode_t *node) case MT_SUPERSPARK: case MT_RAIN: case MT_SNOWFLAKE: + case MT_BLIZZARDSNOW: case MT_SPLISH: case MT_SMOKE: case MT_SMALLBUBBLE: @@ -2275,7 +2276,7 @@ void T_EachTimeThinker(levelspecthink_t *eachtime) continue; if (!(players[i].mo->subsector->sector == sec - || P_PlayerTouchingSectorSpecial(&players[i], 2, (GETSECSPECIAL(sec->special, 2))) == sec)) + || P_MobjTouchingSectorSpecial(players[i].mo, 2, (GETSECSPECIAL(sec->special, 2)), false) == sec)) continue; if (floortouch == true && P_IsObjectOnRealGround(players[i].mo, sec)) diff --git a/src/p_inter.c b/src/p_inter.c index 63e091828..905cf1c1f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -27,6 +27,7 @@ #include "m_misc.h" #include "v_video.h" // video flags for CEchos #include "k_kart.h" // SRB2kart +#include "k_pwrlv.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" @@ -924,7 +925,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->fuse = 1; special->flags2 |= MF2_JUSTATTACKED; - if (!P_PlayerTouchingSectorSpecial(player, 4, 2 + flagteam)) + if (!P_MobjTouchingSectorSpecial(player->mo, 4, 2 + flagteam, false)) { CONS_Printf(M_GetText("%s returned the %c%s%c to base.\n"), plname, flagcolor, flagtext, 0x80); @@ -1763,6 +1764,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } } +// Easily make it so that overtime works offline +//#define TESTOVERTIMEINFREEPLAY + /** Checks if the level timer is over the timelimit and the round should end, * unless you are in overtime. In which case leveltime may stretch out beyond * timelimitintics and overtime's status will be checked here each tick. @@ -1773,7 +1777,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) */ void P_CheckTimeLimit(void) { - INT32 i, k; + INT32 i; if (!cv_timelimit.value) return; @@ -1807,71 +1811,76 @@ void P_CheckTimeLimit(void) } } } + else*/ //Optional tie-breaker for Match/CTF - else*/ if (cv_overtime.value) + if (cv_overtime.value) { - INT32 playerarray[MAXPLAYERS]; - INT32 tempplayer = 0; - INT32 spectators = 0; - INT32 playercount = 0; - - //Figure out if we have enough participating players to care. +#ifndef TESTOVERTIMEINFREEPLAY + boolean foundone = false; // Overtime is used for closing off down to a specific item. for (i = 0; i < MAXPLAYERS; i++) { - if (players[i].exiting) - return; - if (playeringame[i] && players[i].spectator) - spectators++; - } - - if ((D_NumPlayers() - spectators) > 1) - { - // Play the starpost sfx after the first second of overtime. - if (gamestate == GS_LEVEL && (leveltime == (timelimitintics + TICRATE))) - S_StartSound(NULL, sfx_strpst); - - // Normal Match - if (!G_GametypeHasTeams()) + if (!playeringame[i] || players[i].spectator) + continue; + if (foundone) { - //Store the nodes of participating players in an array. - for (i = 0; i < MAXPLAYERS; i++) +#endif + // Initiate the kill zone + if (!battleovertime.enabled) { - if (playeringame[i] && !players[i].spectator) + INT32 b = 0; + thinker_t *th; + mobj_t *item = NULL; + + P_RespawnBattleBoxes(); // FORCE THESE TO BE RESPAWNED FOR THIS!!!!!!! + + // Find us an item box to center on. + for (th = thinkercap.next; th != &thinkercap; th = th->next) { - playerarray[playercount] = i; - playercount++; + mobj_t *thismo; + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + thismo = (mobj_t *)th; + + if (thismo->type != MT_RANDOMITEM) + continue; + if (thismo->threshold == 69) // Disappears + continue; + + b++; + + // Only select items that are on the ground, ignore ones in the air. Ambush flag inverts this rule. + if ((!P_IsObjectOnGround(thismo)) != (thismo->flags2 & MF2_AMBUSH)) + continue; + + if (item == NULL || (b < nummapboxes && P_RandomChance(((nummapboxes-b)*FRACUNIT)/nummapboxes))) // This is to throw off the RNG some + item = thismo; + if (b >= nummapboxes) // end early if we've found them all already + break; } - } - if (playercount > MAXPLAYERS) - playercount = MAXPLAYERS; - - //Sort 'em. - for (i = 1; i < playercount; i++) - { - for (k = i; k < playercount; k++) + if (item == NULL) // no item found, could happen if every item is in the air or has ambush flag, or the map has none { - if (players[playerarray[i-1]].marescore < players[playerarray[k]].marescore) - { - tempplayer = playerarray[i-1]; - playerarray[i-1] = playerarray[k]; - playerarray[k] = tempplayer; - } + CONS_Alert(CONS_WARNING, "No usuable items for Battle overtime!\n"); + return; } - } - //End the round if the top players aren't tied. - if (players[playerarray[0]].marescore == players[playerarray[1]].marescore) - return; + item->threshold = 70; // Set constant respawn + battleovertime.x = item->x; + battleovertime.y = item->y; + battleovertime.z = item->z; + battleovertime.radius = 4096*mapobjectscale; + battleovertime.minradius = (cv_overtime.value == 2 ? 40 : 512) * mapobjectscale; + battleovertime.enabled = 1; + S_StartSound(NULL, sfx_kc47); + } + return; +#ifndef TESTOVERTIMEINFREEPLAY } else - { - //In team match and CTF, determining a tie is much simpler. =P - if (redscore == bluescore) - return; - } + foundone = true; } +#endif } for (i = 0; i < MAXPLAYERS; i++) @@ -1882,9 +1891,6 @@ void P_CheckTimeLimit(void) return; P_DoPlayerExit(&players[i]); } - - /*if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0);*/ } /** Checks if a player's score is over the pointlimit and the round should end. @@ -2030,7 +2036,8 @@ void P_CheckPointLimit(void) // Checks whether or not to end a race netgame. boolean P_CheckRacers(void) { - INT32 i, j, numplayersingame = 0; + INT32 i, j, numplayersingame = 0, numexiting = 0; + boolean griefed = false; // Check if all the players in the race have finished. If so, end the level. for (i = 0; i < MAXPLAYERS; i++) @@ -2047,57 +2054,53 @@ boolean P_CheckRacers(void) return true; } - if (cv_karteliminatelast.value) + for (j = 0; j < MAXPLAYERS; j++) { - for (j = 0; j < MAXPLAYERS; j++) + if (nospectategrief[j] != -1) // prevent spectate griefing + griefed = true; + if (!playeringame[j] || players[j].spectator) + continue; + numplayersingame++; + if (players[j].exiting) + numexiting++; + } + + if (cv_karteliminatelast.value && numplayersingame > 1 && !griefed) + { + // check if we just got unlucky and there was only one guy who was a problem + for (j = i+1; j < MAXPLAYERS; j++) { - if (!playeringame[j] || players[j].spectator) + if (!playeringame[j] || players[j].spectator || players[j].exiting || !players[j].lives) continue; - numplayersingame++; + break; } - if (numplayersingame > 1 && nospectategrief > 0 && numplayersingame >= nospectategrief) // prevent spectate griefing + if (j == MAXPLAYERS) // finish anyways, force a time over { - // check if we just got unlucky and there was only one guy who was a problem - for (j = i+1; j < MAXPLAYERS; j++) - { - if (!playeringame[j] || players[j].spectator || players[j].exiting || !players[j].lives) - continue; - - break; - } - - if (j == MAXPLAYERS) // finish anyways, force a time over - { - P_DoTimeOver(&players[i]); - countdown = countdown2 = 0; - return true; - } + P_DoTimeOver(&players[i]); + countdown = countdown2 = 0; + return true; } } if (!countdown) // Check to see if the winners have finished, to set countdown. { - UINT8 numingame = 0, numexiting = 0; UINT8 winningpos = 1; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - numingame++; - if (players[i].exiting) - numexiting++; - } - - winningpos = max(1, numingame/2); - if (numingame % 2) // any remainder? + winningpos = max(1, numplayersingame/2); + if (numplayersingame % 2) // any remainder? winningpos++; if (numexiting >= winningpos) countdown = (((netgame || multiplayer) ? cv_countdowntime.value : 30)*TICRATE) + 1; // 30 seconds to finish, get going! } + if (numplayersingame < 2) // reset nospectategrief in free play + { + for (j = 0; j < MAXPLAYERS; j++) + nospectategrief[j] = -1; + } + return false; } @@ -2142,6 +2145,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SPECIAL); target->flags2 &= ~(MF2_SKULLFLY|MF2_NIGHTSPULL); target->health = 0; // This makes it easy to check if something's dead elsewhere. + target->shadowscale = 0; #ifdef HAVE_BLUA if (LUAh_MobjDeath(target, inflictor, source) || P_MobjWasRemoved(target)) diff --git a/src/p_local.h b/src/p_local.h index bc25affd4..1358bf195 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -211,12 +211,11 @@ extern tic_t itemrespawntime[ITEMQUESIZE]; extern size_t iquehead, iquetail; extern consvar_t cv_gravity/*, cv_viewheight*/; +void P_RespawnBattleBoxes(void); void P_RespawnSpecials(void); mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); -mobj_t *P_SpawnShadowMobj(mobj_t * caster); - void P_RecalcPrecipInSector(sector_t *sector); void P_PrecipitationEffects(void); @@ -227,7 +226,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state); boolean P_SetMobjState(mobj_t *mobj, statenum_t state); //void P_RunShields(void); void P_RunOverlays(void); -void P_RunShadows(void); +void P_RunBattleOvertime(void); void P_MobjThinker(mobj_t *mobj); boolean P_RailThinker(mobj_t *mobj); void P_PushableThinker(mobj_t *mobj); diff --git a/src/p_map.c b/src/p_map.c index 1d1b03e57..38ebdf293 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -129,6 +129,8 @@ boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z) // MOVEMENT ITERATOR FUNCTIONS // ========================================================================= +//#define TELEPORTJANK + // For our intermediate buffer, remove any duplicate entries by adding each one to // a temprary buffer if it's not already in there, copy the temporary buffer back over the intermediate afterwards static void spechitint_removedups(void) @@ -228,9 +230,11 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) //INT32 pflags; const fixed_t hscale = mapobjectscale + (mapobjectscale - object->scale); const fixed_t vscale = mapobjectscale + (object->scale - mapobjectscale); - fixed_t offx, offy; fixed_t vertispeed = spring->info->mass; fixed_t horizspeed = spring->info->damage; + UINT8 starcolor = (spring->info->painchance % MAXTRANSLATIONS); + fixed_t savemomx = 0; + fixed_t savemomy = 0; if (object->eflags & MFE_SPRUNG) // Object was already sprung this tic return false; @@ -252,29 +256,36 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) object->eflags |= MFE_SPRUNG; // apply this flag asap! spring->flags &= ~(MF_SOLID|MF_SPECIAL); // De-solidify +#ifdef TELEPORTJANK if (horizspeed && vertispeed) // Mimic SA { object->momx = object->momy = 0; P_TryMove(object, spring->x, spring->y, true); } +#endif if (spring->eflags & MFE_VERTICALFLIP) vertispeed *= -1; + // Vertical springs teleport you on TOP of them. if (vertispeed > 0) object->z = spring->z + spring->height + 1; else if (vertispeed < 0) object->z = spring->z - object->height - 1; else { + fixed_t offx, offy; + // Horizontal springs teleport you in FRONT of them. + savemomx = object->momx; + savemomy = object->momy; object->momx = object->momy = 0; // Overestimate the distance to position you at offx = P_ReturnThrustX(spring, spring->angle, (spring->radius + object->radius + 1) * 2); offy = P_ReturnThrustY(spring, spring->angle, (spring->radius + object->radius + 1) * 2); - // Make it square by clipping + // Then clip it down to a square, so it matches the hitbox size. if (offx > (spring->radius + object->radius + 1)) offx = spring->radius + object->radius + 1; else if (offx < -(spring->radius + object->radius + 1)) @@ -285,27 +296,94 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) else if (offy < -(spring->radius + object->radius + 1)) offy = -(spring->radius + object->radius + 1); - // Set position! P_TryMove(object, spring->x + offx, spring->y + offy, true); } if (vertispeed) - object->momz = FixedMul(vertispeed,FixedSqrt(FixedMul(vscale, spring->scale))); + object->momz = FixedMul(vertispeed, FixedSqrt(FixedMul(vscale, spring->scale))); if (horizspeed) { - if (!object->player) - P_InstaThrustEvenIn2D(object, spring->angle, FixedMul(horizspeed,FixedSqrt(FixedMul(hscale, spring->scale)))); + angle_t finalAngle = spring->angle; + fixed_t finalSpeed = horizspeed; + fixed_t objectSpeed; + + if (object->player) + objectSpeed = object->player->speed; else + objectSpeed = R_PointToDist2(0, 0, savemomx, savemomy); + + if (!vertispeed) { - fixed_t finalSpeed = FixedDiv(horizspeed, hscale); - fixed_t pSpeed = object->player->speed; + // Scale to gamespeed + finalSpeed = FixedMul(finalSpeed, K_GetKartGameSpeedScalar(gamespeed)); - if (pSpeed > finalSpeed) - finalSpeed = pSpeed; + // Reflect your momentum angle against the surface of horizontal springs. + // This makes it a bit more interesting & unique than just being a speed boost in a pre-defined direction + if (savemomx || savemomy) + { + angle_t momang; + INT32 angoffset; + boolean subtract = false; - P_InstaThrustEvenIn2D(object, spring->angle, FixedMul(finalSpeed,FixedSqrt(FixedMul(hscale, spring->scale)))); + momang = R_PointToAngle2(0, 0, savemomx, savemomy); + + angoffset = momang; + angoffset -= spring->angle; // Subtract + + // Flip on wrong side + if ((angle_t)angoffset > ANGLE_180) + { + angoffset = InvAngle((angle_t)angoffset); + subtract = !subtract; + } + + // Fix going directly against the spring's angle sending you the wrong way + if ((spring->angle - momang) > ANGLE_90) + angoffset = ANGLE_180 - angoffset; + + // Offset is reduced to cap it (90 / 2 = max of 45 degrees) + angoffset /= 2; + + // Reduce further based on how slow your speed is compared to the spring's speed + if (finalSpeed > objectSpeed) + angoffset = FixedDiv(angoffset, FixedDiv(finalSpeed, objectSpeed)); + + if (subtract) + angoffset = (signed)(spring->angle) - angoffset; + else + angoffset = (signed)(spring->angle) + angoffset; + + finalAngle = angoffset; + } } + + if (object->player) + { + // Less friction when hitting horizontal springs + if (!vertispeed) + { + if (!object->player->kartstuff[k_tiregrease]) + { + UINT8 i; + for (i = 0; i < 2; i++) + { + mobj_t *grease; + grease = P_SpawnMobj(object->x, object->y, object->z, MT_TIREGREASE); + P_SetTarget(&grease->target, object); + grease->angle = R_PointToAngle2(0, 0, object->momx, object->momy); + grease->extravalue1 = i; + } + } + + object->player->kartstuff[k_tiregrease] = greasetics; //FixedMul(greasetics << FRACBITS, finalSpeed/72) >> FRACBITS + } + } + + // Horizontal speed is used as a minimum thrust, not a direct replacement + finalSpeed = max(objectSpeed, finalSpeed); + + P_InstaThrustEvenIn2D(object, finalAngle, FixedMul(finalSpeed, FixedSqrt(FixedMul(hscale, spring->scale)))); } // Re-solidify @@ -318,46 +396,12 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) if (spring->flags & MF_ENEMY) // Spring shells P_SetTarget(&spring->target, object); - if (horizspeed && object->player->cmd.forwardmove == 0 && object->player->cmd.sidemove == 0) - { - object->angle = spring->angle; - - if (!demo.playback || P_AnalogMove(object->player)) - { - if (object->player == &players[consoleplayer]) - localangle[0] = spring->angle; - else if (object->player == &players[displayplayers[1]]) - localangle[1] = spring->angle; - else if (object->player == &players[displayplayers[2]]) - localangle[2] = spring->angle; - else if (object->player == &players[displayplayers[3]]) - localangle[3] = spring->angle; - } - } - - //pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED); // I still need these. P_ResetPlayer(object->player); - /* // SRB2kart - Springs don't need to change player state in kart. - if (P_MobjFlip(object)*vertispeed > 0) - P_SetPlayerMobjState(object, S_PLAY_SPRING); - else if (P_MobjFlip(object)*vertispeed < 0) - P_SetPlayerMobjState(object, S_PLAY_FALL1); - else // horizontal spring - { - if (pflags & (PF_JUMPED|PF_SPINNING) && object->player->panim == PA_ROLL) - object->player->pflags = pflags; - else - P_SetPlayerMobjState(object, S_PLAY_RUN1); - } - - if (spring->info->painchance) - { - object->player->pflags |= PF_JUMPED; - P_SetPlayerMobjState(object, S_PLAY_ATK1); - } - */ + object->player->kartstuff[k_springstars] = max(vertispeed, horizspeed) / FRACUNIT / 2; + object->player->kartstuff[k_springcolor] = starcolor; } + return true; } @@ -1273,6 +1317,7 @@ static boolean PIT_CheckThing(mobj_t *thing) //else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down return true; } + // missiles can hit other things if (tmthing->flags & MF_MISSILE || tmthing->type == MT_SHELL) { @@ -2869,27 +2914,18 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (!(thing->flags & MF_NOCLIP)) { //All things are affected by their scale. - fixed_t maxstep = FixedMul(MAXSTEPMOVE, mapobjectscale); + const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); + fixed_t maxstep = maxstepmove; - if (thing->player) - { - // If using type Section1:13, double the maxstep. - if (P_PlayerTouchingSectorSpecial(thing->player, 1, 13) - || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 13) - maxstep <<= 1; - // If using type Section1:12, no maxstep. For ledges you don't want the player to climb! (see: Egg Zeppelin & SMK port walls) - else if (P_PlayerTouchingSectorSpecial(thing->player, 1, 12) - || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 12) - maxstep = 0; + if (thing->player && thing->player->kartstuff[k_waterskip]) + maxstep += maxstepmove; // Add some extra stepmove when waterskipping - // Don't 'step up' while springing, - // Only step up "if needed". - /* // SRB2kart - don't need - if (thing->state == &states[S_PLAY_SPRING] - && P_MobjFlip(thing)*thing->momz > FixedMul(FRACUNIT, thing->scale)) - maxstep = 0; - */ - } + // If using type Section1:13, double the maxstep. + if (P_MobjTouchingSectorSpecial(thing, 1, 13, false)) + maxstep <<= 1; + // If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin + else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false)) + maxstep = 0; if (thing->type == MT_SKIM) maxstep = 0; @@ -2912,12 +2948,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) return false; // mobj must lower itself to fit // Ramp test - if (maxstep > 0 && !( - thing->player && ( - P_PlayerTouchingSectorSpecial(thing->player, 1, 14) - || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 14) - ) - ) + if ((maxstep > 0) && !(P_MobjTouchingSectorSpecial(thing, 1, 14, false))) { // If the floor difference is MAXSTEPMOVE or less, and the sector isn't Section1:14, ALWAYS // step down! Formerly required a Section1:13 sector for the full MAXSTEPMOVE, but no more. @@ -3019,14 +3050,24 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) P_HandleSlopeLanding(thing, tmfloorslope); if (thing->momz <= 0) + { thing->standingslope = tmfloorslope; +#ifdef HWRENDER + thing->modeltilt = thing->standingslope; +#endif + } } else if (thing->z+thing->height >= tmceilingz && (thing->eflags & MFE_VERTICALFLIP)) { if (!startingonground && tmceilingslope) P_HandleSlopeLanding(thing, tmceilingslope); if (thing->momz >= 0) + { thing->standingslope = tmceilingslope; +#ifdef HWRENDER + thing->modeltilt = thing->standingslope; +#endif + } } } else // don't set standingslope if you're not going to clip against it @@ -4853,7 +4894,7 @@ fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height) if (!(rover->flags & FF_EXISTS)) continue; - if ((!(rover->flags & FF_SOLID || rover->flags & FF_QUICKSAND) || (rover->flags & FF_SWIMMABLE))) + if (!((rover->flags & FF_SOLID) || (rover->flags & FF_QUICKSAND)) || (rover->flags & FF_SWIMMABLE)) continue; topheight = *rover->topheight; diff --git a/src/p_mobj.c b/src/p_mobj.c index 513dd4c3c..b3a0a6f88 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -47,7 +47,6 @@ consvar_t cv_splats = {"splats", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0 actioncache_t actioncachehead; static mobj_t *overlaycap = NULL; -static mobj_t *shadowcap = NULL; mobj_t *waypointcap = NULL; void P_InitCachedActions(void) @@ -1268,6 +1267,8 @@ fixed_t P_GetMobjGravity(mobj_t *mo) P_PlayerFlip(mo); if (mo->player->kartstuff[k_pogospring]) gravityadd = (5*gravityadd)/2; + if (mo->player->kartstuff[k_waterskip]) + gravityadd = (4*gravityadd)/3; } else { @@ -1327,9 +1328,6 @@ fixed_t P_GetMobjGravity(mobj_t *mo) case MT_JAWZ_DUD: gravityadd = (5*gravityadd)/2; break; - case MT_SIGN: - gravityadd /= 8; - break; case MT_KARMAFIREWORK: gravityadd /= 3; break; @@ -1848,6 +1846,9 @@ void P_XYMovement(mobj_t *mo) // Now compare the Zs of the different quantizations if (oldangle-newangle > ANG30 && oldangle-newangle < ANGLE_180) { // Allow for a bit of sticking - this value can be adjusted later mo->standingslope = oldslope; +#ifdef HWRENDER + mo->modeltilt = mo->standingslope; +#endif P_SlopeLaunch(mo); //CONS_Printf("launched off of slope - "); @@ -1937,7 +1938,7 @@ void P_XYMovement(mobj_t *mo) #endif //{ SRB2kart stuff - if (mo->type == MT_ORBINAUT || mo->type == MT_JAWZ_DUD || mo->type == MT_JAWZ || mo->type == MT_BALLHOG || mo->type == MT_FLINGRING) //(mo->type == MT_JAWZ && !mo->tracer)) + if (mo->type == MT_BALLHOG || mo->type == MT_FLINGRING) //(mo->type == MT_JAWZ && !mo->tracer)) return; if (mo->player && (mo->player->kartstuff[k_spinouttimer] && !mo->player->kartstuff[k_wipeoutslow]) && mo->player->speed <= K_GetKartSpeed(mo->player, false)/2) @@ -2421,6 +2422,9 @@ static boolean P_ZMovement(mobj_t *mo) if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM)) { mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope; +#ifdef HWRENDER + mo->modeltilt = mo->standingslope; +#endif P_ReverseQuantizeMomentumToSlope(&mom, mo->standingslope); } #endif @@ -3295,26 +3299,19 @@ void P_MobjCheckWater(mobj_t *mobj) // skipping stone! if (p && p->kartstuff[k_waterskip] < 2 && ((p->speed/3 > abs(mobj->momz)) // Going more forward than horizontal, so you can skip across the water. - || (p->speed > K_GetKartSpeed(p,false)/3 && p->kartstuff[k_waterskip])) // Already skipped once, so you can skip once more! + || (p->speed > 20*mapobjectscale && p->kartstuff[k_waterskip])) // Already skipped once, so you can skip once more! && ((!(mobj->eflags & MFE_VERTICALFLIP) && thingtop - mobj->momz > mobj->watertop) || ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z - mobj->momz < mobj->waterbottom))) { - const fixed_t min = 6<momx = mobj->momx/2; - mobj->momy = mobj->momy/2; - mobj->momz = -mobj->momz/2; + mobj->momx = (4*mobj->momx)/5; + mobj->momy = (4*mobj->momy)/5; - if (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->momz < FixedMul(min, mobj->scale)) - mobj->momz = FixedMul(min, mobj->scale); - else if (mobj->eflags & MFE_VERTICALFLIP && mobj->momz > FixedMul(-min, mobj->scale)) - mobj->momz = FixedMul(-min, mobj->scale); - - /*if (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->momz > FixedMul(max, mobj->scale)) - mobj->momz = FixedMul(max, mobj->scale); - else if (mobj->eflags & MFE_VERTICALFLIP && mobj->momz < FixedMul(-max, mobj->scale)) - mobj->momz = FixedMul(-max, mobj->scale);*/ + if (mobj->eflags & MFE_VERTICALFLIP) + mobj->momz = FixedMul(-hop, mobj->scale); + else + mobj->momz = FixedMul(hop, mobj->scale); p->kartstuff[k_waterskip]++; } @@ -3729,11 +3726,15 @@ static void P_PlayerMobjThinker(mobj_t *mobj) mobj->z += mobj->momz; P_SetThingPosition(mobj); P_CheckPosition(mobj, mobj->x, mobj->y); + mobj->floorz = tmfloorz; + mobj->ceilingz = tmceilingz; goto animonly; } else if (mobj->player->pflags & PF_MACESPIN && mobj->tracer) { P_CheckPosition(mobj, mobj->x, mobj->y); + mobj->floorz = tmfloorz; + mobj->ceilingz = tmceilingz; goto animonly; } @@ -3927,51 +3928,60 @@ void P_NullPrecipThinker(precipmobj_t *mobj) mobj->precipflags &= ~PCF_THUNK; } -void P_SnowThinker(precipmobj_t *mobj) +void P_PrecipThinker(precipmobj_t *mobj) { P_CycleStateAnimation((mobj_t *)mobj); - // adjust height - if ((mobj->z += mobj->momz) <= mobj->floorz) - mobj->z = mobj->ceilingz; -} - -void P_RainThinker(precipmobj_t *mobj) -{ - P_CycleStateAnimation((mobj_t *)mobj); - - if (mobj->state != &states[S_RAIN1]) + if (mobj->state == &states[S_RAINRETURN]) { - // cycle through states, - // calling action functions at transitions - if (mobj->tics <= 0) - return; - - if (--mobj->tics) - return; - - if (!P_SetPrecipMobjState(mobj, mobj->state->nextstate)) - return; - - if (mobj->state != &states[S_RAINRETURN]) - return; - + // Reset to ceiling! + P_SetPrecipMobjState(mobj, mobj->info->spawnstate); mobj->z = mobj->ceilingz; - P_SetPrecipMobjState(mobj, S_RAIN1); - - return; + mobj->momz = mobj->info->speed; + mobj->precipflags &= ~PCF_SPLASH; } + if (mobj->tics != -1) + { + if (mobj->tics) + { + mobj->tics--; + } + + if (mobj->tics == 0) + { + if ((mobj->precipflags & PCF_SPLASH) && (mobj->state->nextstate == S_NULL)) + { + // HACK: sprite changes are 1 tic late, so you would see splashes on the ceiling if not for this state. + // We need to use the settings from the previous state, since some of those are NOT 1 tic late. + INT32 frame = (mobj->frame & ~FF_FRAMEMASK); + P_SetPrecipMobjState(mobj, S_RAINRETURN); + mobj->frame = frame; + return; + } + else + { + if (!P_SetPrecipMobjState(mobj, mobj->state->nextstate)) + return; + } + } + } + + if (mobj->precipflags & PCF_SPLASH) + return; + // adjust height if ((mobj->z += mobj->momz) <= mobj->floorz) { - // no splashes on sky or bottomless pits - if (mobj->precipflags & PCF_PIT) + if ((mobj->info->deathstate == S_NULL) || (mobj->precipflags & PCF_PIT)) // no splashes on sky or bottomless pits + { mobj->z = mobj->ceilingz; + } else { + P_SetPrecipMobjState(mobj, mobj->info->deathstate); mobj->z = mobj->floorz; - P_SetPrecipMobjState(mobj, S_SPLASH1); + mobj->precipflags |= PCF_SPLASH; } } } @@ -6199,115 +6209,214 @@ static void P_RemoveOverlay(mobj_t *thing) } } -void P_RunShadows(void) +// SAL'S KART BATTLE MODE OVERTIME HANDLER +#define MAXPLANESPERSECTOR (MAXFFLOORS+1)*2 +static void P_SpawnOvertimeParticles(fixed_t x, fixed_t y, fixed_t scale, mobjtype_t type, boolean ceiling) { - mobj_t *mobj, *next, *dest; + UINT8 i; + fixed_t flatz[MAXPLANESPERSECTOR]; + boolean flip[MAXPLANESPERSECTOR]; + UINT8 numflats = 0; + mobj_t *mo; + subsector_t *ss = R_IsPointInSubsector(x, y); + sector_t *sec; - for (mobj = shadowcap; mobj; mobj = next) + if (!ss) + return; + sec = ss->sector; + + // convoluted stuff JUST to get all of the planes we need to draw orbs on :V + + for (i = 0; i < MAXPLANESPERSECTOR; i++) + flip[i] = false; + + if (sec->floorpic != skyflatnum) { - fixed_t floorz; +#ifdef ESLOPE + flatz[numflats] = (sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : sec->floorheight); +#else + flatz[numflats] = (sec->floorheight); +#endif + numflats++; + } + if (sec->ceilingpic != skyflatnum && ceiling) + { +#ifdef ESLOPE + flatz[numflats] = (sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : sec->ceilingheight) - FixedMul(mobjinfo[type].height, scale); +#else + flatz[numflats] = (sec->ceilingheight) - FixedMul(mobjinfo[type].height, scale); +#endif + flip[numflats] = true; + numflats++; + } - next = mobj->hnext; - P_SetTarget(&mobj->hnext, NULL); - - if (!mobj->target || P_MobjWasRemoved(mobj->target)) + if (sec->ffloors) + { + ffloor_t *rover; + for (rover = sec->ffloors; rover; rover = rover->next) { - mobj->flags2 |= MF2_DONTDRAW; - continue; // shouldn't you already be dead? - } - - if (mobj->target->player) - floorz = mobj->target->floorz; - else // FOR SOME REASON, plain floorz is not reliable for normal objects, only players?! - floorz = P_FloorzAtPos(mobj->target->x, mobj->target->y, mobj->target->z, mobj->target->height); - - K_MatchGenericExtraFlags(mobj, mobj->target); - - if (((mobj->target->eflags & MFE_VERTICALFLIP) && mobj->target->z+mobj->target->height > mobj->target->ceilingz) - || (!(mobj->target->eflags & MFE_VERTICALFLIP) && mobj->target->z < floorz)) - mobj->flags2 |= MF2_DONTDRAW; - - // First scale to the same radius - P_SetScale(mobj, FixedDiv(mobj->target->radius, mobj->info->radius)); - - dest = mobj->target; - - if (dest->type == MT_THUNDERSHIELD) - dest = dest->target; - - P_TeleportMove(mobj, dest->x, dest->y, mobj->target->z); - - if (((mobj->eflags & MFE_VERTICALFLIP) && (mobj->ceilingz > mobj->z+mobj->height)) - || (!(mobj->eflags & MFE_VERTICALFLIP) && (floorz < mobj->z))) - { - INT32 i; - fixed_t prevz; - - mobj->z = (mobj->eflags & MFE_VERTICALFLIP ? mobj->ceilingz : floorz); - - for (i = 0; i < MAXFFLOORS; i++) + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER)) + continue; + if (*rover->toppic != skyflatnum) { - prevz = mobj->z; - - // Now scale again based on height difference - P_SetScale(mobj, FixedDiv(mobj->scale, max(FRACUNIT, ((mobj->target->z-mobj->z)/200)+FRACUNIT))); - - // Check new position to see if you should still be on that ledge - P_TeleportMove(mobj, dest->x, dest->y, mobj->z); - - mobj->z = (mobj->eflags & MFE_VERTICALFLIP ? mobj->ceilingz : floorz); - - if (mobj->z == prevz) - break; +#ifdef ESLOPE + flatz[numflats] = (*rover->t_slope ? P_GetZAt(*rover->t_slope, x, y) : *rover->topheight); +#else + flatz[numflats] = (*rover->topheight); +#endif + numflats++; + } + if (*rover->bottompic != skyflatnum && ceiling) + { +#ifdef ESLOPE + flatz[numflats] = (*rover->b_slope ? P_GetZAt(*rover->b_slope, x, y) : *rover->bottomheight) - FixedMul(mobjinfo[type].height, scale); +#else + flatz[numflats] = (*rover->bottomheight) - FixedMul(mobjinfo[type].height, scale); +#endif + flip[numflats] = true; + numflats++; } } - - if (mobj->target->type == MT_FLOATINGITEM) - P_SetScale(mobj, mobj->scale/3); } - P_SetTarget(&shadowcap, NULL); -} -// called whenever shadows think -// It must be done this way so that level changes don't break when the shadowcap can't be reset -static void P_AddShadow(mobj_t *thing) -{ - I_Assert(thing != NULL); - - if (shadowcap == NULL) - P_SetTarget(&shadowcap, thing); - else { - mobj_t *mo; - for (mo = shadowcap; mo && mo->hnext; mo = mo->hnext) - ; - - I_Assert(mo != NULL); - I_Assert(mo->hnext == NULL); - - P_SetTarget(&mo->hnext, thing); - } - P_SetTarget(&thing->hnext, NULL); -} - -// Called only when MT_SHADOW (or anything else in the shadowcap list) is removed. -// Keeps the hnext list from corrupting. -static void P_RemoveShadow(mobj_t *thing) -{ - mobj_t *mo; - if (shadowcap == thing) - { - P_SetTarget(&shadowcap, thing->hnext); - P_SetTarget(&thing->hnext, NULL); + if (numflats <= 0) // no flats return; + + for (i = 0; i < numflats; i++) + { + mo = P_SpawnMobj(x, y, flatz[i], type); + + // Lastly, if this can see the skybox mobj, then... we just wasted our time :V + if (skyboxmo[0] && !P_MobjWasRemoved(skyboxmo[0])) + { + const fixed_t sbz = skyboxmo[0]->z; + fixed_t checkz = sec->floorheight; + + while (checkz < sec->ceilingheight) + { + P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, checkz); + if (P_CheckSight(skyboxmo[0], mo)) + { + P_RemoveMobj(mo); + break; + } + else + checkz += 32*mapobjectscale; + } + + P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, sbz); + + if (P_MobjWasRemoved(mo)) + continue; + } + + P_SetScale(mo, scale); + + if (flip[i]) + { + mo->flags2 |= MF2_OBJECTFLIP; + mo->eflags |= MFE_VERTICALFLIP; + } + + switch(type) + { + case MT_OVERTIMEFOG: + mo->destscale = 8*mo->scale; + mo->momz = P_RandomRange(1,8)*mo->scale; + break; + case MT_OVERTIMEORB: + //mo->destscale = mo->scale/4; + mo->frame += ((leveltime/4) % 8); + /*if (battleovertime.enabled < 10*TICRATE) + mo->flags2 |= MF2_SHADOW;*/ + mo->angle = R_PointToAngle2(mo->x, mo->y, battleovertime.x, battleovertime.y) + ANGLE_90; + mo->z += P_RandomRange(0,48) * mo->scale; + break; + default: + break; + } + } +} +#undef MAXPLANESPERSECTOR + +void P_RunBattleOvertime(void) +{ + UINT16 i, j; + + if (battleovertime.enabled < 10*TICRATE) + { + battleovertime.enabled++; + if (battleovertime.enabled == TICRATE) + S_StartSound(NULL, sfx_bhurry); + if (battleovertime.enabled == 10*TICRATE) + S_StartSound(NULL, sfx_kc40); + } + else + { + if (battleovertime.radius > battleovertime.minradius) + battleovertime.radius -= mapobjectscale; + else + battleovertime.radius = battleovertime.minradius; } - for (mo = shadowcap; mo; mo = mo->hnext) - if (mo->hnext == thing) + if (leveltime & 1) + { + UINT8 transparency = tr_trans50; + + if (!splitscreen && players[displayplayers[0]].mo) { - P_SetTarget(&mo->hnext, thing->hnext); - P_SetTarget(&thing->hnext, NULL); - return; + INT32 dist = P_AproxDistance(battleovertime.x-players[displayplayers[0]].mo->x, battleovertime.y-players[displayplayers[0]].mo->y); + transparency = max(0, NUMTRANSMAPS - ((256 + (dist>>FRACBITS)) / 256)); } + + if (transparency < NUMTRANSMAPS) + { + mobj_t *beam = P_SpawnMobj(battleovertime.x, battleovertime.y, battleovertime.z + (mobjinfo[MT_RANDOMITEM].height/2), MT_OVERTIMEBEAM); + P_SetScale(beam, beam->scale*2); + if (transparency > 0) + beam->frame |= transparency<>FRACBITS / 2));*/ + + for (i = 0; i < 16; i++) + { + j = 0; + while (j < 32) // max attempts + { + fixed_t x = battleovertime.x + ((P_RandomRange(-64,64) * 128)<target) - { - P_RemoveMobj(mobj); - return; - } - - P_AddShadow(mobj); - break; /*case MT_BLACKORB: case MT_WHITEORB: case MT_GREENORB: @@ -6921,7 +7021,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->x = mobj->target->x; mobj->y = mobj->target->y; - if (!splitscreen) + if (!splitscreen && players[displayplayers[0]].mo) { scale = mobj->target->scale + FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayers[0]].mo->x-mobj->target->x, players[displayplayers[0]].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale); @@ -7935,9 +8035,18 @@ void P_MobjThinker(mobj_t *mobj) else { fixed_t finalspeed = mobj->movefactor; + const fixed_t currentspeed = R_PointToDist2(0, 0, mobj->momx, mobj->momy); + fixed_t thrustamount = 0; + fixed_t frictionsafety = (mobj->friction == 0) ? 1 : mobj->friction; mobj_t *ghost = P_SpawnGhostMobj(mobj); ghost->colorized = true; // already has color! + if (!grounded) + { + // No friction in the air + frictionsafety = FRACUNIT; + } + mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); if (mobj->health <= 5) { @@ -7946,16 +8055,22 @@ void P_MobjThinker(mobj_t *mobj) finalspeed = FixedMul(finalspeed, FRACUNIT-FRACUNIT/4); } - P_InstaThrust(mobj, mobj->angle, finalspeed); - - if (grounded) + if (currentspeed >= finalspeed) { - sector_t *sec2 = P_ThingOnSpecial3DFloor(mobj); - if ((sec2 && GETSECSPECIAL(sec2->special, 3) == 1) - || (P_IsObjectOnRealGround(mobj, mobj->subsector->sector) - && GETSECSPECIAL(mobj->subsector->sector->special, 3) == 1)) - K_DoPogoSpring(mobj, 0, 1); + // Thrust as if you were at top speed, slow down naturally + thrustamount = FixedDiv(finalspeed, frictionsafety) - finalspeed; } + else + { + const fixed_t beatfriction = FixedDiv(currentspeed, frictionsafety) - currentspeed; + // Thrust to immediately get to top speed + thrustamount = beatfriction + FixedDiv(finalspeed - currentspeed, frictionsafety); + } + + P_Thrust(mobj, mobj->angle, thrustamount); + + if (P_MobjTouchingSectorSpecial(mobj, 3, 1, true)) + K_DoPogoSpring(mobj, 0, 1); if (mobj->threshold > 0) mobj->threshold--; @@ -7967,10 +8082,6 @@ void P_MobjThinker(mobj_t *mobj) } case MT_JAWZ: { - sector_t *sec2; - fixed_t topspeed = mobj->movefactor; - fixed_t distbarrier = 512*mapobjectscale; - fixed_t distaway; mobj_t *ghost = P_SpawnGhostMobj(mobj); if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player) @@ -7984,44 +8095,11 @@ void P_MobjThinker(mobj_t *mobj) if (leveltime % TICRATE == 0) S_StartSound(mobj, mobj->info->activesound); - distbarrier = FixedMul(distbarrier, FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))); - - if (G_RaceGametype() && mobj->tracer) - { - distaway = P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y); - if (distaway < distbarrier) - { - if (mobj->tracer->player) - { - fixed_t speeddifference = abs(topspeed - min(mobj->tracer->player->speed, K_GetKartSpeed(mobj->tracer->player, false))); - topspeed = topspeed - FixedMul(speeddifference, FRACUNIT-FixedDiv(distaway, distbarrier)); - } - } - } - - if (G_BattleGametype()) - { - mobj->friction -= 1228; - if (mobj->friction > FRACUNIT) - mobj->friction = FRACUNIT; - if (mobj->friction < 0) - mobj->friction = 0; - } - - mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); - P_InstaThrust(mobj, mobj->angle, topspeed); - - if (mobj->tracer) - mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->tracer->x, mobj->tracer->y); - else - mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); + // Movement handling has ALL been moved to A_JawzChase K_DriftDustHandling(mobj); - sec2 = P_ThingOnSpecial3DFloor(mobj); - if ((sec2 && GETSECSPECIAL(sec2->special, 3) == 1) - || (P_IsObjectOnRealGround(mobj, mobj->subsector->sector) - && GETSECSPECIAL(mobj->subsector->sector->special, 3) == 1)) + if (P_MobjTouchingSectorSpecial(mobj, 3, 1, true)) K_DoPogoSpring(mobj, 0, 1); break; @@ -8042,6 +8120,9 @@ void P_MobjThinker(mobj_t *mobj) else { mobj_t *ghost = P_SpawnGhostMobj(mobj); + const fixed_t currentspeed = R_PointToDist2(0, 0, mobj->momx, mobj->momy); + fixed_t frictionsafety = (mobj->friction == 0) ? 1 : mobj->friction; + fixed_t thrustamount = 0; if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player) { @@ -8049,18 +8130,30 @@ void P_MobjThinker(mobj_t *mobj) ghost->colorized = true; } - mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); - P_InstaThrust(mobj, mobj->angle, mobj->movefactor); - - if (grounded) + if (!grounded) { - sector_t *sec2 = P_ThingOnSpecial3DFloor(mobj); - if ((sec2 && GETSECSPECIAL(sec2->special, 3) == 1) - || (P_IsObjectOnRealGround(mobj, mobj->subsector->sector) - && GETSECSPECIAL(mobj->subsector->sector->special, 3) == 1)) - K_DoPogoSpring(mobj, 0, 1); + // No friction in the air + frictionsafety = FRACUNIT; } + if (currentspeed >= mobj->movefactor) + { + // Thrust as if you were at top speed, slow down naturally + thrustamount = FixedDiv(mobj->movefactor, frictionsafety) - mobj->movefactor; + } + else + { + const fixed_t beatfriction = FixedDiv(currentspeed, frictionsafety) - currentspeed; + // Thrust to immediately get to top speed + thrustamount = beatfriction + FixedDiv(mobj->movefactor - currentspeed, frictionsafety); + } + + mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); + P_Thrust(mobj, mobj->angle, thrustamount); + + if (P_MobjTouchingSectorSpecial(mobj, 3, 1, true)) + K_DoPogoSpring(mobj, 0, 1); + if (mobj->threshold > 0) mobj->threshold--; @@ -8207,6 +8300,9 @@ void P_MobjThinker(mobj_t *mobj) P_TeleportMove(mobj, mobj->target->x + P_ReturnThrustX(mobj, mobj->angle+ANGLE_180, mobj->target->radius), mobj->target->y + P_ReturnThrustY(mobj, mobj->angle+ANGLE_180, mobj->target->radius), mobj->target->z); P_SetScale(mobj, mobj->target->scale); +#ifdef HWRENDER + mobj->modeltilt = mobj->target->modeltilt; +#endif { player_t *p = NULL; @@ -8346,17 +8442,62 @@ void P_MobjThinker(mobj_t *mobj) return; } + mobj->z = mobj->target->z; + K_MatchGenericExtraFlags(mobj, mobj->target); + + P_TeleportMove(mobj, mobj->target->x + FINECOSINE(mobj->angle >> ANGLETOFINESHIFT), + mobj->target->y + FINESINE(mobj->angle >> ANGLETOFINESHIFT), + mobj->z + mobj->target->height * P_MobjFlip(mobj)); + break; + case MT_TIREGREASE: + if (!mobj->target || P_MobjWasRemoved(mobj->target) || !mobj->target->player + || !mobj->target->player->kartstuff[k_tiregrease]) { + P_RemoveMobj(mobj); + return; + } + + K_MatchGenericExtraFlags(mobj, mobj->target); + + { + const angle_t off = FixedAngle(40*FRACUNIT); + angle_t ang = mobj->target->angle; fixed_t z; + UINT8 trans = (mobj->target->player->kartstuff[k_tiregrease] * (NUMTRANSMAPS+1)) / greasetics; + + if (trans > NUMTRANSMAPS) + trans = NUMTRANSMAPS; + + trans = NUMTRANSMAPS - trans; + z = mobj->target->z; - if (( mobj->eflags & MFE_VERTICALFLIP )) - z -= mobj->height; - else + if (mobj->eflags & MFE_VERTICALFLIP) z += mobj->target->height; - P_TeleportMove(mobj, mobj->target->x + FINECOSINE(mobj->angle >> ANGLETOFINESHIFT), - mobj->target->y + FINESINE(mobj->angle >> ANGLETOFINESHIFT), - z); + + if (mobj->target->momx || mobj->target->momy) + ang = R_PointToAngle2(0, 0, mobj->target->momx, mobj->target->momy); + + if (mobj->extravalue1) + ang = (signed)(ang - off); + else + ang = (signed)(ang + off); + + P_TeleportMove(mobj, + mobj->target->x - FixedMul(mobj->target->radius, FINECOSINE(ang >> ANGLETOFINESHIFT)), + mobj->target->y - FixedMul(mobj->target->radius, FINESINE(ang >> ANGLETOFINESHIFT)), + z); + mobj->angle = ang; + + if (leveltime & 1) + mobj->flags2 |= MF2_DONTDRAW; + + if (trans >= NUMTRANSMAPS) + mobj->flags2 |= MF2_DONTDRAW; + else if (trans == 0) + mobj->frame = (mobj->frame & ~FF_TRANSMASK); + else + mobj->frame = (mobj->frame & ~FF_TRANSMASK)|(trans << FF_TRANSSHIFT); } break; case MT_THUNDERSHIELD: @@ -8519,31 +8660,104 @@ void P_MobjThinker(mobj_t *mobj) } break; case MT_SIGN: // Kart's unique sign behavior - if (mobj->movecount) + if (mobj->movecount != 0) { - if (mobj->z <= mobj->movefactor) + mobj_t *cur = mobj->hnext; + SINT8 newskin = -1; + UINT8 newcolor = SKINCOLOR_NONE; + angle_t endangle = FixedAngle(mobj->extravalue1 << FRACBITS); + + if (mobj->movecount == 1) { - P_SetMobjState(mobj, S_SIGN_END); - if (mobj->info->attacksound) - S_StartSound(mobj, mobj->info->attacksound); - mobj->flags |= MF_NOGRAVITY; // ? - mobj->flags &= ~MF_NOCLIPHEIGHT; - mobj->z = mobj->movefactor; - mobj->movecount = 0; - } - else - { - P_SpawnMobj(mobj->x + (P_RandomRange(-48,48)*mobj->scale), - mobj->y + (P_RandomRange(-48,48)*mobj->scale), - mobj->z + (24*mobj->scale) + (P_RandomRange(-8,8)*mobj->scale), - MT_SIGNSPARKLE); - mobj->flags &= ~MF_NOGRAVITY; - if (abs(mobj->z - mobj->movefactor) <= (512*mobj->scale) && !mobj->cvmem) + if (mobj->z + mobj->momz <= mobj->movefactor) { - if (mobj->info->seesound) - S_StartSound(mobj, mobj->info->seesound); - mobj->cvmem = 1; + if (mobj->info->attacksound) + S_StartSound(mobj, mobj->info->attacksound); + + mobj->z = mobj->movefactor; + mobj->momz = 0; + mobj->movecount = 2; + + newskin = ((skin_t*)mobj->target->skin)-skins; + newcolor = mobj->target->player->skincolor; } + else + { + fixed_t g = (6*mobj->scale); + UINT16 ticstilimpact = abs(mobj->z - mobj->movefactor) / g; + + P_SpawnMobj( + mobj->x + FixedMul(48*mobj->scale, FINECOSINE(mobj->angle >> ANGLETOFINESHIFT)), + mobj->y + FixedMul(48*mobj->scale, FINESINE(mobj->angle >> ANGLETOFINESHIFT)), + mobj->z + ((24 + ((leveltime % 4) * 8)) * mobj->scale), + MT_SIGNSPARKLE + ); + + if (ticstilimpact == (3*TICRATE/2)) + { + if (mobj->info->seesound) + S_StartSound(mobj, mobj->info->seesound); + } + + mobj->angle += ANGLE_45; + mobj->momz = -g; + + if (mobj->angle == endangle + ANGLE_180) + { + if (ticstilimpact <= 8) + { + newskin = ((skin_t*)mobj->target->skin)-skins; + newcolor = mobj->target->player->skincolor; + } + else + { + newskin = leveltime % numskins; + newcolor = skins[newskin].prefcolor; + } + } + } + } + else if (mobj->movecount == 2) + { + if (mobj->angle != endangle) + mobj->angle += ANGLE_11hh; + } + + while (cur && !P_MobjWasRemoved(cur)) + { + fixed_t amt = cur->extravalue1 * mobj->scale; + angle_t dir = mobj->angle + (cur->extravalue2 * ANGLE_90); + fixed_t z = mobj->z + (23*mobj->scale); + + if (cur->state == &states[S_SIGN_FACE]) + { + if (newcolor != SKINCOLOR_NONE) + { + cur->color = KartColor_Opposite[newcolor*2]; + cur->frame = states[S_SIGN_FACE].frame + KartColor_Opposite[newcolor*2+1]; + } + } + else if (cur->state == &states[S_PLAY_SIGN]) + { + z += (5*mobj->scale); + amt += 1; + + if (newskin != -1) + cur->skin = &skins[newskin]; + + if (newcolor != SKINCOLOR_NONE) + cur->color = newcolor; + } + + P_TeleportMove( + cur, + mobj->x + FixedMul(amt, FINECOSINE(dir >> ANGLETOFINESHIFT)), + mobj->y + FixedMul(amt, FINESINE(dir >> ANGLETOFINESHIFT)), + z + ); + cur->angle = dir + ANGLE_90; + + cur = cur->hnext; } } break; @@ -9061,6 +9275,9 @@ void P_MobjThinker(mobj_t *mobj) } break; case MT_KARMAFIREWORK: + if (mobj->flags & MF_NOGRAVITY) + break; + if (mobj->momz == 0) { P_RemoveMobj(mobj); @@ -9076,6 +9293,40 @@ void P_MobjThinker(mobj_t *mobj) trail->color = mobj->color; } break; + case MT_RANDOMITEM: + if (G_BattleGametype() && mobj->threshold == 70) + { + mobj->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + mobj->colorized = true; + + if (battleovertime.enabled) + { + angle_t ang = FixedAngle((leveltime % 360) << FRACBITS); + fixed_t z = battleovertime.z; + fixed_t dist; + mobj_t *ghost; + + /*if (z < mobj->subsector->sector->floorheight) + z = mobj->subsector->sector->floorheight;*/ + + if (mobj->extravalue1 < 512) + mobj->extravalue1++; + dist = mobj->extravalue1 * mapobjectscale; + + P_TeleportMove(mobj, battleovertime.x + P_ReturnThrustX(NULL, ang, dist), + battleovertime.y + P_ReturnThrustY(NULL, ang, dist), z); + + ghost = P_SpawnGhostMobj(mobj); + ghost->fuse = 4; + ghost->frame |= FF_FULLBRIGHT; + } + } + else + { + mobj->color = SKINCOLOR_NONE; + mobj->colorized = false; + } + break; //} case MT_TURRET: P_MobjCheckWater(mobj); @@ -9122,13 +9373,9 @@ void P_MobjThinker(mobj_t *mobj) break; case MT_BLUEFLAG: case MT_REDFLAG: - { - sector_t *sec2; - sec2 = P_ThingOnSpecial3DFloor(mobj); - if ((sec2 && GETSECSPECIAL(sec2->special, 4) == 2) || (GETSECSPECIAL(mobj->subsector->sector->special, 4) == 2)) - mobj->fuse = 1; // Return to base. - break; - } + if (P_MobjTouchingSectorSpecial(mobj, 4, 2, false)) + mobj->fuse = 1; // Return to base. + break; case MT_CANNONBALL: #ifdef FLOORSPLATS R_AddFloorSplat(mobj->tracer->subsector, mobj->tracer, "TARGET", mobj->tracer->x, @@ -9340,7 +9587,7 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s P_RemoveMobj(mobj); // make sure they disappear return; case MT_RANDOMITEM: - if (G_BattleGametype()) + if (G_BattleGametype() && (mobj->threshold != 70)) { if (mobj->threshold != 69) break; @@ -9356,8 +9603,11 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s else newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type); + P_SpawnMobj(newmobj->x, newmobj->y, newmobj->z, MT_EXPLODE); // poof into existance // Transfer flags2 (strongbox, objectflip) newmobj->flags2 = mobj->flags2 & ~MF2_DONTDRAW; + if (mobj->threshold == 70) + newmobj->threshold = 70; } P_RemoveMobj(mobj); // make sure they disappear return; @@ -9447,7 +9697,9 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s || mobj->type == MT_BIGTUMBLEWEED || mobj->type == MT_LITTLETUMBLEWEED || mobj->type == MT_CANNONBALLDECOR - || mobj->type == MT_FALLINGROCK) { + || mobj->type == MT_FALLINGROCK + || mobj->type == MT_ORBINAUT + || mobj->type == MT_JAWZ || mobj->type == MT_JAWZ_DUD) { P_TryMove(mobj, mobj->x, mobj->y, true); // Sets mo->standingslope correctly //if (mobj->standingslope) CONS_Printf("slope physics on mobj\n"); P_ButteredSlope(mobj); @@ -9711,6 +9963,63 @@ void P_SceneryThinker(mobj_t *mobj) // GAME SPAWN FUNCTIONS // +static void P_DefaultMobjShadowScale(mobj_t *thing) +{ + thing->shadowscale = 0; + thing->whiteshadow = (thing->frame & FF_FULLBRIGHT); + + switch (thing->type) + { + case MT_PLAYER: + case MT_SMALLMACE: + case MT_BIGMACE: + case MT_PUMA: + case MT_BIGPUMA: + case MT_FALLINGROCK: + case MT_SMK_MOLE: + case MT_SMK_THWOMP: + case MT_BATTLEBUMPER: + case MT_BANANA: + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + case MT_JAWZ: + case MT_JAWZ_DUD: + case MT_JAWZ_SHIELD: + case MT_SSMINE: + case MT_SSMINE_SHIELD: + case MT_BALLHOG: + case MT_SINK: + case MT_THUNDERSHIELD: + case MT_ROCKETSNEAKER: + case MT_SPB: + thing->shadowscale = 3*FRACUNIT/2; + break; + case MT_BANANA_SHIELD: + thing->shadowscale = 12*FRACUNIT/5; + break; + case MT_RANDOMITEM: + thing->shadowscale = FRACUNIT/2; + thing->whiteshadow = false; + break; + case MT_EGGMANITEM: + thing->shadowscale = FRACUNIT; + thing->whiteshadow = false; + break; + case MT_EGGMANITEM_SHIELD: + thing->shadowscale = 3*FRACUNIT/2; + thing->whiteshadow = false; + break; + case MT_RING: + case MT_FLOATINGITEM: + thing->shadowscale = FRACUNIT/2; + break; + default: + if (thing->flags & (MF_ENEMY|MF_BOSS)) + thing->shadowscale = FRACUNIT; + break; + } +} + // // P_SpawnMobj // @@ -9811,6 +10120,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->colorized = false; + // Set shadowscale here, before spawn hook so that Lua can change it + P_DefaultMobjShadowScale(mobj); + #ifdef HAVE_BLUA // DANGER! This can cause P_SpawnMobj to return NULL! // Avoid using P_RemoveMobj on the newly created mobj in "MobjSpawn" Lua hooks! @@ -10100,29 +10412,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; } - switch (mobj->type) - { - case MT_PLAYER: - case MT_SMALLMACE: case MT_BIGMACE: - case MT_PUMA: case MT_BIGPUMA: - case MT_FALLINGROCK: - case MT_SMK_MOLE: case MT_SMK_THWOMP: - //case MT_RANDOMITEM: - case MT_FLOATINGITEM: - case MT_BATTLEBUMPER: - case MT_BANANA: case MT_BANANA_SHIELD: - //case MT_EGGMANITEM: case MT_EGGMANITEM_SHIELD: - case MT_ORBINAUT: case MT_ORBINAUT_SHIELD: - case MT_JAWZ: case MT_JAWZ_DUD: case MT_JAWZ_SHIELD: - case MT_SSMINE: case MT_SSMINE_SHIELD: - case MT_BALLHOG: case MT_SINK: - case MT_THUNDERSHIELD: case MT_ROCKETSNEAKER: - case MT_SPB: - P_SpawnShadowMobj(mobj); - default: - break; - } - if (!(mobj->flags & MF_NOTHINK)) P_AddThinker(&mobj->thinker); @@ -10158,133 +10447,23 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) return mobj; } -// -// P_SpawnShadowMobj -// warning: Do not send a shadow mobj as a caster into here, or try to spawn spawn shadows for shadows in P_SpawnMobj, we do not want recursive shadows -// -mobj_t *P_SpawnShadowMobj(mobj_t * caster) -{ - const mobjinfo_t *info = &mobjinfo[MT_SHADOW]; - state_t *st; - mobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); - - // this is officially a mobj, declared as soon as possible. - mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; - mobj->type = MT_SHADOW; - mobj->info = info; - - mobj->x = caster->x; - mobj->y = caster->y; - - mobj->radius = info->radius; - mobj->height = info->height; - mobj->flags = info->flags; - - mobj->health = info->spawnhealth; - - mobj->reactiontime = info->reactiontime; - - mobj->lastlook = -1; // stuff moved in P_enemy.P_LookForPlayer - - // do not set the state with P_SetMobjState, - // because action routines can not be called yet - if (caster->frame & FF_FULLBRIGHT) - st = &states[S_WHITESHADOW]; - else - st = &states[info->spawnstate]; - - mobj->state = st; - mobj->tics = st->tics; - mobj->sprite = st->sprite; - mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits.. - P_SetupStateAnimation(mobj, st); - - mobj->friction = ORIG_FRICTION; - - mobj->movefactor = ORIG_FRICTION_FACTOR; - - // All mobjs are created at 100% scale. - mobj->scale = FRACUNIT; - mobj->destscale = mobj->scale; - mobj->scalespeed = FRACUNIT/12; - - if (mapobjectscale != FRACUNIT) //&& !(mobj->type == MT_BLACKEGGMAN) - { - mobj->destscale = mapobjectscale; - mobj->scalespeed = mapobjectscale/12; - } - - P_SetScale(mobj, mobj->destscale); - - // set subsector and/or block links - P_SetThingPosition(mobj); - I_Assert(mobj->subsector != NULL); - - // Make sure scale matches destscale immediately when spawned - P_SetScale(mobj, mobj->destscale); - - mobj->floorz = mobj->subsector->sector->floorheight; - mobj->ceilingz = mobj->subsector->sector->ceilingheight; - - // Tells MobjCheckWater that the water height was not set. - mobj->watertop = INT32_MAX; - - mobj->z = mobj->floorz; - - // defaults onground - if (mobj->z == mobj->floorz) - mobj->eflags |= MFE_ONGROUND; - - if (!(mobj->flags & MF_NOTHINK)) - P_AddThinker(&mobj->thinker); - - // Call action functions when the state is set - if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC)) - { - if (levelloading) - { - // Cache actions in a linked list - // with function pointer, and - // var1 & var2, which will be executed - // when the level finishes loading. - P_AddCachedAction(mobj, mobj->info->spawnstate); - } - else - { - var1 = st->var1; - var2 = st->var2; -#ifdef HAVE_BLUA - astate = st; -#endif - st->action.acp1(mobj); - // DANGER! This is the ONLY way for P_SpawnMobj to return NULL! - // Avoid using MF_RUNSPAWNFUNC on mobjs whose spawn state expects target or tracer to already be set! - if (P_MobjWasRemoved(mobj)) - return NULL; - } - } - - if (CheckForReverseGravity && !(mobj->flags & MF_NOBLOCKMAP)) - P_CheckGravity(mobj, false); - - P_SetTarget(&mobj->target, caster); // set the shadow's caster as the target - - return mobj; -} - static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) { + const mobjinfo_t *info = &mobjinfo[type]; state_t *st; precipmobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); fixed_t starting_floorz; + mobj->type = type; + mobj->info = info; + mobj->x = x; mobj->y = y; - mobj->flags = mobjinfo[type].flags; + mobj->flags = info->flags; // do not set the state with P_SetMobjState, // because action routines can not be called yet - st = &states[mobjinfo[type].spawnstate]; + st = &states[info->spawnstate]; mobj->state = st; mobj->tics = st->tics; @@ -10307,7 +10486,7 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype mobj->subsector->sector->ceilingheight; mobj->z = z; - mobj->momz = mobjinfo[type].speed; + mobj->momz = info->speed; mobj->thinker.function.acp1 = (actionf_p1)P_NullPrecipThinker; P_AddThinker(&mobj->thinker); @@ -10324,21 +10503,6 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype return mobj; } -static inline precipmobj_t *P_SpawnRainMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) -{ - precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type); - mo->precipflags |= PCF_RAIN; - //mo->thinker.function.acp1 = (actionf_p1)P_RainThinker; - return mo; -} - -static inline precipmobj_t *P_SpawnSnowMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) -{ - precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type); - //mo->thinker.function.acp1 = (actionf_p1)P_SnowThinker; - return mo; -} - // // P_RemoveMobj // @@ -10396,9 +10560,6 @@ void P_RemoveMobj(mobj_t *mobj) if (mobj->type == MT_OVERLAY) P_RemoveOverlay(mobj); - if (mobj->type == MT_SHADOW) - P_RemoveShadow(mobj); - if (mobj->type == MT_SPB) spbplace = -1; @@ -10527,8 +10688,10 @@ consvar_t cv_suddendeath = {"suddendeath", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHE void P_SpawnPrecipitation(void) { - INT32 i, mrand; - fixed_t basex, basey, x, y, height; + INT32 i, j, k; + mobjtype_t type = precipprops[curWeather].type; + UINT8 randomstates = (UINT8)mobjinfo[type].damage; + fixed_t basex, basey, x, y, z, height; subsector_t *precipsector = NULL; precipmobj_t *rainmo = NULL; @@ -10541,8 +10704,9 @@ void P_SpawnPrecipitation(void) basex = bmaporgx + (i % bmapwidth) * MAPBLOCKSIZE; basey = bmaporgy + (i / bmapwidth) * MAPBLOCKSIZE; - //for (j = 0; j < cv_precipdensity.value; ++j) -- density is 1 for kart always { + UINT16 numparticles = 0; + x = basex + ((M_RandomKey(MAPBLOCKUNITS<<3)<>3); y = basey + ((M_RandomKey(MAPBLOCKUNITS<<3)<>3); @@ -10557,40 +10721,43 @@ void P_SpawnPrecipitation(void) if (precipsector->sector->ceilingpic != skyflatnum) continue; + height = precipsector->sector->ceilingheight - precipsector->sector->floorheight; + // Exists, but is too small for reasonable precipitation. - if (!(precipsector->sector->floorheight <= precipsector->sector->ceilingheight - (32<sector->ceilingheight; + // Hack around a quirk of this entire system, where taller sectors look like they get less precipitation. + numparticles = 1 + (height / (MAPBLOCKUNITS<<4<sector->ceilingheight; + + for (j = 0; j < numparticles; j++) { - rainmo = P_SpawnSnowMobj(x, y, height, MT_SNOWFLAKE); - mrand = M_RandomByte(); - if (mrand < 64) - P_SetPrecipMobjState(rainmo, S_SNOW3); - else if (mrand < 144) - P_SetPrecipMobjState(rainmo, S_SNOW2); + rainmo = P_SpawnPrecipMobj(x, y, z, type); + + if (randomstates > 0) + { + UINT8 mrand = M_RandomByte(); + UINT8 threshold = UINT8_MAX / (randomstates + 1); + statenum_t st = mobjinfo[type].spawnstate; + + for (k = 0; k < randomstates; k++) + { + if (mrand < (threshold * (k+1))) + { + P_SetPrecipMobjState(rainmo, st+k+1); + break; + } + } + } + + // Randomly assign a height, now that floorz is set. + rainmo->z = M_RandomRange(rainmo->floorz>>FRACBITS, rainmo->ceilingz>>FRACBITS)<z = M_RandomRange(rainmo->floorz>>FRACBITS, rainmo->ceilingz>>FRACBITS)< 255) volume = 255; - if (sounds_rain && (!leveltime || leveltime % 80 == 1)) - S_StartSoundAtVolume(players[displayplayers[0]].mo, sfx_rainin, volume); + if (rainsfx != sfx_None && (!leveltime || leveltime % rainfreq == 1)) + S_StartSoundAtVolume(players[displayplayers[0]].mo, rainsfx, volume); if (!sounds_thunder) return; @@ -10715,6 +10873,50 @@ void P_PrecipitationEffects(void) } } +void P_RespawnBattleBoxes(void) +{ + thinker_t *th; + + if (!G_BattleGametype()) + return; + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + mobj_t *box; + mobj_t *newmobj; + + if (th->function.acp1 != (actionf_p1)P_MobjThinker) // not a mobj + continue; + + box = (mobj_t *)th; + + if (box->type != MT_RANDOMITEM || box->threshold != 68 || box->fuse) // only popped items + continue; + + // Respawn from mapthing if you have one! + if (box->spawnpoint) + { + P_SpawnMapThing(box->spawnpoint); + newmobj = box->spawnpoint->mobj; // this is set to the new mobj in P_SpawnMapThing + P_SpawnMobj(box->spawnpoint->mobj->x, box->spawnpoint->mobj->y, box->spawnpoint->mobj->z, MT_EXPLODE); // poof into existance + } + else + { + newmobj = P_SpawnMobj(box->x, box->y, box->z, box->type); + P_SpawnMobj(newmobj->x, newmobj->y, newmobj->z, MT_EXPLODE); // poof into existance + } + + // Transfer flags2 (strongbox, objectflip) + newmobj->flags2 = box->flags2; + P_RemoveMobj(box); // make sure they disappear + numgotboxes--; // you've restored a box, remove it from the count + //continue; -- irrelevant? + } + + if (numgotboxes < 0) + numgotboxes = 0; +} + // // P_RespawnSpecials // @@ -10728,45 +10930,7 @@ void P_RespawnSpecials(void) mapthing_t *mthing = NULL; if (G_BattleGametype() && numgotboxes >= (4*nummapboxes/5)) // Battle Mode respawns all boxes in a different way - { - thinker_t *th; - - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - mobj_t *box; - mobj_t *newmobj; - - if (th->function.acp1 != (actionf_p1)P_MobjThinker) // not a mobj - continue; - - box = (mobj_t *)th; - - if (box->type != MT_RANDOMITEM || box->threshold != 68 || box->fuse) // only popped items - continue; - - // Respawn from mapthing if you have one! - if (box->spawnpoint) - { - P_SpawnMapThing(box->spawnpoint); - newmobj = box->spawnpoint->mobj; // this is set to the new mobj in P_SpawnMapThing - P_SpawnMobj(box->spawnpoint->mobj->x, box->spawnpoint->mobj->y, box->spawnpoint->mobj->z, MT_EXPLODE); // poof into existance - } - else - { - newmobj = P_SpawnMobj(box->x, box->y, box->z, box->type); - P_SpawnMobj(newmobj->x, newmobj->y, newmobj->z, MT_EXPLODE); // poof into existance - } - - // Transfer flags2 (strongbox, objectflip) - newmobj->flags2 = box->flags2; - P_RemoveMobj(box); // make sure they disappear - numgotboxes--; // you've restored a box, remove it from the count - //continue; -- irrelevant? - } - - if (numgotboxes < 0) - numgotboxes = 0; - } + P_RespawnBattleBoxes(); // wait time depends on player count for (p = 0; p < MAXPLAYERS; p++) @@ -10778,8 +10942,16 @@ void P_RespawnSpecials(void) if (pcount == 1) // No respawn when alone return; else if (pcount > 1) + { time = (180 - (pcount * 10))*TICRATE; + // If the map is longer or shorter than 3 laps, then adjust ring respawn to account for this. + // 5 lap courses would have more retreaded ground, while 2 lap courses would have less. + if ((mapheaderinfo[gamemap-1]->numlaps != 3) + && !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE)) + time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps); + } + // only respawn items when cv_itemrespawn is on //if (!cv_itemrespawn.value) // TODO: remove this cvar //return; @@ -12079,7 +12251,7 @@ ML_NOCLIMB : Direction not controllable { if (mthing->options & MTF_AMBUSH) { - if (i == MT_YELLOWDIAG || i == MT_REDDIAG) + if (mobj->flags & MF_SPRING && mobj->info->damage) mobj->angle += ANGLE_22h; if (mobj->flags & MF_NIGHTSITEM) diff --git a/src/p_mobj.h b/src/p_mobj.h index dfc8fc738..03a19870c 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -253,25 +253,23 @@ typedef enum // PRECIPITATION flags ?! ?! ?! // typedef enum { - // Don't draw. - PCF_INVISIBLE = 1, - // Above pit. - PCF_PIT = 2, - // Above FOF. - PCF_FOF = 4, - // Above MOVING FOF (this means we need to keep floorz up to date...) - PCF_MOVINGFOF = 8, - // Is rain. - PCF_RAIN = 16, - // Ran the thinker this tic. - PCF_THUNK = 32, + PCF_INVISIBLE = 1, // Don't draw. + PCF_PIT = 1<<1, // Above pit. + PCF_FOF = 1<<2, // Above FOF. + PCF_MOVINGFOF = 1<<3, // Above MOVING FOF (this means we need to keep floorz up to date...) + PCF_SPLASH = 1<<4, // Splashed on the ground, return to the ceiling after the animation's over + PCF_THUNK = 1<<5, // Ran the thinker this tic. } precipflag_t; + // Map Object definition. typedef struct mobj_s { // List: thinker links. thinker_t thinker; + mobjtype_t type; + const mobjinfo_t *info; // &mobjinfo[mobj->type] + // Info for drawing: position. fixed_t x, y, z; @@ -321,9 +319,6 @@ typedef struct mobj_s struct mobj_s *hnext; struct mobj_s *hprev; - mobjtype_t type; - const mobjinfo_t *info; // &mobjinfo[mobj->type] - INT32 health; // for player this is rings + 1 // Movement direction, movement generation (zig-zagging). @@ -370,10 +365,16 @@ typedef struct mobj_s #ifdef ESLOPE struct pslope_s *standingslope; // The slope that the object is standing on (shouldn't need synced in savegames, right?) +#ifdef HWRENDER + struct pslope_s *modeltilt; // Slope used for model tilting. Also is not synched, this is totally visual. +#endif #endif boolean colorized; // Whether the mobj uses the rainbow colormap + fixed_t shadowscale; // If this object casts a shadow, and the size relative to radius + boolean whiteshadow; // Use white shadow, set to true by default for fullbright objects + // WARNING: New fields must be added separately to savegame and Lua. } mobj_t; @@ -389,6 +390,9 @@ typedef struct precipmobj_s // List: thinker links. thinker_t thinker; + mobjtype_t type; + const mobjinfo_t *info; // &mobjinfo[mobj->type] + // Info for drawing: position. fixed_t x, y, z; @@ -456,8 +460,7 @@ void P_SpawnParaloop(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 numb boolean P_BossTargetPlayer(mobj_t *actor, boolean closest); boolean P_SupermanLook4Players(mobj_t *actor); void P_DestroyRobots(void); -void P_SnowThinker(precipmobj_t *mobj); -void P_RainThinker(precipmobj_t *mobj); +void P_PrecipThinker(precipmobj_t *mobj); void P_NullPrecipThinker(precipmobj_t *mobj); void P_RemovePrecipMobj(precipmobj_t *mobj); void P_SetScale(mobj_t *mobj, fixed_t newscale); diff --git a/src/p_saveg.c b/src/p_saveg.c index 1d7dbeef4..d51240521 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -34,6 +34,9 @@ #include "p_slopes.h" #endif +// SRB2Kart +#include "k_pwrlv.h" + savedata_t savedata; UINT8 *save_p; @@ -953,8 +956,8 @@ typedef enum MD2_EXTVAL2 = 1<<6, MD2_HNEXT = 1<<7, MD2_HPREV = 1<<8, - MD2_COLORIZED = 1<<9, - MD2_WAYPOINTCAP = 1<<10 + MD2_COLORIZED = 1<<9, + MD2_WAYPOINTCAP = 1<<10 #ifdef ESLOPE , MD2_SLOPE = 1<<11 #endif @@ -2150,7 +2153,12 @@ static void LoadMobjThinker(actionf_p1 thinker) mobj->hprev = (mobj_t *)(size_t)READUINT32(save_p); #ifdef ESLOPE if (diff2 & MD2_SLOPE) + { mobj->standingslope = P_SlopeById(READUINT16(save_p)); +#ifdef HWRENDER + mobj->modeltilt = mobj->standingslope; +#endif + } #endif if (diff2 & MD2_COLORIZED) mobj->colorized = READUINT8(save_p); @@ -3106,7 +3114,7 @@ static inline void P_NetArchiveSpecials(void) WRITEUINT32(save_p, 0xffffffff); // Sky number - WRITEINT32(save_p, globallevelskynum); + WRITESTRINGN(save_p, globallevelskytexture, 9); // Current global weather type WRITEUINT8(save_p, globalweather); @@ -3125,8 +3133,8 @@ static inline void P_NetArchiveSpecials(void) // static void P_NetUnArchiveSpecials(void) { + char skytex[9]; size_t i; - INT32 j; if (READUINT32(save_p) != ARCHIVEBLOCK_SPECIALS) I_Error("Bad $$$.sav at archive block Specials"); @@ -3139,9 +3147,9 @@ static void P_NetUnArchiveSpecials(void) itemrespawntime[iquehead++] = READINT32(save_p); } - j = READINT32(save_p); - if (j != globallevelskynum) - P_SetupLevelSky(j, true); + READSTRINGN(save_p, skytex, sizeof(skytex)); + if (strcmp(skytex, globallevelskytexture)) + P_SetupLevelSky(skytex, true); globalweather = READUINT8(save_p); @@ -3297,14 +3305,28 @@ static void P_NetArchiveMisc(void) WRITEUINT8(save_p, franticitems); WRITEUINT8(save_p, comeback); + WRITESINT8(save_p, speedscramble); + WRITESINT8(save_p, encorescramble); + for (i = 0; i < 4; i++) WRITESINT8(save_p, battlewanted[i]); + // battleovertime_t + WRITEUINT16(save_p, battleovertime.enabled); + WRITEFIXED(save_p, battleovertime.radius); + WRITEFIXED(save_p, battleovertime.minradius); + WRITEFIXED(save_p, battleovertime.x); + WRITEFIXED(save_p, battleovertime.y); + WRITEFIXED(save_p, battleovertime.z); + WRITEUINT32(save_p, wantedcalcdelay); WRITEUINT32(save_p, indirectitemcooldown); WRITEUINT32(save_p, hyubgone); WRITEUINT32(save_p, mapreset); - WRITEUINT8(save_p, nospectategrief); + + for (i = 0; i < MAXPLAYERS; i++) + WRITEINT16(save_p, nospectategrief[i]); + WRITEUINT8(save_p, thwompsactive); WRITESINT8(save_p, spbplace); @@ -3406,14 +3428,28 @@ static inline boolean P_NetUnArchiveMisc(void) franticitems = (boolean)READUINT8(save_p); comeback = (boolean)READUINT8(save_p); + speedscramble = READSINT8(save_p); + encorescramble = READSINT8(save_p); + for (i = 0; i < 4; i++) battlewanted[i] = READSINT8(save_p); + // battleovertime_t + battleovertime.enabled = READUINT16(save_p); + battleovertime.radius = READFIXED(save_p); + battleovertime.minradius = READFIXED(save_p); + battleovertime.x = READFIXED(save_p); + battleovertime.y = READFIXED(save_p); + battleovertime.z = READFIXED(save_p); + wantedcalcdelay = READUINT32(save_p); indirectitemcooldown = READUINT32(save_p); hyubgone = READUINT32(save_p); mapreset = READUINT32(save_p); - nospectategrief = READUINT8(save_p); + + for (i = 0; i < MAXPLAYERS; i++) + nospectategrief[i] = READINT16(save_p); + thwompsactive = (boolean)READUINT8(save_p); spbplace = READSINT8(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 2e88f664c..e0b3550dc 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -84,6 +84,7 @@ // SRB2Kart #include "k_kart.h" +#include "k_pwrlv.h" #include "k_waypoint.h" // @@ -181,6 +182,8 @@ FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg) static void P_ClearSingleMapHeaderInfo(INT16 i) { const INT16 num = (INT16)(i-1); + INT32 exists = (mapheaderinfo[num]->menuflags & LF2_EXISTSHACK); + DEH_WriteUndoline("LEVELNAME", mapheaderinfo[num]->lvlttl, UNDO_NONE); mapheaderinfo[num]->lvlttl[0] = '\0'; DEH_WriteUndoline("SUBTITLE", mapheaderinfo[num]->subttl, UNDO_NONE); @@ -208,8 +211,9 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) mapheaderinfo[num]->forcecharacter[0] = '\0'; DEH_WriteUndoline("WEATHER", va("%d", mapheaderinfo[num]->weather), UNDO_NONE); mapheaderinfo[num]->weather = 0; - DEH_WriteUndoline("SKYNUM", va("%d", mapheaderinfo[num]->skynum), UNDO_NONE); - mapheaderinfo[num]->skynum = 1; + DEH_WriteUndoline("SKYTEXTURE", va("%d", mapheaderinfo[num]->skytexture), UNDO_NONE); + snprintf(mapheaderinfo[num]->skytexture, 9, "SKY1"); + mapheaderinfo[num]->skytexture[8] = 0; DEH_WriteUndoline("SKYBOXSCALEX", va("%d", mapheaderinfo[num]->skybox_scalex), UNDO_NONE); mapheaderinfo[num]->skybox_scalex = 16; DEH_WriteUndoline("SKYBOXSCALEY", va("%d", mapheaderinfo[num]->skybox_scaley), UNDO_NONE); @@ -245,7 +249,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) DEH_WriteUndoline("LEVELFLAGS", va("%d", mapheaderinfo[num]->levelflags), UNDO_NONE); mapheaderinfo[num]->levelflags = 0; DEH_WriteUndoline("MENUFLAGS", va("%d", mapheaderinfo[num]->menuflags), UNDO_NONE); - mapheaderinfo[num]->menuflags = (mainwads ? 0 : LF2_EXISTSHACK); // see p_setup.c - prevents replacing maps in addons with easier versions + mapheaderinfo[num]->menuflags = exists; // see p_setup.c - prevents replacing maps in addons with easier versions // TODO grades support for delfile (pfft yeah right) P_DeleteGrades(num); // SRB2Kart @@ -2256,17 +2260,18 @@ static inline boolean P_CheckLevel(lumpnum_t lumpnum) /** Sets up a sky texture to use for the level. * The sky texture is used instead of F_SKY1. */ -void P_SetupLevelSky(INT32 skynum, boolean global) +void P_SetupLevelSky(const char *skytexname, boolean global) { - char skytexname[12]; + char tex[9]; + strncpy(tex, skytexname, 9); + tex[8] = 0; - sprintf(skytexname, "SKY%d", skynum); - skytexture = R_TextureNumForName(skytexname); - levelskynum = skynum; + skytexture = R_TextureNumForName(tex); + strncpy(levelskytexture, tex, 9); // Global change if (global) - globallevelskynum = levelskynum; + strncpy(globallevelskytexture, tex, 9); // Don't go beyond for dedicated servers if (dedicated) @@ -2386,22 +2391,30 @@ static void P_LevelInitStuff(void) // SRB2Kart: map load variables if (modeattacking) // Just play it safe and set everything { - gamespeed = 2; + gamespeed = KARTSPEED_HARD; franticitems = false; comeback = true; } else { if (G_BattleGametype()) - gamespeed = 0; + gamespeed = KARTSPEED_EASY; else - gamespeed = (UINT8)cv_kartspeed.value; + { + if (cv_kartspeed.value == KARTSPEED_AUTO) + gamespeed = ((speedscramble == -1) ? KARTSPEED_NORMAL : (UINT8)speedscramble); + else + gamespeed = (UINT8)cv_kartspeed.value; + } franticitems = (boolean)cv_kartfrantic.value; comeback = (boolean)cv_kartcomeback.value; } for (i = 0; i < 4; i++) battlewanted[i] = -1; + + memset(&battleovertime, 0, sizeof(struct battleovertime)); + speedscramble = encorescramble = -1; } // @@ -2779,7 +2792,7 @@ boolean P_SetupLevel(boolean skipprecip) // use gamemap to get map number. // 99% of the things already did, so. // Map header should always be in place at this point - INT32 i, loadprecip = 1, ranspecialwipe = 0; + INT32 i, loadprecip = 1; INT32 loademblems = 1; INT32 fromnetsave = 0; boolean loadedbm = false; @@ -2858,36 +2871,50 @@ boolean P_SetupLevel(boolean skipprecip) S_StartSound(NULL, sfx_ruby1); + // Fade to an inverted screen, with a circle fade... F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 209); + V_EncoreInvertScreen(); F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_speclevel_towhite], false); + F_RunWipe(wipedefs[wipe_encore_toinvert], false, NULL, false, false); + + // Hold on invert for extra effect. + // (This define might be useful for other areas of code? Not sure) +#define WAIT(timetowait) \ + locstarttime = nowtime = lastwipetic; \ + endtime = locstarttime + timetowait; \ + while (nowtime < endtime) \ + { \ + while (!((nowtime = I_GetTime()) - lastwipetic)) \ + I_Sleep(); \ + lastwipetic = nowtime; \ + if (moviemode) \ + M_SaveFrame(); \ + NetKeepAlive(); \ + } \ + + WAIT((3*TICRATE)/2); + S_StartSound(NULL, sfx_ruby2); + + // Then fade to a white screen F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0); - F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_level_final], false); - locstarttime = nowtime = lastwipetic; - endtime = locstarttime + (3*TICRATE)/2; + F_RunWipe(wipedefs[wipe_encore_towhite], false, "FADEMAP1", false, true); // wiggle the screen during this! - // Hold on white for extra effect. - while (nowtime < endtime) - { - // wait loop - while (!((nowtime = I_GetTime()) - lastwipetic)) - I_Sleep(); - lastwipetic = nowtime; - if (moviemode) // make sure we save frames for the white hold too - M_SaveFrame(); + // THEN fade to a black screen. + F_WipeStartScreen(); - // Keep the network alive - NetKeepAlive(); - } + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); - ranspecialwipe = 1; + F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + + // Wait a bit longer. + WAIT((3*TICRATE)/4); } // Make sure all sounds are stopped before Z_FreeTags. @@ -2898,17 +2925,18 @@ boolean P_SetupLevel(boolean skipprecip) // We should be fine starting it here. S_Start(); - levelfadecol = (encoremode && !ranspecialwipe ? 209 : 0); + levelfadecol = (encoremode ? 0 : 31); // Let's fade to white here // But only if we didn't do the encore startup wipe - if (rendermode != render_none && !ranspecialwipe && !demo.rewinding) + if (rendermode != render_none && !demo.rewinding) { F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); F_WipeEndScreen(); - F_RunWipe(wipedefs[(encoremode ? wipe_level_final : wipe_level_toblack)], false); + + F_RunWipe(wipedefs[wipe_level_toblack], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false); } // Reset the palette now all fades have been done @@ -2974,7 +3002,7 @@ boolean P_SetupLevel(boolean skipprecip) CON_SetupBackColormap(); // SRB2 determines the sky texture to be used depending on the map header. - P_SetupLevelSky(mapheaderinfo[gamemap-1]->skynum, true); + P_SetupLevelSky(mapheaderinfo[gamemap-1]->skytexture, true); P_MakeMapMD5(lastloadedmaplumpnum, &mapmd5); @@ -3283,7 +3311,10 @@ boolean P_SetupLevel(boolean skipprecip) indirectitemcooldown = 0; hyubgone = 0; mapreset = 0; - nospectategrief = 0; + + for (i = 0; i < MAXPLAYERS; i++) + nospectategrief[i] = -1; + thwompsactive = false; spbplace = -1; diff --git a/src/p_setup.h b/src/p_setup.h index c4a3aab9a..9abcfe5f6 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -54,7 +54,7 @@ INT32 P_CheckLevelFlat(const char *flatname); extern size_t nummapthings; extern mapthing_t *mapthings; -void P_SetupLevelSky(INT32 skynum, boolean global); +void P_SetupLevelSky(const char *skytexname, boolean global); #ifdef SCANTHINGS void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum); #endif diff --git a/src/p_slopes.c b/src/p_slopes.c index 30605b8da..e623b6f19 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -836,6 +836,9 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) if (P_MobjFlip(thing)*(thing->momz) < 0) { // falling, land on slope thing->momz = -P_MobjFlip(thing); thing->standingslope = slope; +#ifdef HWRENDER + thing->modeltilt = thing->standingslope; +#endif } return; } @@ -852,6 +855,9 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) thing->momz = -P_MobjFlip(thing); thing->standingslope = slope; +#ifdef HWRENDER + thing->modeltilt = thing->standingslope; +#endif } } @@ -871,33 +877,31 @@ void P_ButteredSlope(mobj_t *mo) return; // don't slide down slopes if you can't touch them or you're not affected by gravity if (mo->player) { - if (abs(mo->standingslope->zdelta) < FRACUNIT/4 && !(mo->player->pflags & PF_SPINNING)) + // Changed in kart to only not apply physics on very slight slopes (I think about 4 degree angles) + if (abs(mo->standingslope->zdelta) < FRACUNIT/21 && !(mo->player->pflags & PF_SPINNING)) return; // Don't slide on non-steep slopes unless spinning + // This only means you can be stopped on slopes that aren't steeper than 45 degrees if (abs(mo->standingslope->zdelta) < FRACUNIT/2 && !(mo->player->rmomx || mo->player->rmomy)) return; // Allow the player to stand still on slopes below a certain steepness } - thrust = FINESINE(mo->standingslope->zangle>>ANGLETOFINESHIFT) * 15 / 16 * (mo->eflags & MFE_VERTICALFLIP ? 1 : -1); + thrust = FINESINE(mo->standingslope->zangle>>ANGLETOFINESHIFT) * 4 / 5 * (mo->eflags & MFE_VERTICALFLIP ? 1 : -1); - if (mo->player && (mo->player->pflags & PF_SPINNING)) { - fixed_t mult = 0; + if (mo->player) { + fixed_t mult = FRACUNIT; if (mo->momx || mo->momy) { angle_t angle = R_PointToAngle2(0, 0, mo->momx, mo->momy) - mo->standingslope->xydirection; if (P_MobjFlip(mo) * mo->standingslope->zdelta < 0) angle ^= ANGLE_180; - mult = FINECOSINE(angle >> ANGLETOFINESHIFT); + mult = FRACUNIT + (FRACUNIT + FINECOSINE(angle>>ANGLETOFINESHIFT))*3/2; } - thrust = FixedMul(thrust, FRACUNIT*2/3 + mult/8); + thrust = FixedMul(thrust, mult); } - if (mo->momx || mo->momy) // Slightly increase thrust based on the object's speed - thrust = FixedMul(thrust, FRACUNIT+P_AproxDistance(mo->momx, mo->momy)/16); - // This makes it harder to zigzag up steep slopes, as well as allows greater top speed when rolling down - // Let's get the gravity strength for the object... thrust = FixedMul(thrust, abs(P_GetMobjGravity(mo))); diff --git a/src/p_spec.c b/src/p_spec.c index f7d540d24..1f92ecd5a 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2003,57 +2003,28 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller) // // Switches the weather! // -void P_SwitchWeather(INT32 weathernum) +void P_SwitchWeather(UINT8 newWeather) { boolean purge = false; - INT32 swap = 0; + mobjtype_t swap = MT_NULL; - switch (weathernum) + if (precipprops[newWeather].type == MT_NULL) { - case PRECIP_NONE: // None - if (curWeather == PRECIP_NONE) - return; // Nothing to do. - purge = true; - break; - case PRECIP_STORM: // Storm - case PRECIP_STORM_NOSTRIKES: // Storm w/ no lightning - case PRECIP_RAIN: // Rain - if (curWeather == PRECIP_SNOW || curWeather == PRECIP_BLANK || curWeather == PRECIP_STORM_NORAIN) - swap = PRECIP_RAIN; - break; - case PRECIP_SNOW: // Snow - if (curWeather == PRECIP_SNOW) - return; // Nothing to do. - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES || curWeather == PRECIP_BLANK || curWeather == PRECIP_STORM_NORAIN) - swap = PRECIP_SNOW; // Need to delete the other precips. - break; - case PRECIP_STORM_NORAIN: // Storm w/o rain - if (curWeather == PRECIP_SNOW - || curWeather == PRECIP_STORM - || curWeather == PRECIP_STORM_NOSTRIKES - || curWeather == PRECIP_RAIN - || curWeather == PRECIP_BLANK) - swap = PRECIP_STORM_NORAIN; - else if (curWeather == PRECIP_STORM_NORAIN) - return; - break; - case PRECIP_BLANK: - if (curWeather == PRECIP_SNOW - || curWeather == PRECIP_STORM - || curWeather == PRECIP_STORM_NOSTRIKES - || curWeather == PRECIP_RAIN) - swap = PRECIP_BLANK; - else if (curWeather == PRECIP_STORM_NORAIN) - swap = PRECIP_BLANK; - else if (curWeather == PRECIP_BLANK) - return; - break; - default: - CONS_Debug(DBG_GAMELOGIC, "P_SwitchWeather: Unknown weather type %d.\n", weathernum); - break; + // New type is null, we want to purge the weather. + if (precipprops[curWeather].type == MT_NULL) + return; // Nothing to do. + purge = true; + } + else + { + if (precipprops[curWeather].type != MT_NULL) + { + // There are already existing weather particles to reuse. + swap = precipprops[newWeather].type; + } } - if (purge) + if (purge == true) { thinker_t *think; precipmobj_t *precipmobj; @@ -2068,136 +2039,54 @@ void P_SwitchWeather(INT32 weathernum) P_RemovePrecipMobj(precipmobj); } } - else if (swap && !((swap == PRECIP_BLANK && curWeather == PRECIP_STORM_NORAIN) || (swap == PRECIP_STORM_NORAIN && curWeather == PRECIP_BLANK))) // Rather than respawn all that crap, reuse it! + else if (swap != MT_NULL) // Rather than respawn all that crap, reuse it! { + UINT8 randomstates = (UINT8)mobjinfo[swap].damage; thinker_t *think; precipmobj_t *precipmobj; - state_t *st; + statenum_t st; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker) continue; // not a precipmobj thinker + precipmobj = (precipmobj_t *)think; - if (swap == PRECIP_RAIN) // Snow To Rain + precipmobj->flags = mobjinfo[swap].flags; + + st = mobjinfo[swap].spawnstate; + + if (randomstates > 0) { - precipmobj->flags = mobjinfo[MT_RAIN].flags; - st = &states[mobjinfo[MT_RAIN].spawnstate]; - precipmobj->state = st; - precipmobj->tics = st->tics; - precipmobj->sprite = st->sprite; - precipmobj->frame = st->frame; - precipmobj->momz = mobjinfo[MT_RAIN].speed; + UINT8 mrand = M_RandomByte(); + UINT8 threshold = UINT8_MAX / (randomstates + 1); + UINT8 i; - precipmobj->precipflags &= ~PCF_INVISIBLE; - - precipmobj->precipflags |= PCF_RAIN; - //think->function.acp1 = (actionf_p1)P_RainThinker; + for (i = 0; i < randomstates; i++) + { + if (mrand < (threshold * (i+1))) + { + st += i+1; + break; + } + } } - else if (swap == PRECIP_SNOW) // Rain To Snow - { - INT32 z; - precipmobj->flags = mobjinfo[MT_SNOWFLAKE].flags; - z = M_RandomByte(); + precipmobj->state = &states[st]; + precipmobj->tics = precipmobj->state->tics; + precipmobj->sprite = precipmobj->state->sprite; + precipmobj->frame = precipmobj->state->frame; - if (z < 64) - z = 2; - else if (z < 144) - z = 1; - else - z = 0; - - st = &states[mobjinfo[MT_SNOWFLAKE].spawnstate+z]; - precipmobj->state = st; - precipmobj->tics = st->tics; - precipmobj->sprite = st->sprite; - precipmobj->frame = st->frame; - precipmobj->momz = mobjinfo[MT_SNOWFLAKE].speed; - - precipmobj->precipflags &= ~(PCF_INVISIBLE|PCF_RAIN); - - //think->function.acp1 = (actionf_p1)P_SnowThinker; - } - else if (swap == PRECIP_BLANK || swap == PRECIP_STORM_NORAIN) // Remove precip, but keep it around for reuse. - { - //think->function.acp1 = (actionf_p1)P_NullPrecipThinker; - - precipmobj->precipflags |= PCF_INVISIBLE; - } + precipmobj->momz = mobjinfo[swap].speed; + precipmobj->precipflags &= ~PCF_INVISIBLE; } } - switch (weathernum) - { - case PRECIP_SNOW: // snow - curWeather = PRECIP_SNOW; + curWeather = newWeather; - if (!swap) - P_SpawnPrecipitation(); - - break; - case PRECIP_RAIN: // rain - { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - - curWeather = PRECIP_RAIN; - - if (!dontspawn && !swap) - P_SpawnPrecipitation(); - - break; - } - case PRECIP_STORM: // storm - { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - - curWeather = PRECIP_STORM; - - if (!dontspawn && !swap) - P_SpawnPrecipitation(); - - break; - } - case PRECIP_STORM_NOSTRIKES: // storm w/o lightning - { - boolean dontspawn = false; - - if (curWeather == PRECIP_RAIN || curWeather == PRECIP_STORM || curWeather == PRECIP_STORM_NOSTRIKES) - dontspawn = true; - - curWeather = PRECIP_STORM_NOSTRIKES; - - if (!dontspawn && !swap) - P_SpawnPrecipitation(); - - break; - } - case PRECIP_STORM_NORAIN: // storm w/o rain - curWeather = PRECIP_STORM_NORAIN; - - if (!swap) - P_SpawnPrecipitation(); - - break; - case PRECIP_BLANK: - curWeather = PRECIP_BLANK; - - if (!swap) - P_SpawnPrecipitation(); - - break; - default: - curWeather = PRECIP_NONE; - break; - } + if (swap == MT_NULL && precipprops[newWeather].type != MT_NULL) + P_SpawnPrecipitation(); } // Passed over the finish line forwards @@ -2957,7 +2846,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 423: // Change Sky if ((mo && mo->player && P_IsLocalPlayer(mo->player)) || (line->flags & ML_NOCLIMB)) - P_SetupLevelSky(sides[line->sidenum[0]].textureoffset>>FRACBITS, (line->flags & ML_NOCLIMB)); + P_SetupLevelSky(sides[line->sidenum[0]].text, (line->flags & ML_NOCLIMB)); break; case 424: // Change Weather @@ -3387,6 +3276,76 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } } +static void P_SetupSignObject(mobj_t *sign, mobj_t *pmo) +{ + mobj_t *cur = sign, *prev = NULL; + + // Setup the sign itself + P_SetTarget(&sign->target, pmo); + P_SetMobjState(sign, S_SIGN_POLE); + + sign->movefactor = sign->z; + sign->z += (768*sign->scale) * P_MobjFlip(sign); + sign->movecount = 1; + sign->extravalue1 = AngleFixed(sign->angle) >> FRACBITS; + + // Setup the overlay pieces + // Front + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + P_SetMobjState(cur->hnext, S_SIGN_FACE); + cur->hnext->extravalue1 = 6; + cur->hnext->extravalue2 = 0; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; + + // Player icon + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + cur->hnext->skin = pmo->skin; + P_SetMobjState(cur->hnext, S_PLAY_SIGN); + cur->hnext->extravalue1 = 7; + cur->hnext->extravalue2 = 0; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; + + // Back + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + P_SetMobjState(cur->hnext, S_SIGN_BACK); + cur->hnext->extravalue1 = 6; + cur->hnext->extravalue2 = 2; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; + + // Sides + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + P_SetMobjState(cur->hnext, S_SIGN_SIDE); + cur->hnext->extravalue1 = 30; + cur->hnext->extravalue2 = 1; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; + + cur->hnext = P_SpawnMobj(sign->x, sign->y, sign->z, MT_SIGN_PIECE); + P_SetTarget(&cur->hnext->target, sign); + P_SetMobjState(cur->hnext, S_SIGN_SIDE); + cur->hnext->extravalue1 = 30; + cur->hnext->extravalue2 = 3; + + prev = cur; + cur = cur->hnext; + cur->hprev = prev; +} + // // P_SetupSignExit // @@ -3412,13 +3371,7 @@ void P_SetupSignExit(player_t *player) if (thing->state != &states[thing->info->spawnstate]) continue; - P_SetTarget(&thing->target, player->mo); - P_SetMobjState(thing, S_SIGN1); - - // SRB2Kart: Set sign spinning variables - thing->movefactor = thing->z; - thing->z += (768*thing->scale) * P_MobjFlip(thing); - thing->movecount = 1; + P_SetupSignObject(thing, player->mo); ++numfound; } @@ -3440,14 +3393,7 @@ void P_SetupSignExit(player_t *player) if (thing->state != &states[thing->info->spawnstate]) continue; - P_SetTarget(&thing->target, player->mo); - P_SetMobjState(thing, S_SIGN1); - - // SRB2Kart: Set sign spinning variables - thing->movefactor = thing->z; - thing->z += (768*thing->scale) * P_MobjFlip(thing); - thing->movecount = 1; - + P_SetupSignObject(thing, player->mo); ++numfound; } @@ -3455,14 +3401,11 @@ void P_SetupSignExit(player_t *player) return; // SRB2Kart: FINALLY, add in an alternative if no place is found - if (player->mo) + if (player->mo && !P_MobjWasRemoved(player->mo)) { - mobj_t *sign = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + (768*mapobjectscale), MT_SIGN); - - P_SetTarget(&sign->target, player->mo); - P_SetMobjState(sign, S_SIGN1); - sign->movefactor = player->mo->floorz; - sign->movecount = 1; + thing = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->floorz, MT_SIGN); + thing->angle = player->mo->angle; + P_SetupSignObject(thing, player->mo); } } @@ -3516,7 +3459,7 @@ boolean P_IsFlagAtBase(mobjtype_t flag) } // -// P_PlayerTouchingSectorSpecial +// P_MobjTouchingSectorSpecial // // Replaces the old player->specialsector. // This allows a player to touch more than @@ -3526,60 +3469,86 @@ boolean P_IsFlagAtBase(mobjtype_t flag) // the particular type that it finds. // Returns NULL if it doesn't find it. // -sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 number) +// Sal: Couldn't see a reason for this to +// be a player_t only function. +// +sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number, boolean touchground) { + fixed_t topheight, bottomheight; msecnode_t *node; ffloor_t *rover; - if (!player->mo) + if (!mo) return NULL; // Check default case first - if (GETSECSPECIAL(player->mo->subsector->sector->special, section) == number) - return player->mo->subsector->sector; + if (GETSECSPECIAL(mo->subsector->sector->special, section) == number) + { + if (touchground) + { + topheight = P_GetSpecialTopZ(mo, mo->subsector->sector, mo->subsector->sector); + bottomheight = P_GetSpecialBottomZ(mo, mo->subsector->sector, mo->subsector->sector); + + // Thing must be on top of the floor to be affected... + if (mo->subsector->sector->flags & SF_FLIPSPECIAL_FLOOR) + { + if (!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= bottomheight) + return mo->subsector->sector; + } + + if (mo->subsector->sector->flags & SF_FLIPSPECIAL_CEILING) + { + if ((mo->eflags & MFE_VERTICALFLIP) && mo->z + mo->height >= topheight) + return mo->subsector->sector; + } + } + else + { + return mo->subsector->sector; + } + } // Hmm.. maybe there's a FOF that has it... - for (rover = player->mo->subsector->sector->ffloors; rover; rover = rover->next) + for (rover = mo->subsector->sector->ffloors; rover; rover = rover->next) { - fixed_t topheight, bottomheight; - if (GETSECSPECIAL(rover->master->frontsector->special, section) != number) continue; if (!(rover->flags & FF_EXISTS)) continue; - topheight = P_GetSpecialTopZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); - bottomheight = P_GetSpecialBottomZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); + topheight = P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector); + bottomheight = P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector); // Check the 3D floor's type... - if (rover->flags & FF_BLOCKPLAYER) + if (((rover->flags & FF_BLOCKPLAYER) && mo->player) + || ((rover->flags & FF_BLOCKOTHERS) && !mo->player)) { // Thing must be on top of the floor to be affected... if ((rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR) && !(rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING)) { - if ((player->mo->eflags & MFE_VERTICALFLIP) || player->mo->z != topheight) + if ((mo->eflags & MFE_VERTICALFLIP) || mo->z != topheight) continue; } else if ((rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING) && !(rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR)) { - if (!(player->mo->eflags & MFE_VERTICALFLIP) - || player->mo->z + player->mo->height != bottomheight) + if (!(mo->eflags & MFE_VERTICALFLIP) + || mo->z + mo->height != bottomheight) continue; } else if (rover->master->frontsector->flags & SF_FLIPSPECIAL_BOTH) { - if (!((player->mo->eflags & MFE_VERTICALFLIP && player->mo->z + player->mo->height == bottomheight) - || (!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z == topheight))) + if (!((mo->eflags & MFE_VERTICALFLIP && mo->z + mo->height == bottomheight) + || (!(mo->eflags & MFE_VERTICALFLIP) && mo->z == topheight))) continue; } } else { // Water and DEATH FOG!!! heh - if (player->mo->z > topheight || (player->mo->z + player->mo->height) < bottomheight) + if (mo->z > topheight || (mo->z + mo->height) < bottomheight) continue; } @@ -3587,64 +3556,86 @@ sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 n return rover->master->frontsector; } - for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next) + for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) { if (GETSECSPECIAL(node->m_sector->special, section) == number) { // This sector has the special we're looking for, but // are we allowed to touch it? - if (node->m_sector == player->mo->subsector->sector + if (node->m_sector == mo->subsector->sector || (node->m_sector->flags & SF_TRIGGERSPECIAL_TOUCH)) - return node->m_sector; + { + if (touchground) + { + topheight = P_GetSpecialTopZ(mo, node->m_sector, node->m_sector); + bottomheight = P_GetSpecialBottomZ(mo, node->m_sector, node->m_sector); + + // Thing must be on top of the floor to be affected... + if (node->m_sector->flags & SF_FLIPSPECIAL_FLOOR) + { + if (!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= bottomheight) + return node->m_sector; + } + + if (node->m_sector->flags & SF_FLIPSPECIAL_CEILING) + { + if ((mo->eflags & MFE_VERTICALFLIP) && mo->z + mo->height >= topheight) + return node->m_sector; + } + } + else + { + return node->m_sector; + } + } } // Hmm.. maybe there's a FOF that has it... for (rover = node->m_sector->ffloors; rover; rover = rover->next) { - fixed_t topheight, bottomheight; - if (GETSECSPECIAL(rover->master->frontsector->special, section) != number) continue; if (!(rover->flags & FF_EXISTS)) continue; - topheight = P_GetSpecialTopZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); - bottomheight = P_GetSpecialBottomZ(player->mo, sectors + rover->secnum, player->mo->subsector->sector); + topheight = P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector); + bottomheight = P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector); // Check the 3D floor's type... - if (rover->flags & FF_BLOCKPLAYER) + if (((rover->flags & FF_BLOCKPLAYER) && mo->player) + || ((rover->flags & FF_BLOCKOTHERS) && !mo->player)) { // Thing must be on top of the floor to be affected... if ((rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR) && !(rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING)) { - if ((player->mo->eflags & MFE_VERTICALFLIP) || player->mo->z != topheight) + if ((mo->eflags & MFE_VERTICALFLIP) || mo->z != topheight) continue; } else if ((rover->master->frontsector->flags & SF_FLIPSPECIAL_CEILING) && !(rover->master->frontsector->flags & SF_FLIPSPECIAL_FLOOR)) { - if (!(player->mo->eflags & MFE_VERTICALFLIP) - || player->mo->z + player->mo->height != bottomheight) + if (!(mo->eflags & MFE_VERTICALFLIP) + || mo->z + mo->height != bottomheight) continue; } else if (rover->master->frontsector->flags & SF_FLIPSPECIAL_BOTH) { - if (!((player->mo->eflags & MFE_VERTICALFLIP && player->mo->z + player->mo->height == bottomheight) - || (!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z == topheight))) + if (!((mo->eflags & MFE_VERTICALFLIP && mo->z + mo->height == bottomheight) + || (!(mo->eflags & MFE_VERTICALFLIP) && mo->z == topheight))) continue; } } else { // Water and DEATH FOG!!! heh - if (player->mo->z > topheight || (player->mo->z + player->mo->height) < bottomheight) + if (mo->z > topheight || (mo->z + mo->height) < bottomheight) continue; } // This FOF has the special we're looking for, but are we allowed to touch it? - if (node->m_sector == player->mo->subsector->sector + if (node->m_sector == mo->subsector->sector || (rover->master->frontsector->flags & SF_TRIGGERSPECIAL_TOUCH)) return rover->master->frontsector; } @@ -3792,10 +3783,9 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers break; case 9: // Ring Drainer (Floor Touch) case 10: // Ring Drainer (No Floor Touch) - if (leveltime % (TICRATE/2) == 0 && player->mo->health > 1) + if (leveltime % (TICRATE/2) == 0 && player->kartstuff[k_rings] > 0) { - player->mo->health--; - player->health--; + player->kartstuff[k_rings]--; S_StartSound(player->mo, sfx_itemup); } break; @@ -5723,25 +5713,10 @@ void P_InitSpecials(void) CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false; - // Set curWeather - switch (mapheaderinfo[gamemap-1]->weather) - { - case PRECIP_SNOW: // snow - case PRECIP_RAIN: // rain - case PRECIP_STORM: // storm - case PRECIP_STORM_NORAIN: // storm w/o rain - case PRECIP_STORM_NOSTRIKES: // storm w/o lightning - curWeather = mapheaderinfo[gamemap-1]->weather; - break; - default: // blank/none - curWeather = PRECIP_NONE; - break; - } + // Set weather + curWeather = globalweather = mapheaderinfo[gamemap-1]->weather; - // Set globalweather - globalweather = mapheaderinfo[gamemap-1]->weather; - - P_InitTagLists(); // Create xref tables for tags + P_InitTagLists(); // Create xref tables for tags } /** After the map has loaded, scans for specials that spawn 3Dfloors and diff --git a/src/p_spec.h b/src/p_spec.h index 2763a34ad..a49946c2a 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -37,7 +37,7 @@ void P_SpawnSpecials(INT32 fromnetsave); // every tic void P_UpdateSpecials(void); -sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 number); +sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number, boolean touchground); void P_PlayerInSpecialSector(player_t *player); void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *roversector); @@ -61,7 +61,7 @@ void P_CrossSpecialLine(line_t *ld, INT32 side, mobj_t *thing); void P_SetupSignExit(player_t *player); boolean P_IsFlagAtBase(mobjtype_t flag); -void P_SwitchWeather(INT32 weathernum); +void P_SwitchWeather(UINT8 newWeather); boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller); void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller); diff --git a/src/p_tick.c b/src/p_tick.c index 0a340d80c..31fb95937 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -59,8 +59,6 @@ void Command_Numthinkers_f(void) CONS_Printf(M_GetText("numthinkers <#>: Count number of thinkers\n")); CONS_Printf( "\t1: P_MobjThinker\n" - /*"\t2: P_RainThinker\n" - "\t3: P_SnowThinker\n"*/ "\t2: P_NullPrecipThinker\n" "\t3: T_Friction\n" "\t4: T_Pusher\n" @@ -76,14 +74,6 @@ void Command_Numthinkers_f(void) action = (actionf_p1)P_MobjThinker; CONS_Printf(M_GetText("Number of %s: "), "P_MobjThinker"); break; - /*case 2: - action = (actionf_p1)P_RainThinker; - CONS_Printf(M_GetText("Number of %s: "), "P_RainThinker"); - break; - case 3: - action = (actionf_p1)P_SnowThinker; - CONS_Printf(M_GetText("Number of %s: "), "P_SnowThinker"); - break;*/ case 2: action = (actionf_p1)P_NullPrecipThinker; CONS_Printf(M_GetText("Number of %s: "), "P_NullPrecipThinker"); @@ -618,33 +608,10 @@ void P_Ticker(boolean run) } if (demo.playback) { - -#ifdef DEMO_COMPAT_100 - if (demo.version == 0x0001) - { - G_ReadDemoTiccmd(&players[consoleplayer].cmd, 0); - } - else - { -#endif - G_ReadDemoExtraData(); - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - { - //@TODO all this throwdir stuff shouldn't be here! But it's added to maintain 1.0.4 compat for now... - // Remove for 1.1! - if (players[i].cmd.buttons & BT_FORWARD) - players[i].kartstuff[k_throwdir] = 1; - else if (players[i].cmd.buttons & BT_BACKWARD) - players[i].kartstuff[k_throwdir] = -1; - else - players[i].kartstuff[k_throwdir] = 0; - - G_ReadDemoTiccmd(&players[i].cmd, i); - } -#ifdef DEMO_COMPAT_100 - } -#endif + G_ReadDemoExtraData(); + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + G_ReadDemoTiccmd(&players[i].cmd, i); } for (i = 0; i < MAXPLAYERS; i++) @@ -665,6 +632,8 @@ void P_Ticker(boolean run) if (run) { P_RunThinkers(); + if (G_BattleGametype() && battleovertime.enabled) + P_RunBattleOvertime(); // Run any "after all the other thinkers" stuff for (i = 0; i < MAXPLAYERS; i++) @@ -680,8 +649,6 @@ void P_Ticker(boolean run) //P_RunShields(); P_RunOverlays(); - P_RunShadows(); - P_UpdateSpecials(); P_RespawnSpecials(); @@ -760,11 +727,6 @@ void P_Ticker(boolean run) } else if (demo.playback) // Use Ghost data for consistency checks. { -#ifdef DEMO_COMPAT_100 - if (demo.version == 0x0001) - G_ConsGhostTic(0); - else -#endif G_ConsAllGhostTics(); } @@ -827,6 +789,8 @@ void P_PreTicker(INT32 frames) } P_RunThinkers(); + if (G_BattleGametype() && battleovertime.enabled) + P_RunBattleOvertime(); // Run any "after all the other thinkers" stuff for (i = 0; i < MAXPLAYERS; i++) diff --git a/src/p_user.c b/src/p_user.c index d44ddd914..bfd953f3e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1645,12 +1645,16 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost->frame |= tr_trans50<fuse = ghost->info->damage; ghost->skin = mobj->skin; + ghost->standingslope = mobj->standingslope; +#ifdef HWRENDER + ghost->modeltilt = mobj->modeltilt; +#endif if (mobj->flags2 & MF2_OBJECTFLIP) ghost->flags |= MF2_OBJECTFLIP; if (!(mobj->flags & MF_DONTENCOREMAP)) - mobj->flags &= ~MF_DONTENCOREMAP; + ghost->flags &= ~MF_DONTENCOREMAP; return ghost; } @@ -4186,27 +4190,33 @@ static void P_3dMovement(player_t *player) // If "no" to 2, normalize to topspeed, so we can't suddenly run faster than it of our own accord. // If "no" to 1, we're not reaching any limits yet, so ignore this entirely! // -Shadow Hog - newMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0); - if (newMagnitude > K_GetKartSpeed(player, true)) //topspeed) + // Only do this forced cap of speed when in midair, the kart acceleration code takes into account friction, and + // doesn't let you accelerate past top speed, so this is unnecessary on the ground, but in the air is needed to + // allow for being able to change direction on spring jumps without being accelerated into the void - Sryder + if (!P_IsObjectOnGround(player->mo)) { - fixed_t tempmomx, tempmomy; - if (oldMagnitude > K_GetKartSpeed(player, true)) + newMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0); + if (newMagnitude > K_GetKartSpeed(player, true)) //topspeed) { - if (newMagnitude > oldMagnitude) + fixed_t tempmomx, tempmomy; + if (oldMagnitude > K_GetKartSpeed(player, true)) { - tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), oldMagnitude); - tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), oldMagnitude); + if (newMagnitude > oldMagnitude) + { + tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), oldMagnitude); + tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), oldMagnitude); + player->mo->momx = tempmomx + player->cmomx; + player->mo->momy = tempmomy + player->cmomy; + } + // else do nothing + } + else + { + tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), K_GetKartSpeed(player, true)); //topspeed) + tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), K_GetKartSpeed(player, true)); //topspeed) player->mo->momx = tempmomx + player->cmomx; player->mo->momy = tempmomy + player->cmomy; } - // else do nothing - } - else - { - tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), K_GetKartSpeed(player, true)); //topspeed) - tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), K_GetKartSpeed(player, true)); //topspeed) - player->mo->momx = tempmomx + player->cmomx; - player->mo->momy = tempmomy + player->cmomy; } } } @@ -8130,8 +8140,6 @@ void P_PlayerThink(player_t *player) cmd = &player->cmd; - //@TODO This fixes a one-tic latency on direction handling, AND makes behavior consistent while paused, but is not BC with 1.0.4. Do this for 1.1! -#if 0 // SRB2kart // Save the dir the player is holding // to allow items to be thrown forward or backward. @@ -8141,7 +8149,6 @@ void P_PlayerThink(player_t *player) player->kartstuff[k_throwdir] = -1; else player->kartstuff[k_throwdir] = 0; -#endif // Add some extra randomization. if (cmd->forwardmove) diff --git a/src/r_bsp.c b/src/r_bsp.c index 296cbbe87..a748af51c 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -57,6 +57,16 @@ static boolean R_NoEncore(sector_t *sector, boolean ceiling) return ((boolean)(sector->flags & SF_FLIPSPECIAL_FLOOR)); } +static void R_PlaneLightOverride(sector_t *sector, boolean ceiling, INT32 *lightlevel) +{ + if (GETSECSPECIAL(sector->special, 4) == 6) // Fullbright sneaker panels + { + if ((ceiling && (sector->flags & SF_FLIPSPECIAL_CEILING)) + || (!ceiling && (sector->flags & SF_FLIPSPECIAL_FLOOR))) + *lightlevel = 255; + } +} + // // R_ClearDrawSegs // @@ -895,6 +905,9 @@ static void R_Subsector(size_t num) sub->sector->extra_colormap = frontsector->extra_colormap; + R_PlaneLightOverride(frontsector, false, &floorlightlevel); + R_PlaneLightOverride(frontsector, true, &ceilinglightlevel); + if ((( #ifdef ESLOPE frontsector->f_slope ? P_GetZAt(frontsector->f_slope, viewx, viewy) : @@ -923,8 +936,8 @@ static void R_Subsector(size_t num) || (frontsector->heightsec != -1 && sectors[frontsector->heightsec].floorpic == skyflatnum))) { - ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic, - ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle, + ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic, ceilinglightlevel, + frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle, ceilingcolormap, NULL #ifdef POLYOBJECTS_PLANES , NULL diff --git a/src/r_data.c b/src/r_data.c index 7fb11855f..922e907e2 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -267,7 +267,7 @@ static UINT8 *R_GenerateTexture(size_t texnum) texturememory += blocksize; block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]); - memset(block, 0xF7, blocksize+1); // Transparency hack + memset(block, 0xFF, blocksize+1); // TRANSPARENTPIXEL // columns lookup table colofs = (UINT32 *)(void *)block; @@ -1177,7 +1177,6 @@ void R_ClearColormaps(void) // static double deltas[256][3], map[256][3]; -static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); static int RoundUp(double number); #ifdef HASINVERT @@ -1403,7 +1402,7 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3) // Thanks to quake2 source! // utils3/qdata/images.c -static UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) +UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) { int dr, dg, db; int distortion, bestdistortion = 256 * 256 * 4, bestcolor = 0, i; @@ -1631,7 +1630,7 @@ void R_PrecacheLevel(void) // Sky texture is always present. // Note that F_SKY1 is the name used to indicate a sky floor/ceiling as a flat, - // while the sky texture is stored like a wall texture, with a skynum dependent name. + // while the sky texture is stored like a wall texture, with a texture name set by the map. texturepresent[skytexture] = 1; texturememory = 0; diff --git a/src/r_data.h b/src/r_data.h index 640b30309..cd61bbaac 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -93,6 +93,7 @@ void R_ReInitColormaps(UINT16 num, lumpnum_t newencoremap); void R_ClearColormaps(void); INT32 R_ColormapNumForName(char *name); INT32 R_CreateColormap(char *p1, char *p2, char *p3); +UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); #ifdef HASINVERT void R_MakeInvertmap(void); #endif diff --git a/src/r_main.c b/src/r_main.c index 0d14bed73..a4fa9d463 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -168,8 +168,7 @@ consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, consvar_t cv_flipcam3 = {"flipcam3", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam3_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_flipcam4 = {"flipcam4", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam4_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_shadow = {"shadow", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_soniccd = {"soniccd", "Off", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -1505,7 +1504,6 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_chasecam3); CV_RegisterVar(&cv_chasecam4); CV_RegisterVar(&cv_shadow); - CV_RegisterVar(&cv_shadowoffs); CV_RegisterVar(&cv_skybox); CV_RegisterVar(&cv_cam_dist); diff --git a/src/r_main.h b/src/r_main.h index 38a589682..879d4c6eb 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -76,7 +76,7 @@ extern consvar_t cv_showhud, cv_translucenthud; extern consvar_t cv_homremoval; extern consvar_t cv_chasecam, cv_chasecam2, cv_chasecam3, cv_chasecam4; extern consvar_t cv_flipcam, cv_flipcam2, cv_flipcam3, cv_flipcam4; -extern consvar_t cv_shadow, cv_shadowoffs; +extern consvar_t cv_shadow; extern consvar_t cv_translucency; extern consvar_t /*cv_precipdensity,*/ cv_drawdist, /*cv_drawdist_nights,*/ cv_drawdist_precip; extern consvar_t cv_fov; diff --git a/src/r_plane.c b/src/r_plane.c index db5bfbda2..ec105bf75 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -734,7 +734,7 @@ void R_DrawPlanes(void) dc_x = x; dc_source = R_GetColumn(texturetranslation[skytexture], - angle); + -angle); // Negative because skies were being drawn horizontally flipped wallcolfunc(); } } diff --git a/src/r_sky.c b/src/r_sky.c index fe1630e90..1fe0fe0e6 100644 --- a/src/r_sky.c +++ b/src/r_sky.c @@ -47,8 +47,8 @@ fixed_t skyscale; /** \brief used for keeping track of the current sky */ -INT32 levelskynum; -INT32 globallevelskynum; +char levelskytexture[9]; +char globallevelskytexture[9]; /** \brief The R_SetupSkyDraw function diff --git a/src/r_sky.h b/src/r_sky.h index 86b615595..a41b24463 100644 --- a/src/r_sky.h +++ b/src/r_sky.h @@ -30,8 +30,8 @@ extern INT32 skytexture, skytexturemid; extern fixed_t skyscale; extern INT32 skyflatnum; -extern INT32 levelskynum; -extern INT32 globallevelskynum; +extern char levelskytexture[9]; +extern char globallevelskytexture[9]; // call after skytexture is set to adapt for old/new skies void R_SetupSkyDraw(void); diff --git a/src/r_things.c b/src/r_things.c index 1afbb125c..9ea8bb77b 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -802,9 +802,7 @@ static void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight) static void R_DrawVisSprite(vissprite_t *vis) { column_t *column; -#ifdef RANGECHECK INT32 texturecolumn; -#endif fixed_t frac; patch_t *patch = W_CacheLumpNum(vis->patch, PU_CACHE); fixed_t this_scale = vis->mobj->scale; @@ -920,6 +918,7 @@ static void R_DrawVisSprite(vissprite_t *vis) if (!(vis->scalestep)) { sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); + sprtopscreen += vis->shear.tan * vis->shear.offset; dc_iscale = FixedDiv(FRACUNIT, vis->scale); } @@ -942,31 +941,50 @@ static void R_DrawVisSprite(vissprite_t *vis) vis->x2--; #endif - for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) + // Split drawing loops for paper and non-paper to reduce conditional checks per sprite + if (vis->scalestep) { - if (vis->scalestep) // currently papersprites only + // Papersprite drawing loop + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, spryscale += vis->scalestep) { -#ifndef RANGECHECK - if ((frac>>FRACBITS) < 0 || (frac>>FRACBITS) >= SHORT(patch->width)) // if this doesn't work i'm removing papersprites - break; -#endif + angle_t angle = ((vis->centerangle + xtoviewangle[dc_x]) >> ANGLETOFINESHIFT) & 0xFFF; + texturecolumn = (vis->paperoffset - FixedMul(FINETANGENT(angle), vis->paperdistance)) / this_scale; + + if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + continue; + + if (vis->xiscale < 0) // Flipped sprite + texturecolumn = SHORT(patch->width) - 1 - texturecolumn; + sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale)); dc_iscale = (0xffffffffu / (unsigned)spryscale); - spryscale += vis->scalestep; - } -#ifdef RANGECHECK - texturecolumn = frac>>FRACBITS; - if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) - I_Error("R_DrawSpriteRange: bad texturecolumn"); - column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn])); + column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn])); + + if (vis->cut & SC_VFLIP) + R_DrawFlippedMaskedColumn(column, patch->height); + else + R_DrawMaskedColumn(column); + } + } + else + { + // Non-paper drawing loop + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan) + { +#ifdef RANGECHECK + texturecolumn = frac>>FRACBITS; + if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + I_Error("R_DrawSpriteRange: bad texturecolumn at %d from end", vis->x2 - dc_x); + column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn])); #else - column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS])); + column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS])); #endif - if (vis->cut & SC_VFLIP) - R_DrawFlippedMaskedColumn(column, patch->height); - else - R_DrawMaskedColumn(column); + if (vis->cut & SC_VFLIP) + R_DrawFlippedMaskedColumn(column, patch->height); + else + R_DrawMaskedColumn(column); + } } colfunc = basecolfunc; @@ -1138,6 +1156,278 @@ static void R_SplitSprite(vissprite_t *sprite) } } +// +// R_GetShadowZ(thing, shadowslope) +// Get the first visible floor below the object for shadows +// shadowslope is filled with the floor's slope, if provided +// +fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) +{ + fixed_t z, floorz = INT32_MIN; + pslope_t *slope, *floorslope = NULL; + msecnode_t *node; + sector_t *sector; + ffloor_t *rover; + + for (node = thing->touching_sectorlist; node; node = node->m_sectorlist_next) + { + sector = node->m_sector; + + slope = (sector->heightsec != -1) ? NULL : sector->f_slope; + z = slope ? P_GetZAt(slope, thing->x, thing->y) : ( + (sector->heightsec != -1) ? sectors[sector->heightsec].floorheight : sector->floorheight + ); + + if (z < thing->z+thing->height/2 && z > floorz) + { + floorz = z; + floorslope = slope; + } + + if (sector->ffloors) + for (rover = sector->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES) || (rover->alpha < 90 && !(rover->flags & FF_SWIMMABLE))) + continue; + + z = *rover->t_slope ? P_GetZAt(*rover->t_slope, thing->x, thing->y) : *rover->topheight; + if (z < thing->z+thing->height/2 && z > floorz) + { + floorz = z; + floorslope = *rover->t_slope; + } + } + } + + if (thing->floorz > floorz + (!floorslope ? 0 : FixedMul(abs(floorslope->zdelta), thing->radius*3/2))) + { + floorz = thing->floorz; + floorslope = NULL; + } + +#if 0 // Unfortunately, this drops CEZ2 down to sub-17 FPS on my i7. +//#ifdef POLYOBJECTS + // Check polyobjects and see if floorz needs to be altered, for rings only because they don't update floorz + if (thing->type == MT_RING) + { + INT32 xl, xh, yl, yh, bx, by; + + xl = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + validcount++; + + for (by = yl; by <= yh; by++) + for (bx = xl; bx <= xh; bx++) + { + INT32 offset; + polymaplink_t *plink; // haleyjd 02/22/06 + + if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight) + continue; + + offset = by*bmapwidth + bx; + + // haleyjd 02/22/06: consider polyobject lines + plink = polyblocklinks[offset]; + + while (plink) + { + polyobj_t *po = plink->po; + + if (po->validcount != validcount) // if polyobj hasn't been checked + { + po->validcount = validcount; + + if (!P_MobjInsidePolyobj(po, thing) || !(po->flags & POF_RENDERPLANES)) + { + plink = (polymaplink_t *)(plink->link.next); + continue; + } + + // We're inside it! Yess... + z = po->lines[0]->backsector->ceilingheight; + + if (z < thing->z+thing->height/2 && z > floorz) + { + floorz = z; + floorslope = NULL; + } + } + plink = (polymaplink_t *)(plink->link.next); + } + } + } +#endif + + if (shadowslope != NULL) + *shadowslope = floorslope; + + return floorz; +} + +static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t tx, fixed_t tz) +{ + vissprite_t *shadow; + patch_t *patch; + fixed_t xscale, yscale, shadowxscale, shadowyscale, shadowskew, x1, x2; + INT32 light = 0; + fixed_t scalemul; UINT8 trans; + fixed_t floordiff; + fixed_t floorz; + pslope_t *floorslope; + + floorz = R_GetShadowZ(thing, &floorslope); + + if (abs(floorz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes + + floordiff = abs(thing->z - floorz); + + trans = floordiff / (100*FRACUNIT) + 3; + if (trans >= 9) return; + + scalemul = FixedMul(FRACUNIT - floordiff/640, scale); + + if (thing->whiteshadow) + patch = W_CachePatchName("LSHADOW", PU_CACHE); + else + patch = W_CachePatchName("DSHADOW", PU_CACHE); + + xscale = FixedDiv(projection, tz); + yscale = FixedDiv(projectiony, tz); + shadowxscale = FixedMul(thing->radius*2, scalemul); + shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(floorz - viewz), tz)); + shadowyscale = min(shadowyscale, shadowxscale) / patch->height; + shadowxscale /= patch->width; + shadowskew = 0; + + if (floorslope) + { + // haha let's try some dumb stuff + fixed_t xslope, zslope; + angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - floorslope->xydirection) >> ANGLETOFINESHIFT; + + xslope = FixedMul(FINESINE(sloperelang), floorslope->zdelta); + zslope = FixedMul(FINECOSINE(sloperelang), floorslope->zdelta); + + //CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope); + + if (viewz < floorz) + shadowyscale += FixedMul(FixedMul(thing->radius*2 / patch->height, scalemul), zslope); + else + shadowyscale -= FixedMul(FixedMul(thing->radius*2 / patch->height, scalemul), zslope); + + shadowyscale = abs(shadowyscale); + + shadowskew = xslope; + } + + tx -= patch->width * shadowxscale/2; + x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; + if (x1 >= viewwidth) return; + + tx += patch->width * shadowxscale; + x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--; + if (x2 < 0 || x2 <= x1) return; + + if (shadowyscale < FRACUNIT/patch->height) return; // fix some crashes? + + shadow = R_NewVisSprite(); + + if (thing->whiteshadow) + shadow->patch = W_CheckNumForName("LSHADOW"); + else + shadow->patch = W_CheckNumForName("DSHADOW"); + + shadow->heightsec = vis->heightsec; + + shadow->thingheight = FRACUNIT; + shadow->pz = floorz; + shadow->pzt = shadow->pz + shadow->thingheight; + + shadow->mobjflags = 0; + shadow->sortscale = vis->sortscale; + shadow->dispoffset = vis->dispoffset - 5; + shadow->gx = thing->x; + shadow->gy = thing->y; + shadow->gzt = shadow->pz + patch->height * shadowyscale / 2; + shadow->gz = shadow->gzt - patch->height * shadowyscale; + shadow->texturemid = FixedMul(thing->scale, FixedDiv(shadow->gzt - viewz, shadowyscale)); + if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES) + shadow->texturemid = FixedMul(shadow->texturemid, ((skin_t *)thing->skin)->highresscale); + shadow->scalestep = 0; + shadow->shear.tan = shadowskew; // repurposed variable + + shadow->mobj = thing; // Easy access! Tails 06-07-2002 + + shadow->x1 = x1 < 0 ? 0 : x1; + shadow->x2 = x2 >= viewwidth ? viewwidth-1 : x2; + + // PORTAL SEMI-CLIPPING + if (portalrender) + { + if (shadow->x1 < portalclipstart) + shadow->x1 = portalclipstart; + if (shadow->x2 >= portalclipend) + shadow->x2 = portalclipend-1; + } + + shadow->xscale = FixedMul(xscale, shadowxscale); //SoM: 4/17/2000 + shadow->scale = FixedMul(yscale, shadowyscale); + shadow->sector = vis->sector; + shadow->szt = (INT16)((centeryfrac - FixedMul(shadow->gzt - viewz, yscale))>>FRACBITS); + shadow->sz = (INT16)((centeryfrac - FixedMul(shadow->gz - viewz, yscale))>>FRACBITS); + shadow->cut = SC_ISSCALED|SC_SHADOW; //check this + + shadow->startfrac = 0; + //shadow->xiscale = 0x7ffffff0 / (shadow->xscale/2); + shadow->xiscale = (patch->width<x1 > x1) + shadow->startfrac += shadow->xiscale*(shadow->x1-x1); + + // reusing x1 variable + x1 += (x2-x1)/2; + shadow->shear.offset = shadow->x1-x1; + + if (thing->subsector->sector->numlights) + { + INT32 lightnum; +#ifdef ESLOPE // R_GetPlaneLight won't work on sloped lights! + light = thing->subsector->sector->numlights - 1; + + for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) { + fixed_t h = thing->subsector->sector->lightlist[lightnum].slope ? P_GetZAt(thing->subsector->sector->lightlist[lightnum].slope, thing->x, thing->y) + : thing->subsector->sector->lightlist[lightnum].height; + if (h <= shadow->gzt) { + light = lightnum - 1; + break; + } + } +#else + light = R_GetPlaneLight(thing->subsector->sector, shadow->gzt, false); +#endif + } + + if (thing->subsector->sector->numlights) + shadow->extra_colormap = thing->subsector->sector->lightlist[light].extra_colormap; + else + shadow->extra_colormap = thing->subsector->sector->extra_colormap; + + shadow->transmap = transtables + (trans<whiteshadow) + shadow->colormap = scalelight[LIGHTLEVELS - 1][0]; // full bright! + else + shadow->colormap = scalelight[0][0]; // full dark! + + objectsdrawn++; +} + // // R_ProjectSprite // Generates a vissprite for a thing @@ -1168,7 +1458,11 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t iscale; fixed_t scalestep; // toast '16 fixed_t offset, offset2; - boolean papersprite = (thing->frame & FF_PAPERSPRITE); + + fixed_t basetx; // drop shadows + + boolean papersprite = !!(thing->frame & FF_PAPERSPRITE); + fixed_t paperoffset = 0, paperdistance = 0; angle_t centerangle = 0; //SoM: 3/17/2000 fixed_t gz, gzt; @@ -1176,8 +1470,6 @@ static void R_ProjectSprite(mobj_t *thing) INT32 light = 0; fixed_t this_scale = thing->scale; - fixed_t ang_scale = FRACUNIT; - // transform the origin point tr_x = thing->x - viewx; tr_y = thing->y - viewy; @@ -1188,15 +1480,15 @@ static void R_ProjectSprite(mobj_t *thing) tz = gxt-gyt; // thing is behind view plane? - if (!(papersprite) && (tz < FixedMul(MINZ, this_scale))) // papersprite clipping is handled later + if (!papersprite && (tz < FixedMul(MINZ, this_scale))) // papersprite clipping is handled later return; gxt = -FixedMul(tr_x, viewsin); gyt = FixedMul(tr_y, viewcos); - tx = -(gyt + gxt); + basetx = tx = -(gyt + gxt); // too far off the side? - if (abs(tx) > tz<<2) + if (!papersprite && abs(tx) > tz<<2) // papersprite clipping is handled later return; // aspect ratio stuff @@ -1249,8 +1541,6 @@ static void R_ProjectSprite(mobj_t *thing) ang = R_PointToAngle (thing->x, thing->y) - thing->player->frameangle; else ang = R_PointToAngle (thing->x, thing->y) - thing->angle; - if (papersprite) - ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT)); } if (sprframe->rotate == SRF_SINGLE) @@ -1288,27 +1578,12 @@ static void R_ProjectSprite(mobj_t *thing) else offset = -spritecachedinfo[lump].offset; offset = FixedMul(offset, this_scale); - tx += FixedMul(offset, ang_scale); - x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS; - - // off the right side? - if (x1 > viewwidth) - return; - offset2 = FixedMul(spritecachedinfo[lump].width, this_scale); - tx += FixedMul(offset2, ang_scale); - x2 = ((centerxfrac + FixedMul (tx,xscale)) >> FRACBITS) - 1; - - // off the left side - if (x2 < 0) - return; if (papersprite) { - fixed_t yscale2, cosmul, sinmul, tz2; - - if (x2 <= x1) - return; + fixed_t xscale2, yscale2, cosmul, sinmul, tx2, tz2; + INT32 range; if (ang >= ANGLE_180) { @@ -1325,7 +1600,23 @@ static void R_ProjectSprite(mobj_t *thing) gyt = -FixedMul(tr_y, viewsin); tz = gxt-gyt; yscale = FixedDiv(projectiony, tz); - if (yscale < 64) return; // Fix some funky visuals + //if (yscale < 64) return; // Fix some funky visuals + + gxt = -FixedMul(tr_x, viewsin); + gyt = FixedMul(tr_y, viewcos); + tx = -(gyt + gxt); + xscale = FixedDiv(projection, tz); + x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; + + // Get paperoffset (offset) and paperoffset (distance) + paperoffset = -FixedMul(tr_x, cosmul) - FixedMul(tr_y, sinmul); + paperdistance = -FixedMul(tr_x, sinmul) + FixedMul(tr_y, cosmul); + if (paperdistance < 0) + { + paperoffset = -paperoffset; + paperdistance = -paperdistance; + } + centerangle = viewangle - thing->angle; tr_x += FixedMul(offset2, cosmul); tr_y += FixedMul(offset2, sinmul); @@ -1333,13 +1624,52 @@ static void R_ProjectSprite(mobj_t *thing) gyt = -FixedMul(tr_y, viewsin); tz2 = gxt-gyt; yscale2 = FixedDiv(projectiony, tz2); - if (yscale2 < 64) return; // ditto + //if (yscale2 < 64) return; // ditto + + gxt = -FixedMul(tr_x, viewsin); + gyt = FixedMul(tr_y, viewcos); + tx2 = -(gyt + gxt); + xscale2 = FixedDiv(projection, tz2); + x2 = ((centerxfrac + FixedMul(tx2,xscale2))>>FRACBITS); if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier return; - scalestep = (yscale2 - yscale)/(x2 - x1); - scalestep = scalestep ? scalestep : 1; + // Needs partially clipped + if (tz < FixedMul(MINZ, this_scale)) + { + fixed_t div = FixedDiv(tz2-tz, FixedMul(MINZ, this_scale)-tz); + tx += FixedDiv(tx2-tx, div); + tz = FixedMul(MINZ, this_scale); + yscale = FixedDiv(projectiony, tz); + xscale = FixedDiv(projection, tz); + x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; + } + else if (tz2 < FixedMul(MINZ, this_scale)) + { + fixed_t div = FixedDiv(tz-tz2, FixedMul(MINZ, this_scale)-tz2); + tx2 += FixedDiv(tx-tx2, div); + tz2 = FixedMul(MINZ, this_scale); + yscale2 = FixedDiv(projectiony, tz2); + xscale2 = FixedDiv(projection, tz2); + x2 = (centerxfrac + FixedMul(tx2,xscale2))>>FRACBITS; + } + + // off the right side? + if (x1 > viewwidth) + return; + + // off the left side + if (x2 < 0) + return; + + if ((range = x2 - x1) <= 0) + return; + + range++; // fencepost problem + + scalestep = ((yscale2 - yscale)/range) ?: 1; + xscale = FixedDiv(range<>FRACBITS; - xscale = FixedMul(xscale, ang_scale); + // off the right side? + if (x1 > viewwidth) + return; + + tx += offset2; + x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--; + + // off the left side + if (x2 < 0) + return; + } // PORTAL SPRITE CLIPPING if (portalrender) @@ -1445,6 +1786,11 @@ static void R_ProjectSprite(mobj_t *thing) vis->pzt = vis->pz + vis->thingheight; vis->texturemid = vis->gzt - viewz; vis->scalestep = scalestep; + vis->paperoffset = paperoffset; + vis->paperdistance = paperdistance; + vis->centerangle = centerangle; + vis->shear.tan = 0; + vis->shear.offset = 0; vis->mobj = thing; // Easy access! Tails 06-07-2002 @@ -1537,6 +1883,9 @@ static void R_ProjectSprite(mobj_t *thing) if (thing->subsector->sector->numlights) R_SplitSprite(vis); + if (thing->shadowscale && cv_shadow.value) + R_ProjectDropShadow(thing, vis, thing->shadowscale, basetx, tz); + // Debug ++objectsdrawn; } @@ -1639,14 +1988,10 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) // okay, we can't return now except for vertical clipping... this is a hack, but weather isn't networked, so it should be ok if (!(thing->precipflags & PCF_THUNK)) { - if (thing->precipflags & PCF_RAIN) - P_RainThinker(thing); - else - P_SnowThinker(thing); + P_PrecipThinker(thing); thing->precipflags |= PCF_THUNK; } - //SoM: 3/17/2000: Disregard sprites that are out of view.. gzt = thing->z + spritecachedinfo[lump].topoffset; gz = gzt - spritecachedinfo[lump].height; @@ -1670,6 +2015,9 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis->pzt = vis->pz + vis->thingheight; vis->texturemid = vis->gzt - viewz; vis->scalestep = 0; + vis->paperdistance = 0; + vis->shear.tan = 0; + vis->shear.offset = 0; vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; diff --git a/src/r_things.h b/src/r_things.h index 4837b4aee..a50e0803d 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -50,6 +50,8 @@ void R_SortVisSprites(void); // (only sprites from namelist are added or replaced) void R_AddSpriteDefs(UINT16 wadnum); +fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope); + #ifdef DELFILE void R_DelSpriteDefs(UINT16 wadnum); #endif @@ -114,7 +116,8 @@ typedef enum SC_FULLBRIGHT = 1<<4, SC_SEMIBRIGHT = 1<<5, SC_VFLIP = 1<<6, - SC_ISSCALED = 1>>7, + SC_ISSCALED = 1<<7, + SC_SHADOW = 1<<8, // masks SC_CUTMASK = SC_TOP|SC_BOTTOM, SC_FLAGMASK = ~SC_CUTMASK @@ -139,8 +142,16 @@ typedef struct vissprite_s fixed_t startfrac; // horizontal position of x1 fixed_t scale, sortscale; // sortscale only differs from scale for flat sprites fixed_t scalestep; // only for flat sprites, 0 otherwise + fixed_t paperoffset, paperdistance; // for paper sprites, offset/dist relative to the angle fixed_t xiscale; // negative if flipped + angle_t centerangle; // for paper sprites + + struct { + fixed_t tan; // The amount to shear the sprite vertically per row + INT32 offset; // The center of the shearing location offset from x1 + } shear; + fixed_t texturemid; lumpnum_t patch; diff --git a/src/screen.c b/src/screen.c index 4cb8bac5d..96a78ae8e 100644 --- a/src/screen.c +++ b/src/screen.c @@ -427,13 +427,13 @@ void SCR_DisplayTicRate(void) ticcntcolor|V_NOSCALESTART, va("%02d/%02u", totaltics, TICRATE));*/ // draw "FPS" - V_DrawFixedPatch(306< servermaxping)) // only show 2 (warning) if our ping is at a bad level { INT32 dispy = cv_ticrate.value ? 160 : 181; - HU_drawPing(307, dispy, ping, V_SNAPTORIGHT | V_SNAPTOBOTTOM); + HU_drawPing(307, dispy, ping, V_SNAPTORIGHT | V_SNAPTOBOTTOM | V_HUDTRANS); } } diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index d24bd5ade..f75a42117 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -165,6 +165,7 @@ static char returnWadPath[256]; #include "../d_net.h" #include "../g_game.h" #include "../filesrch.h" +#include "../k_pwrlv.h" #include "endtxt.h" #include "sdlmain.h" @@ -3056,6 +3057,11 @@ void I_Quit(void) #ifndef NONET D_SaveBan(); // save the ban list #endif + + // Make sure you lose points for ALT-F4 + if (Playing()) + K_PlayerForfeit(consoleplayer, true); + G_SaveGameData(false); // Tails 12-08-2002 //added:16-02-98: when recording a demo, should exit using 'q' key, // but sometimes we forget and use 'F10'.. so save here too. @@ -3177,11 +3183,14 @@ void I_Error(const char *error, ...) #endif G_SaveGameData(false); // Tails 12-08-2002 + /* Prevent segmentation fault if testers go to Record Attack... */ +#ifndef TESTERS // Shutdown. Here might be other errors. if (demo.recording) G_CheckDemoStatus(); if (metalrecording) G_StopMetalRecording(); +#endif D_QuitNetGame(); I_ShutdownMusic(); diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 42e0a917f..c900417ab 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1377,11 +1377,17 @@ void I_FinishUpdate(void) if (I_SkipFrame()) return; - if (cv_ticrate.value) - SCR_DisplayTicRate(); + if (st_overlay) + { + if (cv_ticrate.value) + SCR_DisplayTicRate(); - if (cv_showping.value && netgame && consoleplayer != serverplayer) - SCR_DisplayLocalPing(); + if (cv_showping.value && netgame && + ( consoleplayer != serverplayer || ! server_lagless )) + { + SCR_DisplayLocalPing(); + } + } if (rendermode == render_soft && screens[0]) { diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c index 62256f3a5..14d183748 100644 --- a/src/sdl12/i_system.c +++ b/src/sdl12/i_system.c @@ -233,6 +233,7 @@ static char returnWadPath[256]; #include "../d_net.h" #include "../g_game.h" #include "../filesrch.h" +#include "../k_pwrlv.h" #include "endtxt.h" #include "sdlmain.h" @@ -2977,6 +2978,11 @@ void I_Quit(void) #ifndef NONET D_SaveBan(); // save the ban list #endif + + // Make sure you lose points for ALT-F4 + if (Playing()) + K_PlayerForfeit(consoleplayer, true); + G_SaveGameData(); // Tails 12-08-2002 //added:16-02-98: when recording a demo, should exit using 'q' key, // but sometimes we forget and use 'F10'.. so save here too. diff --git a/src/sounds.c b/src/sounds.c index 61fddb76f..ef150d7a5 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -815,6 +815,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"chain", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Mementos Reaper {"mkuma", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Trigger Happy Havoc Monokuma {"toada", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Arid Sands Toad scream + {"bhurry", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // v1.0.2 Battle overtime {"bsnipe", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Banana sniping {"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree: {"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification diff --git a/src/sounds.h b/src/sounds.h index bb46ea9d8..8b9ca609e 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -890,6 +890,7 @@ typedef enum sfx_chain, sfx_mkuma, sfx_toada, + sfx_bhurry, sfx_bsnipe, sfx_itfree, sfx_dbgsal, diff --git a/src/st_stuff.c b/src/st_stuff.c index c55724359..5ab079182 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2134,12 +2134,7 @@ void ST_Drawer(void) ST_MayonakaStatic(); } - // Draw a white fade on level opening - if (timeinmap < 15) - { - if (timeinmap <= 5) - V_DrawFill(0,0,BASEVIDWIDTH,BASEVIDHEIGHT,0); // Pure white on first few frames, to hide SRB2's awful level load artifacts - else - V_DrawFadeScreen(0, 15-timeinmap); // Then gradually fade out from there - } + // Draw a fade on level opening + if (timeinmap < 16) + V_DrawCustomFadeScreen(((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), 32-(timeinmap*2)); // Then gradually fade out from there } diff --git a/src/v_video.c b/src/v_video.c index 1b1d03baa..297eae9c4 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1287,28 +1287,75 @@ void V_DrawVhsEffect(boolean rewind) void V_DrawFadeScreen(UINT16 color, UINT8 strength) { #ifdef HWRENDER - if (rendermode != render_soft && rendermode != render_none) - { - HWR_FadeScreenMenuBack(color, strength); - return; - } + if (rendermode != render_soft && rendermode != render_none) + { + HWR_FadeScreenMenuBack(color, strength); + return; + } #endif - { - const UINT8 *fadetable = + { + const UINT8 *fadetable = (color > 0xFFF0) // Grab a specific colormap palette? ? R_GetTranslationColormap(color | 0xFFFF0000, strength, GTC_CACHE) : ((color & 0xFF00) // Color is not palette index? ? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade. : ((UINT8 *)transtables + ((9-strength)< data.match.val[data.match.numplayers])) + INT16 increase = ((data.match.increase[i] == INT16_MIN) ? 0 : data.match.increase[i]); + UINT32 score = (powertype != -1 ? clientpowerlevels[i][powertype] : players[i].score); + + if (!(data.match.val[data.match.numplayers] == UINT32_MAX || (score - increase) > data.match.val[data.match.numplayers])) return; - data.match.val[data.match.numplayers] = (players[i].score - increase); + data.match.val[data.match.numplayers] = (score - increase); data.match.num[data.match.numplayers] = i; } @@ -205,7 +209,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) { INT32 i, j; boolean completed[MAXPLAYERS]; - INT32 numplayersingame = 0; + INT32 numplayersingame = 0, numgriefers = 0; // Initialize variables if (rankingsmode > 1) @@ -257,14 +261,17 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) { data.match.val[i] = UINT32_MAX; + if (nospectategrief[i] != -1) + numgriefers++; + if (!playeringame[i] || players[i].spectator) { - data.match.increase[i] = UINT8_MAX; + data.match.increase[i] = INT16_MIN; continue; } if (!rankingsmode) - data.match.increase[i] = UINT8_MAX; + data.match.increase[i] = INT16_MIN; numplayersingame++; } @@ -276,8 +283,6 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) for (j = 0; j < numplayersingame; j++) { - INT32 nump = ((G_RaceGametype() && nospectategrief > 0) ? nospectategrief : numplayersingame); - for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator || completed[i]) @@ -299,9 +304,10 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) else data.match.pos[data.match.numplayers] = data.match.numplayers+1; - if (!rankingsmode && !(players[i].pflags & PF_TIMEOVER) && (data.match.pos[data.match.numplayers] < nump)) + if ((!rankingsmode && powertype == -1) // Single player rankings (grand prix). Online rank is handled below. + && !(players[i].pflags & PF_TIMEOVER) && (data.match.pos[data.match.numplayers] < (numplayersingame + numgriefers))) { - data.match.increase[i] = nump - data.match.pos[data.match.numplayers]; + data.match.increase[i] = (numplayersingame + numgriefers) - data.match.pos[data.match.numplayers]; players[i].score += data.match.increase[i]; } @@ -429,9 +435,10 @@ void Y_IntermissionDrawer(void) INT32 y = 41, gutter = ((data.match.numplayers > NUMFORNEWCOLUMN) ? 0 : (BASEVIDWIDTH/2)); INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; const char *timeheader; + int y2; if (data.match.rankingsmode) - timeheader = "RANK"; + timeheader = "PWR.LV"; else timeheader = (intertype == int_race ? "TIME" : "SCORE"); @@ -486,28 +493,61 @@ void Y_IntermissionDrawer(void) STRBUFCPY(strtime, data.match.name[i]); + y2 = y; + + if (data.match.num[i] == 0 && server_lagless) + { + static int alagles_timer = 0; + patch_t *alagles; + + y2 = ( y - 4 ); + + V_DrawScaledPatch(x + 36, y2, 0, W_CachePatchName(va("BLAGLES%d", (intertic / 3) % 6), PU_CACHE)); + // every 70 tics + if (( leveltime % 70 ) == 0) + { + alagles_timer = 9; + } + if (alagles_timer > 0) + { + alagles = W_CachePatchName(va("ALAGLES%d", alagles_timer), PU_CACHE); + V_DrawScaledPatch(x + 36, y2, 0, alagles); + if (( leveltime % 2 ) == 0) + alagles_timer--; + } + else + { + alagles = W_CachePatchName("ALAGLES0", PU_CACHE); + V_DrawScaledPatch(x + 36, y2, 0, alagles); + } + + y2 += SHORT (alagles->height) + 1; + } + if (data.match.numplayers > NUMFORNEWCOLUMN) - V_DrawThinString(x+36, y-1, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + V_DrawThinString(x+36, y2-1, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); else - V_DrawString(x+36, y, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); + V_DrawString(x+36, y2, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); if (data.match.rankingsmode) { - if (data.match.increase[data.match.num[i]] != UINT8_MAX) + if (!clientpowerlevels[data.match.num[i]][powertype]) // No power level (splitscreen guests) + STRBUFCPY(strtime, "----"); + else { - if (data.match.increase[data.match.num[i]] > 9) - snprintf(strtime, sizeof strtime, "(+%02d)", data.match.increase[data.match.num[i]]); - else - snprintf(strtime, sizeof strtime, "(+ %d)", data.match.increase[data.match.num[i]]); + if (data.match.increase[data.match.num[i]] != INT16_MIN) + { + snprintf(strtime, sizeof strtime, "(%d)", data.match.increase[data.match.num[i]]); - if (data.match.numplayers > NUMFORNEWCOLUMN) - V_DrawRightAlignedThinString(x+135+gutter, y-1, V_6WIDTHSPACE, strtime); - else - V_DrawRightAlignedString(x+120+gutter, y, 0, strtime); + if (data.match.numplayers > NUMFORNEWCOLUMN) + V_DrawRightAlignedThinString(x+133+gutter, y-1, V_6WIDTHSPACE, strtime); + else + V_DrawRightAlignedString(x+118+gutter, y, 0, strtime); + } + + snprintf(strtime, sizeof strtime, "%d", data.match.val[i]); } - snprintf(strtime, sizeof strtime, "%d", data.match.val[i]); - if (data.match.numplayers > NUMFORNEWCOLUMN) V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, strtime); else @@ -591,9 +631,14 @@ dotimer: break; } - // Make it obvious that scrambling is happening next round. - if (cv_scrambleonchange.value && cv_teamscramble.value && (intertic/TICRATE % 2 == 0)) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!")); + //if ((intertic/TICRATE) & 1) // Make it obvious that scrambling is happening next round. (OR NOT, I GUESS) + //{ + /*if (cv_scrambleonchange.value && cv_teamscramble.value) + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!"));*/ + if (speedscramble != -1 && speedscramble != gamespeed) + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, hilicol|V_ALLOWLOWERCASE|V_SNAPTOBOTTOM, + va(M_GetText("Next race will be %s Speed!"), kartspeed_cons_t[1+speedscramble].strvalue)); + //} } // @@ -676,13 +721,46 @@ void Y_Ticker(void) { if (data.match.num[q] == MAXPLAYERS || !data.match.increase[data.match.num[q]] - || data.match.increase[data.match.num[q]] == UINT8_MAX) + || data.match.increase[data.match.num[q]] == INT16_MIN) continue; r++; data.match.jitter[data.match.num[q]] = 1; - if (--data.match.increase[data.match.num[q]]) - kaching = false; + + if (powertype != -1) + { + // Power Levels + if (abs(data.match.increase[data.match.num[q]]) < 10) + { + // Not a lot of point increase left, just set to 0 instantly + data.match.increase[data.match.num[q]] = 0; + } + else + { + SINT8 remove = 0; // default (should not happen) + + if (data.match.increase[data.match.num[q]] < 0) + remove = -10; + else if (data.match.increase[data.match.num[q]] > 0) + remove = 10; + + // Remove 10 points at a time + data.match.increase[data.match.num[q]] -= remove; + + // Still not zero, no kaching yet + if (data.match.increase[data.match.num[q]] != 0) + kaching = false; + } + } + else + { + // Basic bitch points + if (data.match.increase[data.match.num[q]]) + { + if (--data.match.increase[data.match.num[q]]) + kaching = false; + } + } } if (r) @@ -782,6 +860,153 @@ static void Y_UpdateRecordReplays(void) CV_AddValue(&cv_nextmap, -1); } +static void K_UpdatePowerLevels(void) +{ + INT32 i, j; + INT32 numplayersingame = 0, numgriefers = 0; + INT16 increment[MAXPLAYERS]; + + // Compare every single player against each other for power level increases. + // Every player you won against gives you more points, and vice versa. + // The amount of points won per match-up depends on the difference between the loser's power and the winner's power. + // See K_CalculatePowerLevelInc for more info. + + for (i = 0; i < MAXPLAYERS; i++) + { + increment[i] = 0; + + if (nospectategrief[i] != -1) + numgriefers++; + + if (!playeringame[i] || players[i].spectator) + continue; + + numplayersingame++; + } + + for (i = 0; i < numplayersingame; i++) + { + UINT16 yourpower = PWRLVRECORD_DEF; + UINT16 theirpower = PWRLVRECORD_DEF; + INT16 diff = 0; // Loser PWR.LV - Winner PWR.LV + INT16 inc = 0; // Total pt increment + UINT8 ipnum = data.match.num[i]; + UINT8 jpnum; + + CONS_Debug(DBG_GAMELOGIC, "Power Level Gain for player %d:\n", ipnum); + + if (clientpowerlevels[ipnum][powertype] == 0) // splitscreen guests don't record power level changes + continue; + yourpower = clientpowerlevels[ipnum][powertype]; + + CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", ipnum, yourpower); + + for (j = 0; j < numplayersingame; j++) + { + boolean won = false; + + jpnum = data.match.num[j]; + + if (i == j || ipnum == jpnum) // Same person + continue; + + CONS_Debug(DBG_GAMELOGIC, "Player %d VS Player %d:\n", ipnum, jpnum); + + if (data.match.val[i] == data.match.val[j]) // Tie -- neither get any points for this match up. + { + CONS_Debug(DBG_GAMELOGIC, "TIE, no change.\n"); + continue; + } + + theirpower = PWRLVRECORD_DEF; + if (clientpowerlevels[jpnum][powertype] != 0) // No power level acts as 5000 (used for splitscreen guests) + theirpower = clientpowerlevels[jpnum][powertype]; + CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower); + + if (G_RaceGametype()) + { + if (data.match.val[i] < data.match.val[j]) + won = true; + } + else + { + if (data.match.val[i] > data.match.val[j]) + won = true; + } + + if (won) // This player won! + { + diff = theirpower - yourpower; + inc += K_CalculatePowerLevelInc(diff); + CONS_Debug(DBG_GAMELOGIC, "WON! Diff is %d, total increment is %d\n", diff, inc); + } + else // This player lost... + { + diff = yourpower - theirpower; + inc -= K_CalculatePowerLevelInc(diff); + CONS_Debug(DBG_GAMELOGIC, "LOST... Diff is %d, total increment is %d\n", diff, inc); + } + } + + if (numgriefers != 0) // Automatic win against quitters. + { + for (jpnum = 0; jpnum < MAXPLAYERS; jpnum++) + { + if (nospectategrief[jpnum] == -1) // Empty slot + continue; + + if (ipnum == jpnum) // Same person + continue; + + CONS_Debug(DBG_GAMELOGIC, "Player %d VS Player %d (griefer):\n", ipnum, jpnum); + + theirpower = PWRLVRECORD_DEF; + if (nospectategrief[jpnum] != 0) // No power level acts as 5000 (used for splitscreen guests) + theirpower = nospectategrief[jpnum]; + CONS_Debug(DBG_GAMELOGIC, "Player %d's PWR.LV: %d\n", jpnum, theirpower); + + diff = theirpower - yourpower; + inc += K_CalculatePowerLevelInc(diff); + CONS_Debug(DBG_GAMELOGIC, "AUTO-WON! Diff is %d, total increment is %d\n", diff, inc); + } + } + + if (inc == 0) + { + data.match.increase[ipnum] = INT16_MIN; + CONS_Debug(DBG_GAMELOGIC, "Total Result: No increment, no change.\n"); + continue; + } + + if (yourpower + inc > PWRLVRECORD_MAX) + inc -= ((yourpower + inc) - PWRLVRECORD_MAX); + if (yourpower + inc < PWRLVRECORD_MIN) + inc -= ((yourpower + inc) - PWRLVRECORD_MIN); + + CONS_Debug(DBG_GAMELOGIC, "Total Result: Increment of %d.\n", inc); + increment[ipnum] = inc; + } + + CONS_Debug(DBG_GAMELOGIC, "Setting final power levels...\n"); + for (i = 0; i < MAXPLAYERS; i++) + { + if (increment[i] == 0) + continue; + + data.match.increase[i] = increment[i]; + clientpowerlevels[i][powertype] += data.match.increase[i]; + + if (i == consoleplayer) + { + CONS_Debug(DBG_GAMELOGIC, "Player %d is you! Saving...\n", i); + vspowerlevel[powertype] = clientpowerlevels[i][powertype]; + if (M_UpdateUnlockablesAndExtraEmblems(true)) + S_StartSound(NULL, sfx_ncitem); + G_SaveGameData(true); + } + } +} + // // Y_StartIntermission // @@ -796,6 +1021,17 @@ void Y_StartIntermission(void) I_Error("endtic is dirty"); #endif + // set player Power Level type + powertype = PWRLV_DISABLED; + + if (netgame && cv_kartusepwrlv.value) + { + if (G_RaceGametype()) + powertype = PWRLV_RACE; + else if (G_BattleGametype()) + powertype = PWRLV_BATTLE; + } + if (!multiplayer) { timer = 0; @@ -872,6 +1108,9 @@ void Y_StartIntermission(void) break; } + if (powertype != PWRLV_DISABLED) + K_UpdatePowerLevels(); + //if (intertype == int_race || intertype == int_match) { //bgtile = W_CachePatchName("SRB2BACK", PU_STATIC); diff --git a/src/z_zone.c b/src/z_zone.c index a3e13422f..2c0ddc295 100644 --- a/src/z_zone.c +++ b/src/z_zone.c @@ -210,7 +210,11 @@ void Z_Free(void *ptr) static void *xm(size_t size) { const size_t padedsize = size+sizeof (size_t); - void *p = malloc(padedsize); + void *p; + + if (padedsize < size)/* overflow check */ + I_Error("You are allocating memory too large!"); + p = malloc(padedsize); if (p == NULL) { @@ -254,6 +258,9 @@ void *Z_MallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits) CONS_Debug(DBG_MEMORY, "Z_Malloc %s:%d\n", file, line); #endif + if (blocksize < size)/* overflow check */ + I_Error("You are allocating memory too large!"); + block = xm(sizeof *block); #ifdef HAVE_VALGRIND padsize += (1<