diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bbf46945..855393de1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) # DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string. # Version change is fine. project(SRB2 - VERSION 2.2.0 + VERSION 2.2.1 LANGUAGES C) if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) diff --git a/appveyor.yml b/appveyor.yml index 6b80b4ec3..20b18d7d5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.2.0.{branch}-{build} +version: 2.2.1.{branch}-{build} os: MinGW environment: @@ -83,7 +83,14 @@ before_build: - ccache -V - ccache -s - if [%NOUPX%] == [1] ( set "NOUPX=NOUPX=1" ) else ( set "NOUPX=" ) -- set "SRB2_MFLAGS=-C src WARNINGMODE=1 CCACHE=1 NOOBJDUMP=1 %NOUPX%" +- if defined [%APPVEYOR_PULL_REQUEST_HEAD_COMMIT%] ( set "COMMIT=%APPVEYOR_PULL_REQUEST_HEAD_COMMIT%" ) else ( set "COMMIT=%APPVEYOR_REPO_COMMIT%" ) +- cmd: git rev-parse --short %COMMIT%>%TMP%/gitshort.txt +- cmd: set /P GITSHORT=<%TMP%/gitshort.txt +# for pull requests, take the owner's name only, if this isn't the same repo of course +- set "REPO=%APPVEYOR_REPO_BRANCH%" +- if not [%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%] == [] ( if not [%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%] == [%APPVEYOR_REPO_NAME%] ( for /f "delims=/" %%a in ("%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%") do set "REPO=%%a-%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%" ) ) +- set "EXENAME=EXENAME=srb2win-%REPO%-%GITSHORT%.exe" +- set "SRB2_MFLAGS=-C src WARNINGMODE=1 CCACHE=1 NOOBJDUMP=1 %NOUPX% %EXENAME%" - if [%X86_64%] == [1] ( set "MINGW_FLAGS=MINGW64=1 X86_64=1 GCC81=1" ) else ( set "MINGW_FLAGS=MINGW=1 GCC91=1" ) - set "SRB2_MFLAGS=%SRB2_MFLAGS% %MINGW_FLAGS% %CONFIGURATION%=1" @@ -99,10 +106,8 @@ after_build: ) - if [%X86_64%] == [1] ( set "CONFIGURATION=%CONFIGURATION%64" ) - ccache -s -- cmd: git rev-parse --short %APPVEYOR_REPO_COMMIT%>%TMP%/gitshort.txt -- cmd: set /P GITSHORT=<%TMP%/gitshort.txt -- set BUILD_ARCHIVE=%APPVEYOR_REPO_BRANCH%-%GITSHORT%-%CONFIGURATION%.7z -- set BUILDSARCHIVE=%APPVEYOR_REPO_BRANCH%-%CONFIGURATION%.7z +- set BUILD_ARCHIVE=%REPO%-%GITSHORT%-%CONFIGURATION%.7z +- set BUILDSARCHIVE=%REPO%-%CONFIGURATION%.7z - cmd: 7z a %BUILD_ARCHIVE% %BUILD_PATH% -xr!.gitignore - appveyor PushArtifact %BUILD_ARCHIVE% - cmd: copy %BUILD_ARCHIVE% %BUILDSARCHIVE% diff --git a/src/am_map.c b/src/am_map.c index 7dfbf4ba4..b2c7de442 100644 --- a/src/am_map.c +++ b/src/am_map.c @@ -160,6 +160,7 @@ static boolean am_stopped = true; static INT32 f_x, f_y; // location of window on screen (always zero for both) static INT32 f_w, f_h; // size of window on screen (always the screen width and height respectively) +static boolean m_keydown[4]; // which window panning keys are being pressed down? static mpoint_t m_paninc; // how far the window pans each tic (map coords) static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords) static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords) @@ -205,6 +206,7 @@ static boolean followplayer = true; // specifies whether to follow the player ar typedef void (*AMDRAWFLINEFUNC) (const fline_t *fl, INT32 color); static AMDRAWFLINEFUNC AM_drawFline; +static void AM_drawPixel(INT32 xx, INT32 yy, INT32 cc); static void AM_drawFline_soft(const fline_t *fl, INT32 color); static void AM_activateNewScale(void) @@ -344,22 +346,22 @@ static void AM_initVariables(void) old_m_h = m_h; } +// +// Called when the screen size changes. +// +static void AM_FrameBufferInit(void) +{ + f_x = f_y = 0; + f_w = vid.width; + f_h = vid.height; +} + // // should be called at the start of every level // right now, i figure it out myself // static void AM_LevelInit(void) { - f_x = f_y = 0; - f_w = vid.width; - f_h = vid.height; - - AM_drawFline = AM_drawFline_soft; -#ifdef HWRENDER - if (rendermode == render_opengl) - AM_drawFline = HWR_drawAMline; -#endif - AM_findMinMaxBoundaries(); scale_mtof = FixedDiv(min_scale_mtof*10, 7*FRACUNIT); if (scale_mtof > max_scale_mtof) @@ -381,7 +383,7 @@ void AM_Stop(void) * * \sa AM_Stop */ -static inline void AM_Start(void) +void AM_Start(void) { static INT32 lastlevel = -1; @@ -390,8 +392,12 @@ static inline void AM_Start(void) am_stopped = false; if (lastlevel != gamemap || am_recalc) // screen size changed { - AM_LevelInit(); - lastlevel = gamemap; + AM_FrameBufferInit(); + if (lastlevel != gamemap) + { + AM_LevelInit(); + lastlevel = gamemap; + } am_recalc = false; } AM_initVariables(); @@ -417,6 +423,28 @@ static void AM_maxOutWindowScale(void) AM_activateNewScale(); } +// +// set window panning +// +static void AM_setWindowPanning(void) +{ + // up and down + if (m_keydown[2]) // pan up + m_paninc.y = FTOM(F_PANINC); + else if (m_keydown[3]) // pan down + m_paninc.y = -FTOM(F_PANINC); + else + m_paninc.y = 0; + + // left and right + if (m_keydown[0]) // pan right + m_paninc.x = FTOM(F_PANINC); + else if (m_keydown[1]) // pan left + m_paninc.x = -FTOM(F_PANINC); + else + m_paninc.x = 0; +} + /** Responds to user inputs in automap mode. * * \param ev Event to possibly respond to. @@ -449,35 +477,49 @@ boolean AM_Responder(event_t *ev) { case AM_PANRIGHTKEY: // pan right if (!followplayer) - m_paninc.x = FTOM(F_PANINC); + { + m_keydown[0] = true; + AM_setWindowPanning(); + } else rc = false; break; case AM_PANLEFTKEY: // pan left if (!followplayer) - m_paninc.x = -FTOM(F_PANINC); + { + m_keydown[1] = true; + AM_setWindowPanning(); + } else rc = false; break; case AM_PANUPKEY: // pan up if (!followplayer) - m_paninc.y = FTOM(F_PANINC); + { + m_keydown[2] = true; + AM_setWindowPanning(); + } else rc = false; break; case AM_PANDOWNKEY: // pan down if (!followplayer) - m_paninc.y = -FTOM(F_PANINC); + { + m_keydown[3] = true; + AM_setWindowPanning(); + } else rc = false; break; case AM_ZOOMOUTKEY: // zoom out mtof_zoommul = M_ZOOMOUT; ftom_zoommul = M_ZOOMIN; + AM_setWindowPanning(); break; case AM_ZOOMINKEY: // zoom in mtof_zoommul = M_ZOOMIN; ftom_zoommul = M_ZOOMOUT; + AM_setWindowPanning(); break; case AM_TOGGLEKEY: AM_Stop(); @@ -491,6 +533,7 @@ boolean AM_Responder(event_t *ev) } else AM_restoreScaleAndLoc(); + AM_setWindowPanning(); break; case AM_FOLLOWKEY: followplayer = !followplayer; @@ -510,14 +553,32 @@ boolean AM_Responder(event_t *ev) switch (ev->data1) { case AM_PANRIGHTKEY: + if (!followplayer) + { + m_keydown[0] = false; + AM_setWindowPanning(); + } + break; case AM_PANLEFTKEY: if (!followplayer) - m_paninc.x = 0; + { + m_keydown[1] = false; + AM_setWindowPanning(); + } break; case AM_PANUPKEY: + if (!followplayer) + { + m_keydown[2] = false; + AM_setWindowPanning(); + } + break; case AM_PANDOWNKEY: if (!followplayer) - m_paninc.y = 0; + { + m_keydown[3] = false; + AM_setWindowPanning(); + } break; case AM_ZOOMOUTKEY: case AM_ZOOMINKEY: @@ -718,6 +779,17 @@ static boolean AM_clipMline(const mline_t *ml, fline_t *fl) } #undef DOOUTCODE +// +// Draws a pixel. +// +static void AM_drawPixel(INT32 xx, INT32 yy, INT32 cc) +{ + UINT8 *dest = screens[0]; + if (xx < 0 || yy < 0 || xx >= vid.width || yy >= vid.height) + return; // off the screen + dest[(yy*vid.width) + xx] = cc; +} + // // Classic Bresenham w/ whatever optimizations needed for speed // @@ -739,8 +811,6 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color) } #endif - #define PUTDOT(xx,yy,cc) V_DrawFill(xx,yy,1,1,cc|V_NOSCALESTART); - dx = fl->b.x - fl->a.x; ax = 2 * (dx < 0 ? -dx : dx); sx = dx < 0 ? -1 : 1; @@ -757,7 +827,7 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color) d = ay - ax/2; for (;;) { - PUTDOT(x, y, color) + AM_drawPixel(x, y, color); if (x == fl->b.x) return; if (d >= 0) @@ -774,7 +844,7 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color) d = ax - ay/2; for (;;) { - PUTDOT(x, y, color) + AM_drawPixel(x, y, color); if (y == fl->b.y) return; if (d >= 0) @@ -786,8 +856,6 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color) d += ax; } } - - #undef PUTDOT } // @@ -1100,6 +1168,12 @@ void AM_Drawer(void) if (!automapactive) return; + AM_drawFline = AM_drawFline_soft; +#ifdef HWRENDER + if (rendermode == render_opengl) + AM_drawFline = HWR_drawAMline; +#endif + AM_clearFB(BACKGROUND); if (draw_grid) AM_drawGrid(GRIDCOLORS); AM_drawWalls(); diff --git a/src/am_map.h b/src/am_map.h index 462bb3d6d..0560207bb 100644 --- a/src/am_map.h +++ b/src/am_map.h @@ -38,6 +38,9 @@ void AM_Ticker(void); // Called by main loop, instead of view drawer if automap is active. void AM_Drawer(void); +// Enables the automap. +void AM_Start(void); + // Called to force the automap to quit if the level is completed while it is up. void AM_Stop(void); diff --git a/src/command.c b/src/command.c index 0afc07118..cb6803b57 100644 --- a/src/command.c +++ b/src/command.c @@ -823,8 +823,13 @@ static void COM_Help_f(void) if (!stricmp(cvar->PossibleValue[MINVAL].strvalue, "MIN")) { if (floatmode) - CONS_Printf(" range from %f to %f\n", FIXED_TO_FLOAT(cvar->PossibleValue[MINVAL].value), - FIXED_TO_FLOAT(cvar->PossibleValue[MAXVAL].value)); + { + float fu = FIXED_TO_FLOAT(cvar->PossibleValue[MINVAL].value); + float ck = FIXED_TO_FLOAT(cvar->PossibleValue[MAXVAL].value); + CONS_Printf(" range from %ld%s to %ld%s\n", + (long)fu, M_Ftrim(fu), + (long)ck, M_Ftrim(ck)); + } else CONS_Printf(" range from %d to %d\n", cvar->PossibleValue[MINVAL].value, cvar->PossibleValue[MAXVAL].value); @@ -973,7 +978,10 @@ static void COM_Add_f(void) } if (( cvar->flags & CV_FLOAT )) - CV_Set(cvar, va("%f", FIXED_TO_FLOAT (cvar->value) + atof(COM_Argv(2)))); + { + float n =FIXED_TO_FLOAT (cvar->value) + atof(COM_Argv(2)); + CV_Set(cvar, va("%ld%s", (long)n, M_Ftrim(n))); + } else CV_AddValue(cvar, atoi(COM_Argv(2))); } @@ -1457,15 +1465,8 @@ static void Got_NetVar(UINT8 **p, INT32 playernum) { // not from server or remote admin, must be hacked/buggy client CONS_Alert(CONS_WARNING, M_GetText("Illegal netvar command received from %s\n"), player_names[playernum]); - if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } netid = READUINT16(*p); diff --git a/src/config.h.in b/src/config.h.in index 233cbdc53..498f3086d 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -26,12 +26,12 @@ #else /* Manually defined asset hashes for non-CMake builds - * Last updated 2019 / 12 / 06 - v2.2.0 - main assets + * Last updated 2020 / 02 / 15 - v2.2.1 - main assets * Last updated 20?? / ?? / ?? - v2.2.? - patch.pk3 */ -#define ASSET_HASH_SRB2_PK3 "51419a33b4982d840c6772c159ba7c0a" -#define ASSET_HASH_ZONES_PK3 "df74843919fd51af26a0baa8e21e4c19" -#define ASSET_HASH_PLAYER_DTA "56a247e074dd0dc794b6617efef1e918" +#define ASSET_HASH_SRB2_PK3 "0277c9416756627004e83cbb5b2e3e28" +#define ASSET_HASH_ZONES_PK3 "f7e88afb6af7996a834c7d663144bead" +#define ASSET_HASH_PLAYER_DTA "ad49e07b17cc662f1ad70c454910b4ae" #ifdef USE_PATCH_DTA #define ASSET_HASH_PATCH_PK3 "there is no patch.pk3, only zuul" #endif diff --git a/src/console.c b/src/console.c index 01d1ddaa2..59d2b3e6c 100644 --- a/src/console.c +++ b/src/console.c @@ -32,6 +32,7 @@ #include "d_main.h" #include "m_menu.h" #include "filesrch.h" +#include "m_misc.h" #ifdef _WINDOWS #include "win32/win_main.h" @@ -815,6 +816,33 @@ boolean CON_Responder(event_t *ev) || key == KEY_LALT || key == KEY_RALT) return true; + if (key == KEY_LEFTARROW) + { + if (input_cur != 0) + { + if (ctrldown) + input_cur = M_JumpWordReverse(inputlines[inputline], input_cur); + else + --input_cur; + } + if (!shiftdown) + input_sel = input_cur; + return true; + } + else if (key == KEY_RIGHTARROW) + { + if (input_cur < input_len) + { + if (ctrldown) + input_cur += M_JumpWord(&inputlines[inputline][input_cur]); + else + ++input_cur; + } + if (!shiftdown) + input_sel = input_cur; + return true; + } + // ctrl modifier -- changes behavior, adds shortcuts if (ctrldown) { @@ -967,23 +995,6 @@ boolean CON_Responder(event_t *ev) con_scrollup--; return true; } - - if (key == KEY_LEFTARROW) - { - if (input_cur != 0) - --input_cur; - if (!shiftdown) - input_sel = input_cur; - return true; - } - else if (key == KEY_RIGHTARROW) - { - if (input_cur < input_len) - ++input_cur; - if (!shiftdown) - input_sel = input_cur; - return true; - } else if (key == KEY_HOME) { input_cur = 0; @@ -1551,9 +1562,14 @@ static void CON_DrawConsole(void) if (cons_backpic.value || con_forcepic) { patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_PATCH); + int h; + + h = con_curlines/vid.dupy; // Jimita: CON_DrawBackpic just called V_DrawScaledPatch - V_DrawScaledPatch(0, 0, 0, con_backpic); + //V_DrawScaledPatch(0, 0, 0, con_backpic); + V_DrawCroppedPatch(0, 0, FRACUNIT, 0, con_backpic, + 0, ( BASEVIDHEIGHT - h ), BASEVIDWIDTH, h); W_UnlockCachedPatch(con_backpic); } diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 46f404f9a..77118110e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -21,6 +21,7 @@ #include "d_net.h" #include "d_main.h" #include "g_game.h" +#include "st_stuff.h" #include "hu_stuff.h" #include "keys.h" #include "g_input.h" // JOY1 @@ -76,6 +77,7 @@ char motd[254], server_context[8]; // Message of the Day, Unique Context (even w // Server specific vars UINT8 playernode[MAXPLAYERS]; +char playeraddress[MAXPLAYERS][64]; // Minimum timeout for sending the savegame // The actual timeout will be longer depending on the savegame length @@ -391,11 +393,7 @@ static void ExtraDataTicker(void) { if (server) { - UINT8 buf[3]; - - buf[0] = (UINT8)i; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic)); } CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]); @@ -406,8 +404,8 @@ static void ExtraDataTicker(void) } // If you are a client, you can safely forget the net commands for this tic - // If you are the server, you need to remember them until every client has been aknowledged, - // because if you need to resend a PT_SERVERTICS packet, you need to put the commands in it + // If you are the server, you need to remember them until every client has been acknowledged, + // because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it if (client) D_FreeTextcmd(gametic); } @@ -437,6 +435,18 @@ void D_ResetTiccmds(void) D_Clearticcmd(textcmds[i]->tic); } +void SendKick(UINT8 playernum, UINT8 msg) +{ + UINT8 buf[2]; + + if (!(server && cv_rejointimeout.value)) + msg &= ~KICK_MSG_KEEP_BODY; + + buf[0] = playernum; + buf[1] = msg; + SendNetXCmd(XD_KICK, &buf, 2); +} + // ----------------------------------------------------------------- // end of extra data function // ----------------------------------------------------------------- @@ -1053,10 +1063,7 @@ static void SV_SendResynch(INT32 node) if (resynch_score[node] > (unsigned)cv_resynchattempts.value*250) { - UINT8 buf[2]; - buf[0] = (UINT8)nodetoplayer[node]; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(nodetoplayer[node], KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); resynch_score[node] = 0; } } @@ -1271,8 +1278,14 @@ static boolean CL_SendJoin(void) netbuffer->u.clientcfg.subversion = SUBVERSION; strncpy(netbuffer->u.clientcfg.application, SRB2APPLICATION, sizeof netbuffer->u.clientcfg.application); + + CleanupPlayerName(consoleplayer, cv_playername.zstring); + if (splitscreen) + CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */ + strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME); strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME); + return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak)); } @@ -1346,11 +1359,11 @@ static void SV_SendPlayerInfo(INT32 node) { if (!playeringame[i]) { - netbuffer->u.playerinfo[i].node = 255; // This slot is empty. + netbuffer->u.playerinfo[i].num = 255; // This slot is empty. continue; } - netbuffer->u.playerinfo[i].node = i; + netbuffer->u.playerinfo[i].num = i; strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1); netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0'; @@ -1625,6 +1638,7 @@ static void CL_LoadReceivedSavegame(void) paused = false; demoplayback = false; + titlemapinaction = TITLEMAP_OFF; titledemo = false; automapactive = false; @@ -2410,6 +2424,7 @@ void CL_ClearPlayer(INT32 playernum) if (players[playernum].mo) P_RemoveMobj(players[playernum].mo); memset(&players[playernum], 0, sizeof (player_t)); + memset(playeraddress[playernum], 0, sizeof(*playeraddress)); } // @@ -2417,7 +2432,7 @@ void CL_ClearPlayer(INT32 playernum) // // Removes a player from the current game // -static void CL_RemovePlayer(INT32 playernum, INT32 reason) +static void CL_RemovePlayer(INT32 playernum, kickreason_t reason) { // Sanity check: exceptional cases (i.e. c-fails) can cause multiple // kick commands to be issued for the same player. @@ -2430,9 +2445,6 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason) playerpernode[node]--; if (playerpernode[node] <= 0) { - // If a resynch was in progress, well, it no longer needs to be. - SV_InitResynchVars(playernode[playernum]); - nodeingame[playernode[playernum]] = false; Net_CloseConnection(playernode[playernum]); ResetNode(node); @@ -2554,6 +2566,7 @@ void CL_Reset(void) multiplayer = false; servernode = 0; server = true; + resynch_local_inprogress = false; doomcom->numnodes = 1; doomcom->numslots = 1; SV_StopServer(); @@ -2827,9 +2840,12 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) char buf[3 + MAX_REASONLENGTH]; char *reason = buf; kickreason_t kickreason = KR_KICK; + boolean keepbody; pnum = READUINT8(*p); msg = READUINT8(*p); + keepbody = (msg & KICK_MSG_KEEP_BODY) != 0; + msg &= ~KICK_MSG_KEEP_BODY; if (pnum == serverplayer && IsPlayerAdmin(playernum)) { @@ -2889,6 +2905,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) */ pnum = playernum; msg = KICK_MSG_CON_FAIL; + keepbody = true; } //CONS_Printf("\x82%s ", player_names[pnum]); @@ -2908,7 +2925,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) switch (msg) { case KICK_MSG_GO_AWAY: - HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false); + if (!players[pnum].quittime) + HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false); kickreason = KR_KICK; break; case KICK_MSG_PING_HIGH: @@ -2957,7 +2975,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) kickreason = KR_TIMEOUT; break; case KICK_MSG_PLAYER_QUIT: - if (netgame) // not splitscreen/bots + if (netgame && !players[pnum].quittime) // not splitscreen/bots or soulless body HU_AddChatText(va("\x82*%s left the game", player_names[pnum]), false); kickreason = KR_LEAVE; break; @@ -2998,6 +3016,24 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) else M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING); } + else if (keepbody) + { + if (server && !demoplayback) + { + INT32 node = playernode[pnum]; + playerpernode[node]--; + if (playerpernode[node] <= 0) + { + nodeingame[node] = false; + Net_CloseConnection(node); + ResetNode(node); + } + } + + playernode[pnum] = UINT8_MAX; + + players[pnum].quittime = 1; + } else CL_RemovePlayer(pnum, kickreason); } @@ -3006,6 +3042,9 @@ consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; +consvar_t cv_rejointimeout = {"rejointimeout", "Off", CV_SAVE|CV_FLOAT, rejointimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; consvar_t cv_resynchattempts = {"resynchattempts", "10", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL }; consvar_t cv_blamecfail = {"blamecfail", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; @@ -3075,6 +3114,7 @@ static void ResetNode(INT32 node) nodewaiting[node] = 0; playerpernode[node] = 0; sendingsavegame[node] = false; + SV_InitResynchVars(node); } void SV_ResetServer(void) @@ -3090,13 +3130,8 @@ void SV_ResetServer(void) tictoclear = maketic; for (i = 0; i < MAXNETNODES; i++) - { ResetNode(i); - // Make sure resynch status doesn't get carried over! - SV_InitResynchVars(i); - } - for (i = 0; i < MAXPLAYERS; i++) { #ifdef HAVE_BLUA @@ -3104,6 +3139,7 @@ void SV_ResetServer(void) #endif playeringame[i] = false; playernode[i] = UINT8_MAX; + memset(playeraddress[i], 0, sizeof(*playeraddress)); sprintf(player_names[i], "Player %d", i + 1); adminplayers[i] = -1; // Populate the entire adminplayers array with -1. } @@ -3194,6 +3230,37 @@ void D_QuitNetGame(void) #endif } +static INT32 FindRejoinerNum(SINT8 node) +{ + char strippednodeaddress[64]; + const char *nodeaddress; + char *port; + INT32 i; + + // Make sure there is no dead dress before proceeding to the stripping + if (!I_GetNodeAddress) + return -1; + nodeaddress = I_GetNodeAddress(node); + if (!nodeaddress) + return -1; + + // Strip the address of its port + strcpy(strippednodeaddress, nodeaddress); + port = strchr(strippednodeaddress, ':'); + if (port) + *port = '\0'; + + // Check if any player matches the stripped address + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX + && !strcmp(playeraddress[i], strippednodeaddress)) + return i; + } + + return -1; +} + // Adds a node to the game (player will follow at map change or at savegame....) static inline void SV_AddNode(INT32 node) { @@ -3210,19 +3277,16 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) { INT16 node, newplayernum; boolean splitscreenplayer; + boolean rejoined; + player_t *newplayer; + char *port; if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { // protect against hacked/buggy client CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3231,15 +3295,34 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) splitscreenplayer = newplayernum & 0x80; newplayernum &= ~0x80; - // Clear player before joining, lest some things get set incorrectly - // HACK: don't do this for splitscreen, it relies on preset values - if (!splitscreen && !botingame) - CL_ClearPlayer(newplayernum); - playeringame[newplayernum] = true; + rejoined = playeringame[newplayernum]; + + if (!rejoined) + { + // Clear player before joining, lest some things get set incorrectly + // HACK: don't do this for splitscreen, it relies on preset values + if (!splitscreen && !botingame) + CL_ClearPlayer(newplayernum); + playeringame[newplayernum] = true; + G_AddPlayer(newplayernum); + if (newplayernum+1 > doomcom->numslots) + doomcom->numslots = (INT16)(newplayernum+1); + + if (server && I_GetNodeAddress) + { + strcpy(playeraddress[newplayernum], I_GetNodeAddress(node)); + port = strchr(playeraddress[newplayernum], ':'); + if (port) + *port = '\0'; + } + } + + newplayer = &players[newplayernum]; + + newplayer->jointime = 0; + newplayer->quittime = 0; + READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME); - G_AddPlayer(newplayernum); - if (newplayernum+1 > doomcom->numslots) - doomcom->numslots = (INT16)(newplayernum+1); // the server is creating my player if (node == mynode) @@ -3257,29 +3340,67 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) secondarydisplayplayer = newplayernum; DEBFILE("spawning my brother\n"); if (botingame) - players[newplayernum].bot = 1; + newplayer->bot = 1; } D_SendPlayerConfig(); addedtogame = true; + + if (rejoined) + { + if (newplayer->mo) + { + if (!splitscreenplayer) + localangle = newplayer->mo->angle; + else + localangle2 = newplayer->mo->angle; + + newplayer->viewheight = 41*newplayer->height/48; + + if (newplayer->mo->eflags & MFE_VERTICALFLIP) + newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight; + else + newplayer->viewz = newplayer->mo->z + newplayer->viewheight; + } + + // wake up the status bar + ST_Start(); + // wake up the heads up text + HU_Start(); + + if (camera.chase && !splitscreenplayer) + P_ResetCamera(newplayer, &camera); + if (camera2.chase && splitscreenplayer) + P_ResetCamera(newplayer, &camera2); + } } if (netgame) { - if (server && cv_showjoinaddress.value) - { - const char *address; - if (I_GetNodeAddress && (address = I_GetNodeAddress(node)) != NULL) - HU_AddChatText(va("\x82*%s has joined the game (player %d) (%s)", player_names[newplayernum], newplayernum, address), false); // merge join notification + IP to avoid clogging console/chat. - } + char joinmsg[256]; + + if (rejoined) + strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)")); else - HU_AddChatText(va("\x82*%s has joined the game (player %d)", player_names[newplayernum], newplayernum), false); // if you don't wanna see the join address. + strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)")); + strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); + + // Merge join notification + IP to avoid clogging console/chat + if (server && cv_showjoinaddress.value && I_GetNodeAddress) + { + const char *address = I_GetNodeAddress(node); + if (address) + strcat(joinmsg, va(" (%s)", address)); + } + + HU_AddChatText(joinmsg, false); } if (server && multiplayer && motd[0] != '\0') COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); #ifdef HAVE_BLUA - LUAh_PlayerJoin(newplayernum); + if (!rejoined) + LUAh_PlayerJoin(newplayernum); #endif } @@ -3288,11 +3409,7 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) INT32 node, n, newplayer = false; UINT8 buf[2 + MAXPLAYERNAME]; UINT8 *p; - UINT8 newplayernum = 0; - - // What is the reason for this? Why can't newplayernum always be 0? - if (dedicated) - newplayernum = 1; + INT32 newplayernum; for (node = 0; node < MAXNETNODES; node++) { @@ -3301,68 +3418,22 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) { newplayer = true; - if (netgame) - // !!!!!!!!! EXTREMELY SUPER MEGA GIGA ULTRA ULTIMATELY TERRIBLY IMPORTANT !!!!!!!!! - // - // The line just after that comment is an awful, horrible, terrible, TERRIBLE hack. - // - // Basically, the fix I did in order to fix the download freezes happens - // to cause situations in which a player number does not match - // the node number associated to that player. - // That is totally normal, there is absolutely *nothing* wrong with that. - // Really. Player 7 being tied to node 29, for instance, is totally fine. - // - // HOWEVER. A few (broken) parts of the netcode do the TERRIBLE mistake - // of mixing up the concepts of node and player, resulting in - // incorrect handling of cases where a player is tied to a node that has - // a different number (which is a totally normal case, or at least should be). - // This incorrect handling can go as far as literally - // anyone from joining your server at all, forever. - // - // Given those two facts, there are two options available - // in order to let this download freeze fix be: - // 1) Fix the broken parts that assume a node is a player or similar bullshit. - // 2) Change the part this comment is located at, so that any player who joins - // is given the same number as their associated node. - // - // No need to say, 1) is by far the obvious best, whereas 2) is a terrible hack. - // Unfortunately, after trying 1), I most likely didn't manage to find all - // of those broken parts, and thus 2) has become the only safe option that remains. - // - // So I did this hack. - // - // If it isn't clear enough, in order to get rid of this ugly hack, - // you will have to fix all parts of the netcode that - // make a confusion between nodes and players. - // - // And if it STILL isn't clear enough, a node and a player - // is NOT the same thing. Never. NEVER. *NEVER*. - // - // And if someday you make the terrible mistake of - // daring to have the unforgivable idea to try thinking - // that a node might possibly be the same as a player, - // or that a player should have the same number as its node, - // be sure that I will somehow know about it and - // hunt you down tirelessly and make you regret it, - // even if you live on the other side of the world. - // - // TODO: vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - // \todo >>>>>>>>>> Remove this horrible hack as soon as possible <<<<<<<<<< - // TODO: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // - // !!!!!!!!! EXTREMELY SUPER MEGA GIGA ULTRA ULTIMATELY TERRIBLY IMPORTANT !!!!!!!!! - newplayernum = node; // OMFG SAY WELCOME TO TEH NEW HACK FOR FIX FIL DOWNLOAD!!1! - else // Don't use the hack if we don't have to + newplayernum = FindRejoinerNum(node); + if (newplayernum == -1) + { // search for a free playernum // we can't use playeringame since it is not updated here - for (; newplayernum < MAXPLAYERS; newplayernum++) + for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) { + if (playeringame[newplayernum]) + continue; for (n = 0; n < MAXNETNODES; n++) if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum) break; if (n == MAXNETNODES) break; } + } // should never happen since we check the playernum // before accepting the join @@ -3389,8 +3460,6 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) SendNetXCmd(XD_ADDPLAYER, &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) - newplayernum++; } } @@ -3405,14 +3474,10 @@ void CL_AddSplitscreenPlayer(void) void CL_RemoveSplitscreenPlayer(void) { - UINT8 buf[2]; - if (cl_mode != CL_CONNECTED) return; - buf[0] = (UINT8)secondarydisplayplayer; - buf[1] = KICK_MSG_PLAYER_QUIT; - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(secondarydisplayplayer, KICK_MSG_PLAYER_QUIT); } // is there a game running @@ -3520,8 +3585,11 @@ static size_t TotalTextCmdPerTic(tic_t tic) static void HandleConnect(SINT8 node) { char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; + INT32 rejoinernum; INT32 i; + rejoinernum = FindRejoinerNum(node); + if (bannednode && bannednode[node]) SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server")); else if (netbuffer->u.clientcfg._255 != 255 || @@ -3533,9 +3601,9 @@ static void HandleConnect(SINT8 node) else if (netbuffer->u.clientcfg.version != VERSION || netbuffer->u.clientcfg.subversion != SUBVERSION) SV_SendRefuse(node, va(M_GetText("Different SRB2 versions cannot\nplay a netgame!\n(server version %d.%d.%d)"), VERSION/100, VERSION%100, SUBVERSION)); - else if (!cv_allownewplayer.value && node) + else if (!cv_allownewplayer.value && node && rejoinernum == -1) SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment")); - else if (D_NumPlayers() >= cv_maxplayers.value) + else if (D_NumPlayers() >= cv_maxplayers.value && rejoinernum == -1) SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value)); else if (netgame && netbuffer->u.clientcfg.localplayers > 1) // Hacked client? SV_SendRefuse(node, M_GetText("Too many players from\nthis node.")); @@ -3550,7 +3618,7 @@ static void HandleConnect(SINT8 node) for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++) { strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); - if (!EnsurePlayerNameIsGood(names[i], -1)) + if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) { SV_SendRefuse(node, "Bad player name"); return; @@ -3951,13 +4019,10 @@ static void HandlePacketFromPlayer(SINT8 node) if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) { - char buf[2]; CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); //D_Clearticcmd(k); - buf[0] = (char)netconsole; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(netconsole, KICK_MSG_CON_FAIL); break; } @@ -3994,11 +4059,7 @@ static void HandlePacketFromPlayer(SINT8 node) } else { - UINT8 buf[3]; - - buf[0] = (UINT8)netconsole; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", netconsole, realstart, consistancy[realstart%BACKUPTICS], SHORT(netbuffer->u.clientpak.consistancy))); @@ -4111,19 +4172,21 @@ static void HandlePacketFromPlayer(SINT8 node) nodewaiting[node] = 0; if (netconsole != -1 && playeringame[netconsole]) { - UINT8 buf[2]; - buf[0] = (UINT8)netconsole; + UINT8 kickmsg; + if (netbuffer->packettype == PT_NODETIMEOUT) - buf[1] = KICK_MSG_TIMEOUT; + kickmsg = KICK_MSG_TIMEOUT; else - buf[1] = KICK_MSG_PLAYER_QUIT; - SendNetXCmd(XD_KICK, &buf, 2); + kickmsg = KICK_MSG_PLAYER_QUIT; + kickmsg |= KICK_MSG_KEEP_BODY; + + SendKick(netconsole, kickmsg); nodetoplayer[node] = -1; + if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 && playeringame[(UINT8)nodetoplayer2[node]]) { - buf[0] = nodetoplayer2[node]; - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(nodetoplayer2[node], kickmsg); nodetoplayer2[node] = -1; } } @@ -4136,15 +4199,8 @@ static void HandlePacketFromPlayer(SINT8 node) if (node != servernode) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHEND", node); - if (server) - { - UINT8 buf[2]; - buf[0] = (UINT8)node; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } resynch_local_inprogress = false; @@ -4161,15 +4217,8 @@ static void HandlePacketFromPlayer(SINT8 node) if (node != servernode) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); - if (server) - { - UINT8 buf[2]; - buf[0] = (UINT8)node; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } @@ -4228,15 +4277,8 @@ static void HandlePacketFromPlayer(SINT8 node) if (node != servernode) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHING", node); - if (server) - { - char buf[2]; - buf[0] = (char)node; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } resynch_local_inprogress = true; @@ -4247,15 +4289,8 @@ static void HandlePacketFromPlayer(SINT8 node) if (node != servernode) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); - if (server) - { - char buf[2]; - buf[0] = (char)node; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } @@ -4278,15 +4313,8 @@ static void HandlePacketFromPlayer(SINT8 node) if (node != servernode) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); - if (server) - { - UINT8 buf[2]; - buf[0] = (UINT8)node; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } if (client) @@ -4482,7 +4510,7 @@ static void CL_SendClientCmd(void) packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16); HSendPacket(servernode, false, 0, packetsize); } - else if (gamestate != GS_NULL) + else if (gamestate != GS_NULL && (addedtogame || dedicated)) { G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); @@ -4656,8 +4684,14 @@ static void Local_Maketic(INT32 realtics) G_BuildTiccmd(&localcmds2, realtics, 2); localcmds.angleturn |= TICCMD_RECEIVED; + localcmds2.angleturn |= TICCMD_RECEIVED; } +// This function is utter bullshit and is responsible for +// the random desynch that happens when a player spawns. +// This is because ticcmds are resent to clients if a packet +// was dropped, and thus modifying them can lead to several +// clients having their ticcmds set to different values. void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle) { tic_t tic; @@ -4691,28 +4725,36 @@ void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle) // create missed tic static void SV_Maketic(void) { - INT32 j; + INT32 i; - for (j = 0; j < MAXNETNODES; j++) - if (playerpernode[j]) + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + // We didn't receive this tic + if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0) { - INT32 player = nodetoplayer[j]; - if ((netcmds[maketic%BACKUPTICS][player].angleturn & TICCMD_RECEIVED) == 0) - { // we didn't receive this tic - INT32 i; + ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i]; + ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i]; - DEBFILE(va("MISS tic%4d for node %d\n", maketic, j)); -#if defined(PARANOIA) && 0 - CONS_Debug(DBG_NETPLAY, "Client Misstic %d\n", maketic); -#endif - // copy the old tic - for (i = 0; i < playerpernode[j]; i++, player = nodetoplayer2[j]) - { - netcmds[maketic%BACKUPTICS][player] = netcmds[(maketic-1)%BACKUPTICS][player]; - netcmds[maketic%BACKUPTICS][player].angleturn &= ~TICCMD_RECEIVED; - } + if (players[i].quittime) + { + // Copy the angle/aiming from the previous tic + // and empty the other inputs + memset(ticcmd, 0, sizeof(netcmds[0][0])); + ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED; + ticcmd->aiming = prevticcmd->aiming; + } + else + { + DEBFILE(va("MISS tic%4d for player %d\n", maketic, i)); + // Copy the input from the previous tic + *ticcmd = *prevticcmd; + ticcmd->angleturn &= ~TICCMD_RECEIVED; } } + } // all tic are now proceed make the next maketic++; @@ -4833,13 +4875,8 @@ static inline void PingUpdate(void) if (pingtimeout[i] > cv_pingtimeout.value) // ok your net has been bad for too long, you deserve to die. { - char buf[2]; - pingtimeout[i] = 0; - - buf[0] = (char)i; - buf[1] = KICK_MSG_PING_HIGH; - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); } } /* diff --git a/src/d_clisrv.h b/src/d_clisrv.h index c797e5ca8..df93fe31d 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -27,7 +27,7 @@ This version is independent of the mod name, and standard version and subversion. It should only account for the basic fields of the packet, and change infrequently. */ -#define PACKETVERSION 1 +#define PACKETVERSION 2 // Network play related stuff. // There is a data struct that stores network @@ -366,7 +366,6 @@ typedef struct UINT8 cheatsenabled; UINT8 isdedicated; UINT8 fileneedednum; - SINT8 adminplayer; tic_t time; tic_t leveltime; char servername[MAXSERVERNAME]; @@ -398,7 +397,7 @@ typedef struct // Shorter player information for external use. typedef struct { - UINT8 node; + UINT8 num; char name[MAXPLAYERNAME+1]; UINT8 address[4]; // sending another string would run us up against MAXPACKETLENGTH UINT8 team; @@ -486,6 +485,7 @@ extern consvar_t cv_playbackspeed; #define KICK_MSG_PING_HIGH 6 #define KICK_MSG_CUSTOM_KICK 7 #define KICK_MSG_CUSTOM_BAN 8 +#define KICK_MSG_KEEP_BODY 0x80 typedef enum { @@ -513,7 +513,9 @@ extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS]; extern tic_t servermaxping; -extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed; +extern consvar_t cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_rejointimeout; +extern consvar_t cv_resynchattempts, cv_blamecfail; +extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; // Used in d_net, the only dependence tic_t ExpandTics(INT32 low); @@ -523,6 +525,7 @@ void D_ClientServerInit(void); void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)); void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam); void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player +void SendKick(UINT8 playernum, UINT8 msg); // Create any new ticcmds and broadcast to other players. void NetUpdate(void); diff --git a/src/d_main.c b/src/d_main.c index 92492e747..99a0c2947 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -266,6 +266,9 @@ static void D_Display(void) #endif } + if (rendermode == render_soft && !splitscreen) + R_CheckViewMorph(); + // change the view size if needed if (setsizeneeded || setrenderstillneeded) { @@ -446,6 +449,9 @@ static void D_Display(void) // Image postprocessing effect if (rendermode == render_soft) { + if (!splitscreen) + R_ApplyViewMorph(); + if (postimgtype) V_DoPostProcessor(0, postimgtype, postimgparam); if (postimgtype2) @@ -652,8 +658,14 @@ void D_SRB2Loop(void) // hack to start on a nice clear console screen. COM_ImmedExecute("cls;version"); - V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(W_GetNumForName("CONSBACK"), PU_CACHE)); I_FinishUpdate(); // page flip or blit buffer + /* + LMFAO this was showing garbage under OpenGL + because I_FinishUpdate was called afterward + */ + /* Smells like a hack... Don't fade Sonic's ass into the title screen. */ + if (gamestate != GS_TITLESCREEN) + V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(W_GetNumForName("CONSBACK"), PU_CACHE)); for (;;) { @@ -1046,10 +1058,8 @@ void D_SRB2Main(void) I_OutputMsg("setvbuf didnt work\n"); #endif -#ifdef GETTEXT // initialise locale code M_StartupLocale(); -#endif // get parameters from a response file (eg: srb2 @parms.txt) M_FindResponseFile(); @@ -1210,7 +1220,7 @@ void D_SRB2Main(void) #endif D_CleanFile(); -#ifndef DEVELOP // md5s last updated 06/12/19 (ddmmyy) +#ifndef DEVELOP // md5s last updated 16/02/20 (ddmmyy) // Check MD5s of autoloaded files W_VerifyFileMD5(0, ASSET_HASH_SRB2_PK3); // srb2.pk3 diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4bf592810..8a49391f0 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -579,6 +579,7 @@ void D_RegisterServerCommands(void) // d_clisrv CV_RegisterVar(&cv_maxplayers); + CV_RegisterVar(&cv_rejointimeout); CV_RegisterVar(&cv_resynchattempts); CV_RegisterVar(&cv_maxsend); CV_RegisterVar(&cv_noticedownload); @@ -631,7 +632,7 @@ void D_RegisterClientCommands(void) // Set default player names // Monster Iestyn (12/08/19): not sure where else I could have actually put this, but oh well for (i = 0; i < MAXPLAYERS; i++) - sprintf(player_names[i], "Player %d", i); + sprintf(player_names[i], "Player %d", 1 + i); if (dedicated) return; @@ -676,6 +677,7 @@ void D_RegisterClientCommands(void) // GIF variables CV_RegisterVar(&cv_gif_optimize); CV_RegisterVar(&cv_gif_downscale); + CV_RegisterVar(&cv_gif_localcolortable); #ifdef WALLSPLATS CV_RegisterVar(&cv_splats); @@ -1007,7 +1009,7 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) * SetPlayerName * \author Graue */ -static void CleanupPlayerName(INT32 playernum, const char *newname) +void CleanupPlayerName(INT32 playernum, const char *newname) { char *buf; char *p; @@ -1035,6 +1037,17 @@ static void CleanupPlayerName(INT32 playernum, const char *newname) tmpname = p; + do + { + /* from EnsurePlayerNameIsGood */ + if (!isprint(*p) || *p == ';' || (UINT8)*p >= 0x80) + break; + } + while (*++p) ; + + if (*p)/* bad char found */ + break; + // Remove trailing spaces. p = &tmpname[strlen(tmpname)-1]; // last character while (*p == ' ' && p >= tmpname) @@ -1124,13 +1137,7 @@ static void SetPlayerName(INT32 playernum, char *newname) { CONS_Printf(M_GetText("Player %d sent a bad name change\n"), playernum+1); if (server && netgame) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } } @@ -1205,7 +1212,7 @@ static INT32 snacpending = 0, snac2pending = 0, chmappending = 0; // static void SendNameAndColor(void) { - char buf[MAXPLAYERNAME+2]; + char buf[MAXPLAYERNAME+6]; char *p; p = buf; @@ -1488,12 +1495,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (kick) { - UINT8 buf[2]; CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s (team: %d), color: %d)\n"), player_names[playernum], p->ctfteam, p->skincolor); - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } } @@ -2033,13 +2036,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal map change received from %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2150,13 +2147,7 @@ static void Got_Pause(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal pause command received from %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2231,13 +2222,7 @@ static void Got_Suicide(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal suicide command received from %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2300,13 +2285,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal clear scores command received from %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2653,13 +2632,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) // this should never happen unless the client is hacked/buggy CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } if (NetPacket.packet.verification) // Special marker that the server sent the request @@ -2668,13 +2641,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } playernum = NetPacket.packet.playernum; @@ -2707,13 +2674,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } return; } @@ -2771,12 +2732,8 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) if (server && ((NetPacket.packet.newteam < 0 || NetPacket.packet.newteam > 3) || error)) { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } //Safety first! @@ -3068,13 +3025,7 @@ static void Got_Verification(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal verification received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3124,13 +3075,7 @@ static void Got_Removal(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal demotion received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3204,14 +3149,7 @@ static void Got_MotD_f(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal motd change received from %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } - + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); Z_Free(mymotd); return; } @@ -3267,13 +3205,7 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal runsoc command received from %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3430,13 +3362,8 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick) { - UINT8 buf[2]; - CONS_Alert(CONS_WARNING, M_GetText("Illegal addfile command received from %s\n"), player_names[playernum]); - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3485,13 +3412,7 @@ static void Got_Addfilecmd(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal addfile command received from %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -4284,13 +4205,7 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal exitlevel command received from %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 8f857c6db..f258cde62 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -194,6 +194,7 @@ typedef union { // add game commands, needs cleanup void D_RegisterServerCommands(void); void D_RegisterClientCommands(void); +void CleanupPlayerName(INT32 playernum, const char *newname); boolean EnsurePlayerNameIsGood(char *name, INT32 playernum); void D_SendPlayerConfig(void); void Command_ExitGame_f(void); diff --git a/src/d_player.h b/src/d_player.h index 2344c9c85..a4f84d98f 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -326,6 +326,8 @@ typedef struct player_s // bounded/scaled total momentum. fixed_t bob; + angle_t viewrollangle; + // Mouse aiming, where the guy is looking at! // It is updated with cmd->aiming. angle_t aiming; @@ -512,6 +514,7 @@ typedef struct player_s UINT8 bot; tic_t jointime; // Timer when player joins game to change skin/color + tic_t quittime; // Time elapsed since user disconnected, zero if connected #ifdef HWRENDER fixed_t fovadd; // adjust FOV for hw rendering #endif diff --git a/src/dehacked.c b/src/dehacked.c index 85a020936..838d42c9d 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -585,21 +585,30 @@ static void readfreeslots(MYFILE *f) continue; // Copy in the spr2 name and increment free_spr2. if (free_spr2 < NUMPLAYERSPRITES) { - CONS_Printf("Sprite SPR2_%s allocated.\n",word); strncpy(spr2names[free_spr2],word,4); spr2defaults[free_spr2] = 0; spr2names[free_spr2++][4] = 0; } else - CONS_Alert(CONS_WARNING, "Ran out of free SPR2 slots!\n"); + deh_warning("Ran out of free SPR2 slots!\n"); } else if (fastcmp(type, "TOL")) { - if (lastcustomtol > 31) - CONS_Alert(CONS_WARNING, "Ran out of free typeoflevel slots!\n"); + // Search if we already have a typeoflevel by that name... + for (i = 0; TYPEOFLEVEL[i].name; i++) + if (fastcmp(word, TYPEOFLEVEL[i].name)) + break; + + // We found it? Then don't allocate another one. + if (TYPEOFLEVEL[i].name) + continue; + + // We don't, so freeslot it. + if (lastcustomtol == (UINT32)MAXTOL) // Unless you have way too many, since they're flags. + deh_warning("Ran out of free typeoflevel slots!\n"); else { - G_AddTOL((1<typeoflevel = tol; } } + else if (fastcmp(word, "KEYWORDS")) + { + deh_strlcpy(mapheaderinfo[num-1]->keywords, word2, + sizeof(mapheaderinfo[num-1]->keywords), va("Level header %d: keywords", num)); + } else if (fastcmp(word, "MUSIC")) { if (fastcmp(word2, "NONE")) @@ -4941,19 +4921,19 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_PLAY_SUPER_TRANS3", "S_PLAY_SUPER_TRANS4", "S_PLAY_SUPER_TRANS5", - "S_PLAY_SUPER_TRANS6", // This has special significance in the code. If you add more frames, search for it and make the appropriate changes. + "S_PLAY_SUPER_TRANS6", // technically the player goes here but it's an infinite tic state "S_OBJPLACE_DUMMY", - // 1-Up Box Sprites (uses player sprite) + // 1-Up Box Sprites overlay (uses player sprite) "S_PLAY_BOX1", "S_PLAY_BOX2", "S_PLAY_ICON1", "S_PLAY_ICON2", "S_PLAY_ICON3", - // Level end sign (uses player sprite) + // Level end sign overlay (uses player sprite) "S_PLAY_SIGN", // NiGHTS character (uses player sprite) @@ -5201,7 +5181,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_ROBOHOOD_JUMP2", "S_ROBOHOOD_JUMP3", - // CastleBot FaceStabber + // Castlebot Facestabber "S_FACESTABBER_STND1", "S_FACESTABBER_STND2", "S_FACESTABBER_STND3", @@ -5421,6 +5401,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EGGMOBILE_FLEE2", "S_EGGMOBILE_BALL", "S_EGGMOBILE_TARGET", + "S_BOSSEGLZ1", "S_BOSSEGLZ2", @@ -5473,7 +5454,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EGGMOBILE3_FLEE1", "S_EGGMOBILE3_FLEE2", - // Boss 3 pinch + // Boss 3 Pinch "S_FAKEMOBILE_INIT", "S_FAKEMOBILE", "S_FAKEMOBILE_ATK1", @@ -5489,7 +5470,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BOSSSEBH2", // Boss 3 Shockwave - "S_SHOCKWAVE1", "S_SHOCKWAVE2", @@ -5526,9 +5506,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_JETFLAME", // Boss 4 Spectator Eggrobo - "S_EGGROBO1_IDLE", + "S_EGGROBO1_STND", "S_EGGROBO1_BSLAP1", - "S_EGGROBO2_BSLAP2", + "S_EGGROBO1_BSLAP2", "S_EGGROBO1_PISSED", // Boss 4 Spectator Eggrobo jet flame @@ -5772,7 +5752,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_CYBRAKDEMON_NAPALM_ATTACK1", "S_CYBRAKDEMON_NAPALM_ATTACK2", "S_CYBRAKDEMON_NAPALM_ATTACK3", - "S_CYBRAKDEMON_FINISH_ATTACK", // If just attacked, remove MF2_FRET w/out going back to spawnstate + "S_CYBRAKDEMON_FINISH_ATTACK1", // If just attacked, remove MF2_FRET w/out going back to spawnstate "S_CYBRAKDEMON_FINISH_ATTACK2", // Force a delay between attacks so you don't get bombarded with them back-to-back "S_CYBRAKDEMON_PAIN1", "S_CYBRAKDEMON_PAIN2", @@ -6016,6 +5996,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SIGNSTOP", "S_SIGNBOARD", "S_EGGMANSIGN", + "S_CLEARSIGN", // Spike Ball "S_SPIKEBALL1", @@ -6465,7 +6446,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_LITTLETUMBLEWEED_ROLL7", "S_LITTLETUMBLEWEED_ROLL8", - // Cacti Sprites + // Cacti "S_CACTI1", "S_CACTI2", "S_CACTI3", @@ -6480,7 +6461,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_CACTITINYSEG", "S_CACTISMALLSEG", - // Warning signs sprites + // Warning signs "S_ARIDSIGN_CAUTION", "S_ARIDSIGN_CACTI", "S_ARIDSIGN_SHARPTURN", @@ -6497,6 +6478,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_TNTBARREL_EXPL4", "S_TNTBARREL_EXPL5", "S_TNTBARREL_EXPL6", + "S_TNTBARREL_EXPL7", "S_TNTBARREL_FLYING", // TNT proximity shell @@ -7042,7 +7024,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_ZAPSB10", "S_ZAPSB11", // blank frame - // Thunder spark + //Thunder spark "S_THUNDERCOIN_SPARK", // Invincibility Sparkles @@ -7343,6 +7325,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BHORIZ7", "S_BHORIZ8", + // Booster "S_BOOSTERSOUND", "S_YELLOWBOOSTERROLLER", "S_YELLOWBOOSTERSEG_LEFT", @@ -7373,7 +7356,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_SPLISH8", "S_SPLISH9", - // Lava splish + // Lava Splish "S_LAVASPLISH", // added water splash @@ -7969,6 +7952,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_ROCKCRUMBLEN", "S_ROCKCRUMBLEO", "S_ROCKCRUMBLEP", + + // Level debris "S_GFZDEBRIS", "S_BRICKDEBRIS", "S_WOODDEBRIS", @@ -7988,7 +7973,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_THOK", // Thok! mobj "MT_PLAYER", "MT_TAILSOVERLAY", // c: - "MT_METALJETFUME", // [: + "MT_METALJETFUME", // Enemies "MT_BLUECRAWLA", // Crawla (Blue) @@ -8105,7 +8090,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_CYBRAKDEMON_NAPALM_FLAMES", "MT_CYBRAKDEMON_VILE_EXPLOSION", - // Metal Sonic + // Metal Sonic (Boss 9) "MT_METALSONIC_RACE", "MT_METALSONIC_BATTLE", "MT_MSSHIELD_FRONT", @@ -8119,7 +8104,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BOMBSPHERE", "MT_REDTEAMRING", //Rings collectable by red team. "MT_BLUETEAMRING", //Rings collectable by blue team. - "MT_TOKEN", // Special Stage Token + "MT_TOKEN", // Special Stage token for special stage "MT_REDFLAG", // Red CTF Flag "MT_BLUEFLAG", // Blue CTF Flag "MT_EMBLEM", @@ -8345,22 +8330,22 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Arid Canyon Scenery "MT_BIGTUMBLEWEED", "MT_LITTLETUMBLEWEED", - "MT_CACTI1", - "MT_CACTI2", - "MT_CACTI3", - "MT_CACTI4", - "MT_CACTI5", - "MT_CACTI6", - "MT_CACTI7", - "MT_CACTI8", - "MT_CACTI9", - "MT_CACTI10", - "MT_CACTI11", - "MT_CACTITINYSEG", - "MT_CACTISMALLSEG", - "MT_ARIDSIGN_CAUTION", - "MT_ARIDSIGN_CACTI", - "MT_ARIDSIGN_SHARPTURN", + "MT_CACTI1", // Tiny Red Flower Cactus + "MT_CACTI2", // Small Red Flower Cactus + "MT_CACTI3", // Tiny Blue Flower Cactus + "MT_CACTI4", // Small Blue Flower Cactus + "MT_CACTI5", // Prickly Pear + "MT_CACTI6", // Barrel Cactus + "MT_CACTI7", // Tall Barrel Cactus + "MT_CACTI8", // Armed Cactus + "MT_CACTI9", // Ball Cactus + "MT_CACTI10", // Tiny Cactus + "MT_CACTI11", // Small Cactus + "MT_CACTITINYSEG", // Tiny Cactus Segment + "MT_CACTISMALLSEG", // Small Cactus Segment + "MT_ARIDSIGN_CAUTION", // Caution Sign + "MT_ARIDSIGN_CACTI", // Cacti Sign + "MT_ARIDSIGN_SHARPTURN", // Sharp Turn Sign "MT_OILLAMP", "MT_TNTBARREL", "MT_PROXIMITYTNT", @@ -8415,7 +8400,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_GLAREGOYLEUP", "MT_GLAREGOYLEDOWN", "MT_GLAREGOYLELONG", - "MT_TARGET", + "MT_TARGET", // AKA Red Crystal "MT_GREENFLAME", "MT_BLUEGARGOYLE", @@ -8466,7 +8451,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_HHZSTALAGMITE_TALL", "MT_HHZSTALAGMITE_SHORT", - // Botanic Serenity + // Botanic Serenity scenery "MT_BSZTALLFLOWER_RED", "MT_BSZTALLFLOWER_PURPLE", "MT_BSZTALLFLOWER_BLUE", @@ -8746,6 +8731,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_ROCKCRUMBLE14", "MT_ROCKCRUMBLE15", "MT_ROCKCRUMBLE16", + + // Level debris "MT_GFZDEBRIS", "MT_BRICKDEBRIS", "MT_WOODDEBRIS", @@ -8842,7 +8829,7 @@ static const char *const MOBJEFLAG_LIST[] = { #ifdef HAVE_BLUA static const char *const MAPTHINGFLAG_LIST[4] = { - NULL, + "EXTRA", // Extra flag for objects. "OBJECTFLIP", // Reverse gravity flag for objects. "OBJECTSPECIAL", // Special flag used with certain objects. "AMBUSH" // Deaf monsters/do not react to sound. @@ -9141,6 +9128,7 @@ static const char *const HUDITEMS_LIST[] = { "RINGS", "RINGSNUM", + "RINGSNUMTICS", "SCORE", "SCORENUM", @@ -9160,8 +9148,7 @@ static const char *const HUDITEMS_LIST[] = { "TIMELEFTNUM", "TIMEUP", "HUNTPICS", - "POWERUPS", - "LAP" + "POWERUPS" }; static const char *const MENUTYPES_LIST[] = { @@ -9425,7 +9412,7 @@ struct { {"SH_FORCE",SH_FORCE}, {"SH_FORCEHP",SH_FORCEHP}, // to be used as a bitmask only // Mostly for use with Mario mode. - {"SH_FIREFLOWER", SH_FIREFLOWER}, + {"SH_FIREFLOWER",SH_FIREFLOWER}, {"SH_STACK",SH_STACK}, {"SH_NOSTACK",SH_NOSTACK}, @@ -9440,7 +9427,7 @@ struct { {"CR_ROPEHANG",CR_ROPEHANG}, {"CR_MACESPIN",CR_MACESPIN}, {"CR_MINECART",CR_MINECART}, - {"CR_ROLLOUT", CR_ROLLOUT}, + {"CR_ROLLOUT",CR_ROLLOUT}, {"CR_PTERABYTE",CR_PTERABYTE}, // Ring weapons (ringweapons_t) @@ -9607,7 +9594,7 @@ struct { {"NUM_WEAPONS",NUM_WEAPONS}, // Value for infinite lives - {"INFLIVES", INFLIVES}, + {"INFLIVES",INFLIVES}, // Got Flags, for player->gotflag! // Used to be MF_ for some stupid reason, now they're GF_ to stop them looking like mobjflags @@ -9668,10 +9655,11 @@ struct { {"FF_QUICKSAND",FF_QUICKSAND}, ///< Quicksand! {"FF_PLATFORM",FF_PLATFORM}, ///< You can jump up through this to the top. {"FF_REVERSEPLATFORM",FF_REVERSEPLATFORM}, ///< A fall-through floor in normal gravity, a platform in reverse gravity. - {"FF_INTANGABLEFLATS",FF_INTANGABLEFLATS}, ///< Both flats are intangable, but the sides are still solid. + {"FF_INTANGIBLEFLATS",FF_INTANGIBLEFLATS}, ///< Both flats are intangible, but the sides are still solid. + {"FF_INTANGABLEFLATS",FF_INTANGIBLEFLATS}, ///< Both flats are intangable, but the sides are still solid. {"FF_SHATTER",FF_SHATTER}, ///< Used with ::FF_BUSTUP. Bustable on mere touch. {"FF_SPINBUST",FF_SPINBUST}, ///< Used with ::FF_BUSTUP. Also bustable if you're in your spinning frames. - {"FF_STRONGBUST",FF_STRONGBUST }, ///< Used with ::FF_BUSTUP. Only bustable by "strong" characters (Knuckles) and abilities (bouncing, twinspin, melee). + {"FF_STRONGBUST",FF_STRONGBUST}, ///< Used with ::FF_BUSTUP. Only bustable by "strong" characters (Knuckles) and abilities (bouncing, twinspin, melee). {"FF_RIPPLE",FF_RIPPLE}, ///< Ripple the flats {"FF_COLORMAPONLY",FF_COLORMAPONLY}, ///< Only copy the colormap, not the lightlevel {"FF_GOOWATER",FF_GOOWATER}, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop. @@ -10462,16 +10450,23 @@ static inline int lib_freeslot(lua_State *L) } else if (fastcmp(type, "TOL")) { - if (lastcustomtol > 31) - CONS_Alert(CONS_WARNING, "Ran out of free typeoflevel slots!\n"); - else - { - UINT32 newtol = (1< -#include #endif +#include // locale should not be dependent on GETTEXT -- 11/01/20 Monster Iestyn #include #include @@ -143,9 +143,9 @@ extern char logfilename[1024]; // we use comprevision and compbranch instead. #else #define VERSION 202 // Game version -#define SUBVERSION 0 // more precise version number -#define VERSIONSTRING "v2.2.0" -#define VERSIONSTRINGW L"v2.2.0" +#define SUBVERSION 1 // more precise version number +#define VERSIONSTRING "v2.2.1" +#define VERSIONSTRINGW L"v2.2.1" // Hey! If you change this, add 1 to the MODVERSION below! // Otherwise we can't force updates! #endif @@ -210,7 +210,7 @@ extern char logfilename[1024]; // it's only for detection of the version the player is using so the MS can alert them of an update. // Only set it higher, not lower, obviously. // Note that we use this to help keep internal testing in check; this is why v2.2.0 is not version "1". -#define MODVERSION 40 +#define MODVERSION 41 // To version config.cfg, MAJOREXECVERSION is set equal to MODVERSION automatically. // Increment MINOREXECVERSION whenever a config change is needed that does not correspond @@ -454,12 +454,12 @@ char savegamename[256]; // m_misc.h #ifdef GETTEXT #define M_GetText(String) gettext(String) -void M_StartupLocale(void); #else // If no translations are to be used, make a stub // M_GetText function that just returns the string. #define M_GetText(x) (x) #endif +void M_StartupLocale(void); extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL; char *va(const char *format, ...) FUNCPRINTF; char *M_GetToken(const char *inputString); @@ -490,6 +490,7 @@ extern INT32 cv_debug; #define DBG_SETUP 0x0400 #define DBG_LUA 0x0800 #define DBG_RANDOMIZER 0x1000 +#define DBG_VIEWMORPH 0x2000 // ======================= // Misc stuff for later... @@ -546,6 +547,8 @@ INT32 I_GetKey(void); #define PATHSEP "/" #endif +#define PUNCTUATION "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" + // Compile date and time and revision. extern const char *compdate, *comptime, *comprevision, *compbranch; diff --git a/src/doomstat.h b/src/doomstat.h index 7e961677f..cf02e4389 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -289,6 +289,7 @@ typedef struct UINT8 actnum; ///< Act number or 0 for none. UINT32 typeoflevel; ///< Combination of typeoflevel flags. INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end. + char keywords[33]; ///< Keywords separated by space to search for. 32 characters. char musname[7]; ///< Music track to play. "" for no music. UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. UINT32 muspos; ///< Music position to jump to. @@ -436,6 +437,7 @@ extern const char *Gametype_ConstantNames[NUMGAMETYPES]; extern INT32 pointlimits[NUMGAMETYPES]; extern INT32 timelimits[NUMGAMETYPES]; +// TypeOfLevel things enum TypeOfLevel { TOL_SP = 0x01, ///< Single Player @@ -460,16 +462,16 @@ enum TypeOfLevel TOL_XMAS = 0x1000, ///< Christmas NiGHTS }; -#define NUMBASETOL 18 -#define NUMMAXTOL (18 + NUMGAMETYPEFREESLOTS) +#define MAXTOL (1<<31) +#define NUMBASETOLNAMES (19) +#define NUMTOLNAMES (NUMBASETOLNAMES + NUMGAMETYPEFREESLOTS) typedef struct { const char *name; UINT32 flag; } tolinfo_t; -extern tolinfo_t TYPEOFLEVEL[NUMMAXTOL]; -extern INT32 numtolinfo; +extern tolinfo_t TYPEOFLEVEL[NUMTOLNAMES]; extern UINT32 lastcustomtol; extern tic_t totalplaytime; diff --git a/src/f_finale.c b/src/f_finale.c index bbee48bdc..66f963bbb 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1120,6 +1120,9 @@ static const char *credits[] = { "\1Sonic Robo Blast II", "\1Credits", "", + "\1Producer", + "Rob Tisdell", + "", "\1Game Design", "Ben \"Mystic\" Geyer", "\"SSNTails\"", @@ -1165,6 +1168,7 @@ static const char *credits[] = { "Tasos \"tatokis\" Sahanidis", // Corrected C FixedMul, making 64-bit builds netplay compatible "Wessel \"sphere\" Smit", "Ben \"Cue\" Woodford", + "Ikaro \"Tatsuru\" Vinhas", // Git contributors with 5+ approved merges of substantive quality, // or contributors with at least one groundbreaking merge, may be named. // Everyone else is acknowledged under "Special Thanks > SRB2 Community Contributors". @@ -1233,6 +1237,7 @@ static const char *credits[] = { "Thomas \"Shadow Hog\" Igoe", "Alexander \"DrTapeworm\" Moench-Ford", "\"Kaito Sinclaire\"", + "Anna \"QueenDelta\" Sandlin", "Wessel \"sphere\" Smit", "\"Spazzo\"", "\"SSNTails\"", @@ -1256,7 +1261,7 @@ static const char *credits[] = { "Cody \"SRB2 Playah\" Koester", "Skye \"OmegaVelocity\" Meredith", "Stephen \"HEDGESMFG\" Moellering", - "Nick \"ST218\" Molina", + "Rosalie \"ST218\" Molina", "Samuel \"Prime 2.0\" Peters", "Colin \"Sonict\" Pfaff", "Bill \"Tets\" Reed", @@ -2693,8 +2698,18 @@ static void F_FigureActiveTtScale(void) SINT8 newttscale = max(1, min(6, vid.dupx)); SINT8 oldttscale = activettscale; - if (newttscale == testttscale) - return; + if (needpatchrecache) + ttloaded[0] = ttloaded[1] = ttloaded[2] = ttloaded[3] = ttloaded[4] = ttloaded[5] = 0; + else + { + if (newttscale == testttscale) + return; + + // We have a new ttscale, so load gfx + if(oldttscale > 0) + F_UnloadAlacroixGraphics(oldttscale); + } + testttscale = newttscale; // If ttscale is unavailable: look for lower scales, then higher scales. @@ -2712,10 +2727,6 @@ static void F_FigureActiveTtScale(void) activettscale = (newttscale >= 1 && newttscale <= 6) ? newttscale : 0; - // We have a new ttscale, so load gfx - if(oldttscale > 0) - F_UnloadAlacroixGraphics(oldttscale); - if(activettscale > 0) F_LoadAlacroixGraphics(activettscale); } @@ -2757,12 +2768,6 @@ void F_TitleScreenDrawer(void) return; #endif - if (needpatchrecache && (curttmode == TTMODE_ALACROIX)) - { - ttloaded[0] = ttloaded[1] = ttloaded[2] = ttloaded[3] = ttloaded[4] = ttloaded[5] = 0; - F_LoadAlacroixGraphics(activettscale); - } - switch(curttmode) { case TTMODE_OLD: @@ -3628,7 +3633,6 @@ void F_StartContinue(void) } wipestyleflags = WSF_FADEOUT; - F_TryColormapFade(31); G_SetGamestate(GS_CONTINUING); gameaction = ga_nothing; diff --git a/src/g_game.c b/src/g_game.c index f7778df8f..68f548fbf 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -352,8 +352,8 @@ static CV_PossibleValue_t chattime_cons_t[] = {{5, "MIN"}, {999, "MAX"}, {0, NUL consvar_t cv_chattime = {"chattime", "8", CV_SAVE, chattime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // chatwidth -static CV_PossibleValue_t chatwidth_cons_t[] = {{64, "MIN"}, {150, "MAX"}, {0, NULL}}; -consvar_t cv_chatwidth = {"chatwidth", "128", CV_SAVE, chatwidth_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t chatwidth_cons_t[] = {{64, "MIN"}, {300, "MAX"}, {0, NULL}}; +consvar_t cv_chatwidth = {"chatwidth", "150", CV_SAVE, chatwidth_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // chatheight static CV_PossibleValue_t chatheight_cons_t[] = {{6, "MIN"}, {22, "MAX"}, {0, NULL}}; @@ -1140,7 +1140,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) INT32 *myaiming = (ssplayer == 1 ? &localaiming : &localaiming2); angle_t drawangleoffset = (player->powers[pw_carry] == CR_ROLLOUT) ? ANGLE_180 : 0; - INT32 chasecam, chasefreelook, alwaysfreelook, usejoystick, invertmouse, mousemove; + INT32 chasecam, chasefreelook, alwaysfreelook, usejoystick, invertmouse, turnmultiplier, mousemove; controlstyle_e controlstyle = G_ControlStyle(ssplayer); INT32 *mx; INT32 *my; INT32 *mly; @@ -1163,6 +1163,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) alwaysfreelook = cv_alwaysfreelook.value; usejoystick = cv_usejoystick.value; invertmouse = cv_invertmouse.value; + turnmultiplier = cv_cam_turnmultiplier.value; mousemove = cv_mousemove.value; mx = &mousex; my = &mousey; @@ -1176,6 +1177,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) alwaysfreelook = cv_alwaysfreelook2.value; usejoystick = cv_usejoystick2.value; invertmouse = cv_invertmouse2.value; + turnmultiplier = cv_cam2_turnmultiplier.value; mousemove = cv_mousemove2.value; mx = &mouse2x; my = &mouse2y; @@ -1293,14 +1295,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { if (turnright && turnleft); else if (turnright) - cmd->angleturn = (INT16)(cmd->angleturn - ((angleturn[tspeed] * cv_cam_turnmultiplier.value)>>FRACBITS)); + cmd->angleturn = (INT16)(cmd->angleturn - ((angleturn[tspeed] * turnmultiplier)>>FRACBITS)); else if (turnleft) - cmd->angleturn = (INT16)(cmd->angleturn + ((angleturn[tspeed] * cv_cam_turnmultiplier.value)>>FRACBITS)); + cmd->angleturn = (INT16)(cmd->angleturn + ((angleturn[tspeed] * turnmultiplier)>>FRACBITS)); if (analogjoystickmove && lookjoystickvector.xaxis != 0) { // JOYAXISRANGE should be 1023 (divide by 1024) - cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 10) * cv_cam_turnmultiplier.value)>>FRACBITS)); // ANALOG! + cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 10) * turnmultiplier)>>FRACBITS)); // ANALOG! } if (turnright || turnleft || abs(cmd->angleturn) > angleturn[2]) @@ -1939,6 +1941,10 @@ boolean G_IsTitleCardAvailable(void) if (gametyperules & GTR_NOTITLECARD) return false; + // The current level has no name. + if (!mapheaderinfo[gamemap-1]->lvlttl[0]) + return false; + // The title card is available. return true; } @@ -2412,6 +2418,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT32 skin; UINT32 availabilities; tic_t jointime; + tic_t quittime; boolean spectator; boolean outofcoop; INT16 bot; @@ -2425,6 +2432,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) ctfteam = players[player].ctfteam; exiting = players[player].exiting; jointime = players[player].jointime; + quittime = players[player].quittime; spectator = players[player].spectator; outofcoop = players[player].outofcoop; pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER)); @@ -2496,6 +2504,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->pflags = pflags; p->ctfteam = ctfteam; p->jointime = jointime; + p->quittime = quittime; p->spectator = spectator; p->outofcoop = outofcoop; @@ -2649,73 +2658,24 @@ static boolean G_CheckSpot(INT32 playernum, mapthing_t *mthing) // or a not-so-appropriate spot, if it initially fails // due to a lack of starts open or something. // -void G_SpawnPlayer(INT32 playernum, boolean starpost) +void G_SpawnPlayer(INT32 playernum) { - mapthing_t *spawnpoint; - if (!playeringame[playernum]) return; P_SpawnPlayer(playernum); - - if (starpost) //Don't even bother with looking for a place to spawn. - { - P_MovePlayerToStarpost(playernum); -#ifdef HAVE_BLUA - LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) -#endif - return; - } - - // -- CTF -- - // Order: CTF->DM->Coop - if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam) - { - if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start - && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start - spawnpoint = G_FindCoopStart(playernum); // fallback - } - - // -- DM/Tag/CTF-spectator/etc -- - // Order: DM->CTF->Coop - else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT)) - { - if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start - && !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start - spawnpoint = G_FindCoopStart(playernum); // fallback - } - - // -- Other game modes -- - // Order: Coop->DM->CTF - else - { - if (!(spawnpoint = G_FindCoopStart(playernum)) // find a Co-op start - && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start - spawnpoint = G_FindCTFStart(playernum); // fallback - } - - //No spawns found. ANYWHERE. - if (!spawnpoint) - { - if (nummapthings) - { - if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)) - CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the first mapthing!\n")); - spawnpoint = &mapthings[0]; - } - else - { - if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)) - CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the origin!\n")); - //P_MovePlayerToSpawn handles this fine if the spawnpoint is NULL. - } - } - P_MovePlayerToSpawn(playernum, spawnpoint); - + G_MovePlayerToSpawnOrStarpost(playernum); #ifdef HAVE_BLUA LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) #endif +} +void G_MovePlayerToSpawnOrStarpost(INT32 playernum) +{ + if (players[playernum].starposttime) + P_MovePlayerToStarpost(playernum); + else + P_MovePlayerToSpawn(playernum, G_FindMapStart(playernum)); } mapthing_t *G_FindCTFStart(INT32 playernum) @@ -2812,6 +2772,59 @@ mapthing_t *G_FindCoopStart(INT32 playernum) return NULL; } +mapthing_t *G_FindMapStart(INT32 playernum) +{ + mapthing_t *spawnpoint; + + if (!playeringame[playernum]) + return NULL; + + // -- CTF -- + // Order: CTF->DM->Coop + if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam) + { + if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start + && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start + spawnpoint = G_FindCoopStart(playernum); // fallback + } + + // -- DM/Tag/CTF-spectator/etc -- + // Order: DM->CTF->Coop + else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT)) + { + if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start + && !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start + spawnpoint = G_FindCoopStart(playernum); // fallback + } + + // -- Other game modes -- + // Order: Coop->DM->CTF + else + { + if (!(spawnpoint = G_FindCoopStart(playernum)) // find a Co-op start + && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start + spawnpoint = G_FindCTFStart(playernum); // fallback + } + + //No spawns found. ANYWHERE. + if (!spawnpoint) + { + if (nummapthings) + { + if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)) + CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the first mapthing!\n")); + spawnpoint = &mapthings[0]; + } + else + { + if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)) + CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the origin!\n")); + } + } + + return spawnpoint; +} + // Go back through all the projectiles and remove all references to the old // player mobj, replacing them with the new one. void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo) @@ -2990,7 +3003,7 @@ void G_DoReborn(INT32 playernum) { if (!playeringame[i]) continue; - G_SpawnPlayer(i, (players[i].starposttime)); + G_SpawnPlayer(i); } // restore time in netgame (see also p_setup.c) @@ -3036,7 +3049,7 @@ void G_DoReborn(INT32 playernum) P_RemoveMobj(player->mo); } - G_SpawnPlayer(playernum, (player->starposttime)); + G_SpawnPlayer(playernum); if (oldmo) G_ChangePlayerReferences(oldmo, players[playernum].mo); } @@ -3078,7 +3091,6 @@ void G_AddPlayer(INT32 playernum) } } - p->jointime = 0; p->playerstate = PST_REBORN; p->height = mobjinfo[MT_PLAYER].height; @@ -3101,6 +3113,8 @@ boolean G_EnoughPlayersFinished(void) { if (!playeringame[i] || players[i].spectator || players[i].bot) continue; + if (players[i].quittime > 30 * TICRATE) + continue; if (players[i].lives <= 0) continue; @@ -3189,17 +3203,17 @@ UINT32 gametypedefaultrules[NUMGAMETYPES] = GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT, // Match - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD|GTR_DEATHPENALTY, + GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD|GTR_DEATHPENALTY, // Team Match - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD, + GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD, // Tag - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY, + GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY, // Hide and Seek - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY, + GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY, // CTF - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD, + GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD, }; // @@ -3242,8 +3256,8 @@ void G_AddGametypeConstant(INT16 gtype, const char *newgtconst) { size_t r = 0; // read size_t w = 0; // write - char *gtconst = Z_Calloc(strlen(newgtconst) + 3, PU_STATIC, NULL); - char *tmpconst = Z_Calloc(strlen(newgtconst), PU_STATIC, NULL); + char *gtconst = Z_Calloc(strlen(newgtconst) + 4, PU_STATIC, NULL); + char *tmpconst = Z_Calloc(strlen(newgtconst) + 1, PU_STATIC, NULL); // Copy the gametype name. strcpy(tmpconst, newgtconst); @@ -3370,6 +3384,36 @@ UINT32 gametypetol[NUMGAMETYPES] = TOL_CTF, // CTF }; +tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = { + {"SOLO",TOL_SP}, + {"SP",TOL_SP}, + {"SINGLEPLAYER",TOL_SP}, + {"SINGLE",TOL_SP}, + + {"COOP",TOL_COOP}, + {"CO-OP",TOL_COOP}, + + {"COMPETITION",TOL_COMPETITION}, + {"RACE",TOL_RACE}, + + {"MATCH",TOL_MATCH}, + {"TAG",TOL_TAG}, + {"CTF",TOL_CTF}, + + {"2D",TOL_2D}, + {"MARIO",TOL_MARIO}, + {"NIGHTS",TOL_NIGHTS}, + {"OLDBRAK",TOL_ERZ3}, + + {"XMAS",TOL_XMAS}, + {"CHRISTMAS",TOL_XMAS}, + {"WINTER",TOL_XMAS}, + + {NULL, 0} +}; + +UINT32 lastcustomtol = (TOL_XMAS<<1); + // // G_AddTOL // @@ -3377,16 +3421,16 @@ UINT32 gametypetol[NUMGAMETYPES] = // void G_AddTOL(UINT32 newtol, const char *tolname) { - TYPEOFLEVEL[numtolinfo].name = Z_StrDup(tolname); - TYPEOFLEVEL[numtolinfo].flag = newtol; - numtolinfo++; + INT32 i; + for (i = 0; TYPEOFLEVEL[i].name; i++) + ; - TYPEOFLEVEL[numtolinfo].name = NULL; - TYPEOFLEVEL[numtolinfo].flag = 0; + TYPEOFLEVEL[i].name = Z_StrDup(tolname); + TYPEOFLEVEL[i].flag = newtol; } // -// G_AddTOL +// G_AddGametypeTOL // // Assigns a type of level to a gametype. // @@ -3725,7 +3769,10 @@ static void G_DoCompleted(void) } if (i == 7) + { gottoken = false; + token = 0; + } } if (spec && !gottoken) @@ -4761,6 +4808,9 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep, measurekeywords(&freq[freqc], &freq[freqc].matchd, &freq[freqc].matchc, realmapname, mapname, wanttable); + measurekeywords(&freq[freqc], + &freq[freqc].keywhd, &freq[freqc].keywhc, + mapheaderinfo[i]->keywords, mapname, wanttable); if (freq[freqc].total) freqc++; } diff --git a/src/g_game.h b/src/g_game.h index a589a8917..a4afac163 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -161,7 +161,9 @@ INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep); mapthing_t *G_FindCTFStart(INT32 playernum); mapthing_t *G_FindMatchStart(INT32 playernum); mapthing_t *G_FindCoopStart(INT32 playernum); -void G_SpawnPlayer(INT32 playernum, boolean starpost); +mapthing_t *G_FindMapStart(INT32 playernum); +void G_MovePlayerToSpawnOrStarpost(INT32 playernum); +void G_SpawnPlayer(INT32 playernum); // Can be called by the startup code or M_Responder. // A normal game starts at map 1, but a warp test can start elsewhere diff --git a/src/g_input.c b/src/g_input.c index ac901703f..ed7bc5cb6 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -662,7 +662,13 @@ INT32 G_KeyStringtoNum(const char *keystr) return keystr[0]; if (!strncmp(keystr, "KEY", 3) && keystr[3] >= '0' && keystr[3] <= '9') - return atoi(&keystr[3]); + { + /* what if we out of range bruh? */ + j = atoi(&keystr[3]); + if (j < NUMINPUTS) + return j; + return 0; + } for (j = 0; j < NUMKEYNAMES; j++) if (!stricmp(keynames[j].name, keystr)) diff --git a/src/hardware/hw_clip.c b/src/hardware/hw_clip.c index 4bdc753ec..a63527083 100644 --- a/src/hardware/hw_clip.c +++ b/src/hardware/hw_clip.c @@ -72,6 +72,7 @@ #include "../v_video.h" #include "hw_clip.h" #include "hw_glob.h" +#include "../r_main.h" #include "../r_state.h" #include "../tables.h" #include "r_opengl/r_opengl.h" @@ -328,7 +329,7 @@ angle_t gld_FrustumAngle(void) // NEWCLIP TODO: SRB2CBTODO: make a global render_fov for this function - float render_fov = FIXED_TO_FLOAT(cv_grfov.value); + float render_fov = FIXED_TO_FLOAT(cv_fov.value); float render_fovratio = (float)BASEVIDWIDTH / (float)BASEVIDHEIGHT; // SRB2CBTODO: NEWCLIPTODO: Is this right? float render_multiplier = 64.0f / render_fovratio / RMUL; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 3da803ef6..efed9b602 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -41,6 +41,7 @@ #include "../i_system.h" #include "../m_cheat.h" #include "../f_finale.h" +#include "../r_things.h" // R_GetShadowZ #ifdef ESLOPE #include "../p_slopes.h" #endif @@ -4050,39 +4051,6 @@ static gr_vissprite_t *HWR_NewVisSprite(void) return HWR_GetVisSprite(gr_visspritecount++); } -#ifdef GLBADSHADOWS -// 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; -} -#endif //#ifdef GLBADSHADOWS - // // HWR_DoCulling // Hardware version of R_DoCulling @@ -4123,180 +4091,123 @@ static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float v return false; } -#ifdef GLBADSHADOWS -static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float this_scale) +static void HWR_DrawDropShadow(mobj_t *thing, gr_vissprite_t *spr, 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 = 255; + 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) - { - angle_t shadowdir; + INT32 light; + fixed_t scalemul; + UINT16 alpha; + fixed_t floordiff; + fixed_t floorz; + fixed_t slopez; + pslope_t *floorslope; - // Set direction - if (splitscreen && stplyr == &players[secondarydisplayplayer]) - shadowdir = localangle2 + FixedAngle(cv_cam2_rotate.value); - else - shadowdir = localangle + FixedAngle(cv_cam_rotate.value); + floorz = R_GetShadowZ(thing, &floorslope); - // 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); + //if (abs(floorz - gr_viewz) / tz > 4) return; // Prevent stretchy shadows and possible crashes - // 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; - } + floordiff = abs(thing->z - floorz); - floorheight = FixedInt(spr->mobj->z - floorheight); + alpha = floordiff / (4*FRACUNIT) + 75; + if (alpha >= 255) return; + alpha = 255 - alpha; - offset = floorheight; - } - else - floorheight = FixedInt(spr->mobj->z - mobjfloor); + gpatch = (GLPatch_t *)W_CachePatchName("DSHADOW", PU_CACHE); + 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 (thing && fabsf(fscale - 1.0f) > 1.0E-36f) + offset = (gpatch->height/2) * fscale; + else + offset = (float)(gpatch->height/2); - if (spr->mobj && fabsf(this_scale - 1.0f) > 1.0E-36f) + shadowVerts[0].x = shadowVerts[3].x = fx - offset; + shadowVerts[2].x = shadowVerts[1].x = fx + offset; + shadowVerts[0].z = shadowVerts[1].z = fy - offset; + shadowVerts[3].z = shadowVerts[2].z = fy + offset; + + if (floorslope) { - // 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; + for (i = 0; i < 4; i++) + { + 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 { - // 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); - - // 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; - } - - // We also need to move the bottom ones away when shadowoffs is on - if (cv_shadowoffs.value) - { - 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; + for (i = 0; i < 4; i++) + shadowVerts[i].y = FIXED_TO_FLOAT(floorz) + 0.05f; } if (spr->flip) { - swallVerts[0].sow = swallVerts[3].sow = gpatch->max_s; - swallVerts[2].sow = swallVerts[1].sow = 0; + shadowVerts[0].sow = shadowVerts[3].sow = gpatch->max_s; + shadowVerts[2].sow = shadowVerts[1].sow = 0; } else { - swallVerts[0].sow = swallVerts[3].sow = 0; - swallVerts[2].sow = swallVerts[1].sow = gpatch->max_s; + shadowVerts[0].sow = shadowVerts[3].sow = 0; + shadowVerts[2].sow = shadowVerts[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; + shadowVerts[3].tow = shadowVerts[2].tow = gpatch->max_t; + shadowVerts[0].tow = shadowVerts[1].tow = 0; } else { - swallVerts[3].tow = swallVerts[2].tow = 0; - swallVerts[0].tow = swallVerts[1].tow = gpatch->max_t; + shadowVerts[3].tow = shadowVerts[2].tow = 0; + shadowVerts[0].tow = shadowVerts[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) + if (thing->subsector->sector->numlights) { - sector_t *sector = spr->mobj->subsector->sector; - UINT8 lightlevel = 255; - extracolormap_t *colormap = sector->extra_colormap; + light = R_GetPlaneLight(thing->subsector->sector, floorz, false); // Always use the light at the top instead of whatever I was doing before - if (sector->numlights) - { - INT32 light = R_GetPlaneLight(sector, spr->mobj->floorz, false); + lightlevel = *thing->subsector->sector->lightlist[light].lightlevel; - if (!(spr->mobj->frame & FF_FULLBRIGHT)) - lightlevel = *sector->lightlist[light].lightlevel; - - if (*sector->lightlist[light].extra_colormap) - colormap = *sector->lightlist[light].extra_colormap; - } - 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! + if (*thing->subsector->sector->lightlist[light].extra_colormap) + colormap = *thing->subsector->sector->lightlist[light].extra_colormap; } 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); + lightlevel = thing->subsector->sector->lightlevel; + + 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); } -#endif //#ifdef GLBADSHADOWS // This is expecting a pointer to an array containing 4 wallVerts for a sprite static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts) @@ -4372,24 +4283,6 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) //Hurdler: 25/04/2000: now support colormap in hardware mode HWR_GetMappedPatch(gpatch, spr->colormap); -#ifdef GLBADSHADOWS - // 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); - } -#endif //#ifdef GLBADSHADOWS - baseWallVerts[0].x = baseWallVerts[3].x = spr->x1; baseWallVerts[2].x = baseWallVerts[1].x = spr->x2; baseWallVerts[0].z = baseWallVerts[3].z = spr->z1; @@ -4776,24 +4669,6 @@ static void HWR_DrawSprite(gr_vissprite_t *spr) //Hurdler: 25/04/2000: now support colormap in hardware mode HWR_GetMappedPatch(gpatch, spr->colormap); -#ifdef GLBADSHADOWS - // 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); - } -#endif //#ifdef GLBADSHADOWS - // if it has a dispoffset, push it a little towards the camera if (spr->dispoffset) { float co = -gr_viewcos*(0.05f*spr->dispoffset); @@ -5147,12 +5022,7 @@ void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boo planeinfo[numplanes].isceiling = isceiling; planeinfo[numplanes].fixedheight = fixedheight; - - if (planecolormap && (planecolormap->fog & 1)) - planeinfo[numplanes].lightlevel = lightlevel; - else - planeinfo[numplanes].lightlevel = 255; - + planeinfo[numplanes].lightlevel = (planecolormap && (planecolormap->flags & CMF_FOG)) ? lightlevel : 255; planeinfo[numplanes].levelflat = levelflat; planeinfo[numplanes].xsub = xsub; planeinfo[numplanes].alpha = alpha; @@ -5184,12 +5054,7 @@ void HWR_AddTransparentPolyobjectFloor(levelflat_t *levelflat, polyobj_t *polyse polyplaneinfo[numpolyplanes].isceiling = isceiling; polyplaneinfo[numpolyplanes].fixedheight = fixedheight; - - if (planecolormap && (planecolormap->fog & 1)) - polyplaneinfo[numpolyplanes].lightlevel = lightlevel; - else - polyplaneinfo[numpolyplanes].lightlevel = 255; - + polyplaneinfo[numpolyplanes].lightlevel = (planecolormap && (planecolormap->flags & CMF_FOG)) ? lightlevel : 255; polyplaneinfo[numpolyplanes].levelflat = levelflat; polyplaneinfo[numpolyplanes].polysector = polysector; polyplaneinfo[numpolyplanes].alpha = alpha; @@ -5407,6 +5272,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, spr->mobj->shadowscale); + } + if (spr->mobj && spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) { if (!cv_grmodels.value || md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound || md2_playermodels[(skin_t*)spr->mobj->skin-skins].scale < 0.0f) @@ -5427,6 +5298,7 @@ static void HWR_DrawSprites(void) HWR_DrawSprite(spr); } } + } } } } @@ -5443,7 +5315,7 @@ static void HWR_AddSprites(sector_t *sec) #ifdef HWPRECIP precipmobj_t *precipthing; #endif - fixed_t approx_dist, limit_dist, hoop_limit_dist; + fixed_t limit_dist, hoop_limit_dist; // BSP is traversed by subsector. // A sector might have been split into several @@ -5462,35 +5334,10 @@ static void HWR_AddSprites(sector_t *sec) // If a limit exists, handle things a tiny bit different. limit_dist = (fixed_t)(cv_drawdist.value) << FRACBITS; hoop_limit_dist = (fixed_t)(cv_drawdist_nights.value) << FRACBITS; - if (limit_dist || hoop_limit_dist) + for (thing = sec->thinglist; thing; thing = thing->snext) { - for (thing = sec->thinglist; thing; thing = thing->snext) - { - if (thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW) - continue; - - approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y); - - if (thing->sprite == SPR_HOOP) - { - if (hoop_limit_dist && approx_dist > hoop_limit_dist) - continue; - } - else - { - if (limit_dist && approx_dist > limit_dist) - continue; - } - + if (R_ThingVisibleWithinDist(thing, limit_dist, hoop_limit_dist)) HWR_ProjectSprite(thing); - } - } - else - { - // Draw everything in sector, no checks - for (thing = sec->thinglist; thing; thing = thing->snext) - if (!(thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW)) - HWR_ProjectSprite(thing); } #ifdef HWPRECIP @@ -5499,15 +5346,8 @@ static void HWR_AddSprites(sector_t *sec) { for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) { - if (precipthing->precipflags & PCF_INVISIBLE) - continue; - - approx_dist = P_AproxDistance(viewx-precipthing->x, viewy-precipthing->y); - - if (approx_dist > limit_dist) - continue; - - HWR_ProjectPrecipitationSprite(precipthing); + if (R_PrecipThingVisible(precipthing, limit_dist)) + HWR_ProjectPrecipitationSprite(precipthing); } } #endif @@ -5530,6 +5370,7 @@ static void HWR_ProjectSprite(mobj_t *thing) spritedef_t *sprdef; spriteframe_t *sprframe; spriteinfo_t *sprinfo; + md2_t *md2; size_t lumpoff; unsigned rot; UINT16 flip; @@ -5561,8 +5402,21 @@ static void HWR_ProjectSprite(mobj_t *thing) tz = (tr_x * gr_viewcos) + (tr_y * gr_viewsin); // thing is behind view plane? - if (tz < ZCLIP_PLANE && !papersprite && (!cv_grmodels.value || md2_models[thing->sprite].notfound == true)) //Yellow: Only MD2's dont disappear - return; + if (tz < ZCLIP_PLANE && !papersprite) + { + if (cv_grmodels.value) //Yellow: Only MD2's dont disappear + { + if (thing->skin && thing->sprite == SPR_PLAY) + md2 = &md2_playermodels[( (skin_t *)thing->skin - skins )]; + else + md2 = &md2_models[thing->sprite]; + + if (md2->notfound || md2->scale < 0.0f) + return; + } + else + return; + } // The above can stay as it works for cutting sprites that are too close tr_x = FIXED_TO_FLOAT(thing->x); @@ -5747,7 +5601,7 @@ static void HWR_ProjectSprite(mobj_t *thing) if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) { // bodge support - not nearly as comprehensive as r_things.c, but better than nothing - if (thing->tracer->sprite == SPR_NULL || thing->tracer->flags2 & MF2_DONTDRAW) + if (! R_ThingVisible(thing->tracer)) return; } @@ -5930,7 +5784,7 @@ static void HWR_DrawSkyBackground(player_t *player) if (cv_grskydome.value) { FTransform dometransform; - const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd); + const float fpov = FIXED_TO_FLOAT(cv_fov.value+player->fovadd); postimg_t *type; if (splitscreen && player == &players[secondarydisplayplayer]) @@ -5955,6 +5809,12 @@ static void HWR_DrawSkyBackground(player_t *player) dometransform.scalez = 1; dometransform.fovxangle = fpov; // Tails dometransform.fovyangle = fpov; // Tails + if (player->viewrollangle != 0) + { + fixed_t rol = AngleFixed(player->viewrollangle); + dometransform.rollangle = FIXED_TO_FLOAT(rol); + dometransform.roll = true; + } dometransform.splitscreen = splitscreen; HWR_GetTexture(texturetranslation[skytexture]); @@ -6108,7 +5968,7 @@ void HWR_SetViewSize(void) // ========================================================================== void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player) { - const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd); + const float fpov = FIXED_TO_FLOAT(cv_fov.value+player->fovadd); postimg_t *type; if (splitscreen && player == &players[secondarydisplayplayer]) @@ -6178,6 +6038,12 @@ void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player) atransform.scalez = 1; atransform.fovxangle = fpov; // Tails atransform.fovyangle = fpov; // Tails + if (player->viewrollangle != 0) + { + fixed_t rol = AngleFixed(player->viewrollangle); + atransform.rollangle = FIXED_TO_FLOAT(rol); + atransform.roll = true; + } atransform.splitscreen = splitscreen; gr_fovlud = (float)(1.0l/tan((double)(fpov*M_PIl/360l))); @@ -6234,7 +6100,7 @@ if (0) viewangle = localaiming2; // Handle stuff when you are looking farther up or down. - if ((aimingangle || cv_grfov.value+player->fovadd > 90*FRACUNIT)) + if ((aimingangle || cv_fov.value+player->fovadd > 90*FRACUNIT)) { dup_viewangle += ANGLE_90; HWR_ClearClipSegs(); @@ -6312,7 +6178,7 @@ if (0) // ========================================================================== void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) { - const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd); + const float fpov = FIXED_TO_FLOAT(cv_fov.value+player->fovadd); postimg_t *type; const boolean skybox = (skyboxmo[0] && cv_skybox.value); // True if there's a skybox object and skyboxes are on @@ -6398,6 +6264,12 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) atransform.scalez = 1; atransform.fovxangle = fpov; // Tails atransform.fovyangle = fpov; // Tails + if (player->viewrollangle != 0) + { + fixed_t rol = AngleFixed(player->viewrollangle); + atransform.rollangle = FIXED_TO_FLOAT(rol); + atransform.roll = true; + } atransform.splitscreen = splitscreen; gr_fovlud = (float)(1.0l/tan((double)(fpov*M_PIl/360l))); @@ -6454,7 +6326,7 @@ if (0) viewangle = localaiming2; // Handle stuff when you are looking farther up or down. - if ((aimingangle || cv_grfov.value+player->fovadd > 90*FRACUNIT)) + if ((aimingangle || cv_fov.value+player->fovadd > 90*FRACUNIT)) { dup_viewangle += ANGLE_90; HWR_ClearClipSegs(); @@ -6583,9 +6455,7 @@ static void CV_grmodellighting_OnChange(void); static void CV_grfiltermode_OnChange(void); static void CV_granisotropic_OnChange(void); static void CV_grfogdensity_OnChange(void); -static void CV_grfov_OnChange(void); -static CV_PossibleValue_t grfov_cons_t[] = {{0, "MIN"}, {179*FRACUNIT, "MAX"}, {0, NULL}}; static CV_PossibleValue_t grfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSAMPLED, "Nearest"}, {HWD_SET_TEXTUREFILTER_BILINEAR, "Bilinear"}, {HWD_SET_TEXTUREFILTER_TRILINEAR, "Trilinear"}, {HWD_SET_TEXTUREFILTER_MIXED1, "Linear_Nearest"}, @@ -6594,7 +6464,7 @@ static CV_PossibleValue_t grfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSA {0, NULL}}; CV_PossibleValue_t granisotropicmode_cons_t[] = {{1, "MIN"}, {16, "MAX"}, {0, NULL}}; -consvar_t cv_grfovchange = {"gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_fovchange = {"gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grfog = {"gr_fog", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grfogcolor = {"gr_fogcolor", "AAAAAA", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grsoftwarefog = {"gr_softwarefog", "Off", CV_SAVE, grsoftwarefog_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -6615,7 +6485,6 @@ consvar_t cv_grskydome = {"gr_skydome", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, consvar_t cv_grfakecontrast = {"gr_fakecontrast", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grrounddown = {"gr_rounddown", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_grfov = {"gr_fov", "90", CV_FLOAT|CV_CALL, grfov_cons_t, CV_grfov_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grfogdensity = {"gr_fogdensity", "150", CV_CALL|CV_NOINIT, CV_Unsigned, CV_grfogdensity_OnChange, 0, NULL, NULL, 0, 0, NULL}; @@ -6651,17 +6520,10 @@ static void CV_granisotropic_OnChange(void) HWD.pfnSetSpecialState(HWD_SET_TEXTUREANISOTROPICMODE, cv_granisotropicmode.value); } -static void CV_grfov_OnChange(void) -{ - if ((netgame || multiplayer) && !cv_debug && cv_grfov.value != 90*FRACUNIT) - CV_Set(&cv_grfov, cv_grfov.defaultvalue); -} - //added by Hurdler: console varibale that are saved void HWR_AddCommands(void) { - CV_RegisterVar(&cv_grfovchange); - CV_RegisterVar(&cv_grfov); + CV_RegisterVar(&cv_fovchange); CV_RegisterVar(&cv_grfogdensity); CV_RegisterVar(&cv_grfogcolor); diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 20ff3ad9f..ec5362194 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -84,7 +84,6 @@ extern consvar_t cv_grstaticlighting; extern consvar_t cv_grcoronas; extern consvar_t cv_grcoronasize; #endif -extern consvar_t cv_grfov; extern consvar_t cv_grmodels; extern consvar_t cv_grmodelinterpolation; extern consvar_t cv_grmodellighting; @@ -95,7 +94,7 @@ extern consvar_t cv_grsoftwarefog; extern consvar_t cv_grfiltermode; extern consvar_t cv_granisotropicmode; extern consvar_t cv_grcorrecttricks; -extern consvar_t cv_grfovchange; +extern consvar_t cv_fovchange; extern consvar_t cv_grsolvetjoin; extern consvar_t cv_grspritebillboarding; extern consvar_t cv_grskydome; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index a107a6d05..7c49beb52 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -476,8 +476,9 @@ void HWR_InitModels(void) size_t i; INT32 s; FILE *f; - char name[18], filename[32]; + char name[24], filename[32]; float scale, offset; + size_t prefixlen; CONS_Printf("HWR_InitModels()...\n"); for (s = 0; s < MAXSKINS; s++) @@ -509,46 +510,54 @@ void HWR_InitModels(void) nomd2s = true; return; } - while (fscanf(f, "%19s %31s %f %f", name, filename, &scale, &offset) == 4) + + // length of the player model prefix + prefixlen = strlen(PLAYERMODELPREFIX); + + while (fscanf(f, "%25s %31s %f %f", name, filename, &scale, &offset) == 4) { - if (stricmp(name, "PLAY") == 0) + char *skinname = name; + size_t len = strlen(name); + + // check for the player model prefix. + if (!strnicmp(name, PLAYERMODELPREFIX, prefixlen) && (len > prefixlen)) { - CONS_Printf("Model for sprite PLAY detected in models.dat, use a player skin instead!\n"); - continue; + skinname += prefixlen; + goto addskinmodel; } - for (i = 0; i < NUMSPRITES; i++) + // add sprite model + if (len == 4) // must be 4 characters long exactly. otherwise it's not a sprite name. { - if (stricmp(name, sprnames[i]) == 0) + for (i = 0; i < NUMSPRITES; i++) { - //if (stricmp(name, "PLAY") == 0) - //continue; - - //CONS_Debug(DBG_RENDER, " Found: %s %s %f %f\n", name, filename, scale, offset); - md2_models[i].scale = scale; - md2_models[i].offset = offset; - md2_models[i].notfound = false; - strcpy(md2_models[i].filename, filename); - goto md2found; + if (stricmp(name, sprnames[i]) == 0) + { + md2_models[i].scale = scale; + md2_models[i].offset = offset; + md2_models[i].notfound = false; + strcpy(md2_models[i].filename, filename); + goto modelfound; + } } } +addskinmodel: + // add player model for (s = 0; s < MAXSKINS; s++) { - if (stricmp(name, skins[s].name) == 0) + if (stricmp(skinname, skins[s].name) == 0) { - //CONS_Printf(" Found: %s %s %f %f\n", name, filename, scale, offset); md2_playermodels[s].skin = s; md2_playermodels[s].scale = scale; md2_playermodels[s].offset = offset; md2_playermodels[s].notfound = false; strcpy(md2_playermodels[s].filename, filename); - goto md2found; + goto modelfound; } } - // no sprite/player skin name found?!? - //CONS_Printf("Unknown sprite/player skin %s detected in models.dat\n", name); -md2found: + +modelfound: // move on to next line... continue; } @@ -558,8 +567,9 @@ md2found: void HWR_AddPlayerModel(int skin) // For skins that were added after startup { FILE *f; - char name[18], filename[32]; + char name[24], filename[32]; float scale, offset; + size_t prefixlen; if (nomd2s) return; @@ -577,32 +587,42 @@ void HWR_AddPlayerModel(int skin) // For skins that were added after startup return; } - // Check for any model that match the names of player skins! - while (fscanf(f, "%19s %31s %f %f", name, filename, &scale, &offset) == 4) + // length of the player model prefix + prefixlen = strlen(PLAYERMODELPREFIX); + + // Check for any models that match the names of player skins! + while (fscanf(f, "%25s %31s %f %f", name, filename, &scale, &offset) == 4) { - if (stricmp(name, skins[skin].name) == 0) + char *skinname = name; + size_t len = strlen(name); + + // ignore the player model prefix. + if (!strnicmp(name, PLAYERMODELPREFIX, prefixlen) && (len > prefixlen)) + skinname += prefixlen; + + if (stricmp(skinname, skins[skin].name) == 0) { md2_playermodels[skin].skin = skin; md2_playermodels[skin].scale = scale; md2_playermodels[skin].offset = offset; md2_playermodels[skin].notfound = false; strcpy(md2_playermodels[skin].filename, filename); - goto playermd2found; + goto playermodelfound; } } - //CONS_Printf("Model for player skin %s not found\n", skins[skin].name); md2_playermodels[skin].notfound = true; -playermd2found: +playermodelfound: fclose(f); } void HWR_AddSpriteModel(size_t spritenum) // For sprites that were added after startup { FILE *f; - // name[18] is used to check for names in the models.dat file that match with sprites or player skins + // name[24] is used to check for names in the models.dat file that match with sprites or player skins // sprite names are always 4 characters long, and names is for player skins can be up to 19 characters long - char name[18], filename[32]; + // PLAYERMODELPREFIX is 6 characters long + char name[24], filename[32]; float scale, offset; if (nomd2s) @@ -622,22 +642,30 @@ void HWR_AddSpriteModel(size_t spritenum) // For sprites that were added after s return; } - // Check for any MD2s that match the names of sprite names! - while (fscanf(f, "%19s %31s %f %f", name, filename, &scale, &offset) == 4) + // Check for any models that match the names of sprite names! + while (fscanf(f, "%25s %31s %f %f", name, filename, &scale, &offset) == 4) { + // length of the sprite name + size_t len = strlen(name); + if (len != 4) // must be 4 characters long exactly. otherwise it's not a sprite name. + continue; + + // check for the player model prefix. + if (!strnicmp(name, PLAYERMODELPREFIX, strlen(PLAYERMODELPREFIX))) + continue; // that's not a sprite... + if (stricmp(name, sprnames[spritenum]) == 0) { md2_models[spritenum].scale = scale; md2_models[spritenum].offset = offset; md2_models[spritenum].notfound = false; strcpy(md2_models[spritenum].filename, filename); - goto spritemd2found; + goto spritemodelfound; } } - //CONS_Printf("MD2 for sprite %s not found\n", sprnames[spritenum]); md2_models[spritenum].notfound = true; -spritemd2found: +spritemodelfound: fclose(f); } diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h index 6f5985a44..3bbe30053 100644 --- a/src/hardware/hw_md2.h +++ b/src/hardware/hw_md2.h @@ -49,4 +49,6 @@ void HWR_AddPlayerModel(INT32 skin); void HWR_AddSpriteModel(size_t spritenum); boolean HWR_DrawModel(gr_vissprite_t *spr); +#define PLAYERMODELPREFIX "PLAYER" + #endif // _HW_MD2_H_ diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 327dbd3df..a3ed3c8d2 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2222,11 +2222,11 @@ EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, EXPORT void HWRAPI(SetTransform) (FTransform *stransform) { static boolean special_splitscreen; + float used_fov; pglLoadIdentity(); if (stransform) { - boolean fovx90; - + used_fov = stransform->fovxangle; #ifdef USE_FTRANSFORM_MIRROR // mirroring from Kart if (stransform->mirror) @@ -2238,36 +2238,34 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform) else pglScalef(stransform->scalex, stransform->scaley, -stransform->scalez); + if (stransform->roll) + pglRotatef(stransform->rollangle, 0.0f, 0.0f, 1.0f); pglRotatef(stransform->anglex , 1.0f, 0.0f, 0.0f); pglRotatef(stransform->angley+270.0f, 0.0f, 1.0f, 0.0f); pglTranslatef(-stransform->x, -stransform->z, -stransform->y); - pglMatrixMode(GL_PROJECTION); - pglLoadIdentity(); - fovx90 = stransform->fovxangle > 0.0f && fabsf(stransform->fovxangle - 90.0f) < 0.5f; - special_splitscreen = (stransform->splitscreen && fovx90); - if (special_splitscreen) - GLPerspective(53.13f, 2*ASPECT_RATIO); // 53.13 = 2*atan(0.5) - else - GLPerspective(stransform->fovxangle, ASPECT_RATIO); - pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer) - pglMatrixMode(GL_MODELVIEW); + special_splitscreen = stransform->splitscreen; } else { + used_fov = fov; pglScalef(1.0f, 1.0f, -1.0f); - - pglMatrixMode(GL_PROJECTION); - pglLoadIdentity(); - if (special_splitscreen) - GLPerspective(53.13f, 2*ASPECT_RATIO); // 53.13 = 2*atan(0.5) - else - //Hurdler: is "fov" correct? - GLPerspective(fov, ASPECT_RATIO); - pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer) - pglMatrixMode(GL_MODELVIEW); } + pglMatrixMode(GL_PROJECTION); + pglLoadIdentity(); + + if (special_splitscreen) + { + used_fov = atan(tan(used_fov*M_PI/360)*0.8)*360/M_PI; + GLPerspective(used_fov, 2*ASPECT_RATIO); + } + else + GLPerspective(used_fov, ASPECT_RATIO); + + pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer) + pglMatrixMode(GL_MODELVIEW); + pglGetFloatv(GL_MODELVIEW_MATRIX, modelMatrix); // added for new coronas' code (without depth buffer) } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index c6a92487c..bf2432f5d 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -17,6 +17,7 @@ #include "m_menu.h" // gametype_cons_t #include "m_cond.h" // emblems +#include "m_misc.h" // word jumping #include "d_clisrv.h" @@ -501,37 +502,31 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags) // what we're gonna do now is check if the player exists // with that logic, characters 4 and 5 are our numbers: const char *newmsg; - char *playernum = (char*) malloc(3); + char playernum[3]; INT32 spc = 1; // used if playernum[1] is a space. strncpy(playernum, msg+3, 3); // check for undesirable characters in our "number" - if (((playernum[0] < '0') || (playernum[0] > '9')) || ((playernum[1] < '0') || (playernum[1] > '9'))) + if (((playernum[0] < '0') || (playernum[0] > '9')) || ((playernum[1] < '0') || (playernum[1] > '9'))) { // check if playernum[1] is a space if (playernum[1] == ' ') spc = 0; - // let it slide + // let it slide else { HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false); - free(playernum); return; } } // I'm very bad at C, I swear I am, additional checks eww! - if (spc != 0) - { - if (msg[5] != ' ') - { - HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false); - free(playernum); - return; - } - } + if (spc != 0 && msg[5] != ' ') + { + HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false); + return; + } - target = atoi((const char*) playernum); // turn that into a number - free(playernum); + target = atoi(playernum); // turn that into a number //CONS_Printf("%d\n", target); // check for target player, if it doesn't exist then we can't send the message! @@ -659,13 +654,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) M_GetText("Illegal say command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"), player_names[playernum]); if (server) - { - UINT8 buf[2]; - - buf[0] = (UINT8)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -679,13 +668,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) { CONS_Alert(CONS_WARNING, M_GetText("Illegal say command received from %s containing invalid characters\n"), player_names[playernum]); if (server) - { - char buf[2]; - - buf[0] = (char)playernum; - buf[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &buf, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } } @@ -1033,9 +1016,6 @@ void HU_Ticker(void) #ifndef NONET static boolean teamtalk = false; -/*static char chatchars[QUEUESIZE]; -static INT32 head = 0, tail = 0;*/ -// WHY DO YOU OVERCOMPLICATE EVERYTHING????????? // Clear spaces so we don't end up with messages only made out of emptiness static boolean HU_clearChatSpaces(void) @@ -1094,11 +1074,11 @@ static void HU_queueChatChar(char c) if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0) // used /pm { - INT32 spc = 1; // used if nodenum[1] is a space. - char *nodenum = (char*) malloc(3); + INT32 spc = 1; // used if playernum[1] is a space. + char playernum[3]; const char *newmsg; - // what we're gonna do now is check if the node exists + // what we're gonna do now is check if the player exists // with that logic, characters 4 and 5 are our numbers: // teamtalk can't send PMs, just don't send it, else everyone would be able to see it, and no one wants to see your sex RP sicko. @@ -1108,18 +1088,17 @@ static void HU_queueChatChar(char c) return; } - strncpy(nodenum, msg+3, 3); + strncpy(playernum, msg+3, 3); // check for undesirable characters in our "number" - if (((nodenum[0] < '0') || (nodenum[0] > '9')) || ((nodenum[1] < '0') || (nodenum[1] > '9'))) + if (((playernum[0] < '0') || (playernum[0] > '9')) || ((playernum[1] < '0') || (playernum[1] > '9'))) { - // check if nodenum[1] is a space - if (nodenum[1] == ' ') + // check if playernum[1] is a space + if (playernum[1] == ' ') spc = 0; // let it slide else { - HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false); - free(nodenum); + HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false); return; } } @@ -1128,14 +1107,12 @@ static void HU_queueChatChar(char c) { if (msg[5] != ' ') { - HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false); - free(nodenum); + HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false); return; } } - target = atoi((const char*) nodenum); // turn that into a number - free(nodenum); + target = atoi(playernum); // turn that into a number //CONS_Printf("%d\n", target); // check for target player, if it doesn't exist then we can't send the message! @@ -1147,7 +1124,7 @@ static void HU_queueChatChar(char c) return; } - // we need to get rid of the /pm + // we need to get rid of the /pm newmsg = msg+5+spc; strlcpy(msg, newmsg, 255); } @@ -1349,9 +1326,19 @@ boolean HU_Responder(event_t *ev) chat_scrolltime = 4; } else if (c == KEY_LEFTARROW && c_input != 0 && !OLDCHAT) // i said go back - c_input--; + { + if (ctrldown) + c_input = M_JumpWordReverse(w_chat, c_input); + else + c_input--; + } else if (c == KEY_RIGHTARROW && c_input < strlen(w_chat) && !OLDCHAT) // don't need to check for admin or w/e here since the chat won't ever contain anything if it's muted. - c_input++; + { + if (ctrldown) + c_input += M_JumpWord(&w_chat[c_input]); + else + c_input++; + } return true; } #endif @@ -1450,7 +1437,7 @@ static void HU_drawMiniChat(void) for (; i>0; i--) { - const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i-1]); + char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i-1]); size_t j = 0; INT32 linescount = 0; @@ -1492,6 +1479,9 @@ static void HU_drawMiniChat(void) dy = 0; dx = 0; msglines += linescount+1; + + if (msg) + Z_Free(msg); } y = chaty - charheight*(msglines+1); @@ -1514,7 +1504,7 @@ static void HU_drawMiniChat(void) INT32 timer = ((cv_chattime.value*TICRATE)-chat_timers[i]) - cv_chattime.value*TICRATE+9; // see below... INT32 transflag = (timer >= 0 && timer <= 9) ? (timer*V_10TRANS) : 0; // you can make bad jokes out of this one. size_t j = 0; - const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i]); // get the current message, and word wrap it. + char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i]); // get the current message, and word wrap it. UINT8 *colormap = NULL; while(msg[j]) // iterate through msg @@ -1560,6 +1550,9 @@ static void HU_drawMiniChat(void) } dy += charheight; dx = 0; + + if (msg) + Z_Free(msg); } // decrement addy and make that shit smooth: @@ -1611,7 +1604,7 @@ static void HU_drawChatLog(INT32 offset) { INT32 clrflag = 0; INT32 j = 0; - const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_log[i]); // get the current message, and word wrap it. + char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_log[i]); // get the current message, and word wrap it. UINT8 *colormap = NULL; while(msg[j]) // iterate through msg { @@ -1651,6 +1644,9 @@ static void HU_drawChatLog(INT32 offset) } dy += charheight; dx = 0; + + if (msg) + Z_Free(msg); } @@ -1660,12 +1656,9 @@ static void HU_drawChatLog(INT32 offset) } chat_scrollmedown = false; - // getmaxscroll through a lazy hack. We do all these loops, so let's not do more loops that are gonna lag the game more. :P - chat_maxscroll = (dy/charheight); // welcome to C, we don't know what min() and max() are. - if (chat_maxscroll <= (UINT32)cv_chatheight.value) - chat_maxscroll = 0; - else - chat_maxscroll -= cv_chatheight.value; + // getmaxscroll through a lazy hack. We do all these loops, + // so let's not do more loops that are gonna lag the game more. :P + chat_maxscroll = max(dy / charheight - cv_chatheight.value, 0); // if we're not bound by the time, autoscroll for next frame: if (atbottom) @@ -1806,21 +1799,17 @@ static void HU_DrawChat(void) i = 0; for(i=0; (i '9'))) || ((w_chat[4] != 0) && (((w_chat[4] < '0') || (w_chat[4] > '9'))))) && (w_chat[4] != ' ')) break; - - nodenum = (char*) malloc(3); - strncpy(nodenum, w_chat+3, 3); - n = atoi((const char*) nodenum); // turn that into a number - free(nodenum); + strncpy(playernum, w_chat+3, 3); + n = atoi(playernum); // turn that into a number // special cases: if ((n == 0) && !(w_chat[4] == '0')) @@ -1867,7 +1856,6 @@ static void HU_DrawChat(void) } HU_drawChatLog(typelines-1); // typelines is the # of lines we're typing. If there's more than 1 then the log should scroll up to give us more space. - } @@ -2390,7 +2378,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I if (!splitscreen) // don't draw it on splitscreen, { - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 253, y, playerpingtable[tab[i].num], false, 0); //else // V_DrawSmallString(x+ 246, y+4, V_YELLOWMAP, "SERVER"); @@ -2589,7 +2577,7 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer) V_DrawRightAlignedThinString(x+128, y, ((players[tab[i].num].spectator || players[tab[i].num].playerstate == PST_DEAD) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count)); if (!splitscreen) { - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 135, y+1, playerpingtable[tab[i].num], true, 0); //else //V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); @@ -2713,7 +2701,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer) V_DrawRightAlignedThinString(x+100, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count)); if (!splitscreen) { - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 113, y, playerpingtable[tab[i].num], false, 0); //else // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); @@ -2744,7 +2732,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline supercheck = supercheckdef; strlcpy(name, tab[i].name, 7); - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 113, y, playerpingtable[tab[i].num], false, 0); //else // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); @@ -2852,7 +2840,7 @@ static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scor strlcpy(name, tab[i].name, 7); if (!splitscreen) // don't draw it on splitscreen, { - if (!(tab[i].num == serverplayer)) + if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) HU_drawPing(x+ 135, y+1, playerpingtable[tab[i].num], true, 0); //else // V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); diff --git a/src/info.c b/src/info.c index 60913b519..20bd73498 100644 --- a/src/info.c +++ b/src/info.c @@ -762,7 +762,7 @@ state_t states[NUMSTATES] = {SPR_PLAY, SPR2_LIFE, 20, {NULL}, 0, 4, S_NULL}, // S_PLAY_ICON3 // Level end sign (uses player sprite) - {SPR_PLAY, SPR2_SIGN|FF_PAPERSPRITE, -1, {NULL}, 0, 29, S_PLAY_SIGN}, // S_PLAY_SIGN + {SPR_PLAY, SPR2_SIGN|FF_PAPERSPRITE, 2, {NULL}, 0, 29, S_PLAY_SIGN}, // S_PLAY_SIGN // NiGHTS Player, transforming {SPR_PLAY, SPR2_TRNS|FF_ANIMATE, 7, {NULL}, 0, 4, S_PLAY_NIGHTS_TRANS2}, // S_PLAY_NIGHTS_TRANS1 @@ -1849,18 +1849,19 @@ state_t states[NUMSTATES] = {SPR_BBLS, 3, 8, {A_BubbleCheck}, 0, 0, S_BUBBLES1}, // S_BUBBLES4 // Level End Sign - {SPR_SIGN, 0, -1, {A_SignPlayer}, -3, 0, S_NULL}, // S_SIGN - {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSPIN2}, // S_SIGNSPIN1 - {SPR_SIGN, 0, 0, {A_Repeat}, 4, S_SIGNSPIN1, S_SIGNSPIN3}, // S_SIGNSPIN2 - {SPR_SIGN, 0, 0, {A_SignPlayer}, -2, 0, S_SIGNSPIN4}, // S_SIGNSPIN3 - {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSPIN5}, // S_SIGNSPIN4 - {SPR_SIGN, 0, 0, {A_Repeat}, 4, S_SIGNSPIN4, S_SIGNSPIN6}, // S_SIGNSPIN5 - {SPR_SIGN, 0, 0, {A_SignPlayer}, -3, 0, S_SIGNSPIN1}, // S_SIGNSPIN6 - {SPR_SIGN, 0, 1, {A_SignPlayer}, -1, 0, S_SIGNSLOW}, // S_SIGNPLAYER - {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSLOW}, // S_SIGNSLOW - {SPR_SIGN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SIGNSTOP - {SPR_SIGN, FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_SIGNBOARD - {SPR_SIGN, FF_PAPERSPRITE|1, -1, {NULL}, 0, 29, S_NULL}, // S_EGGMANSIGN + {SPR_SIGN, 0, -1, {A_SignPlayer}, -3, 0, S_NULL}, // S_SIGN + {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSPIN2}, // S_SIGNSPIN1 + {SPR_SIGN, 0, 0, {A_Repeat}, 4, S_SIGNSPIN1, S_SIGNSPIN3}, // S_SIGNSPIN2 + {SPR_SIGN, 0, 0, {A_SignPlayer}, -2, 0, S_SIGNSPIN4}, // S_SIGNSPIN3 + {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSPIN5}, // S_SIGNSPIN4 + {SPR_SIGN, 0, 0, {A_Repeat}, 4, S_SIGNSPIN4, S_SIGNSPIN6}, // S_SIGNSPIN5 + {SPR_SIGN, 0, 0, {A_SignPlayer}, -3, 0, S_SIGNSPIN1}, // S_SIGNSPIN6 + {SPR_SIGN, 0, 1, {A_SignPlayer}, -1, 0, S_SIGNSLOW}, // S_SIGNPLAYER + {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSLOW}, // S_SIGNSLOW + {SPR_SIGN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SIGNSTOP + {SPR_SIGN, FF_PAPERSPRITE| 2, -1, {NULL}, 0, 0, S_NULL}, // S_SIGNBOARD + {SPR_SIGN, FF_PAPERSPRITE| 1, -1, {NULL}, 0, 29, S_NULL}, // S_EGGMANSIGN + {SPR_SIGN, FF_PAPERSPRITE|18, -1, {NULL}, 0, 29, S_NULL}, // S_CLEARSIGN // Spike Ball {SPR_SPIK, 0, 1, {NULL}, 0, 0, S_SPIKEBALL2}, // S_SPIKEBALL1 @@ -2342,12 +2343,13 @@ state_t states[NUMSTATES] = // TNT barrel {SPR_BARR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_TNTBARREL_STND1 - {SPR_BARX, 0|FF_FULLBRIGHT, 3, {A_SetObjectFlags}, MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP, 0, S_TNTBARREL_EXPL2}, // S_TNTBARREL_EXPL1 - {SPR_BARX, 1|FF_FULLBRIGHT, 2, {A_TNTExplode}, MT_TNTDUST, 0, S_TNTBARREL_EXPL3}, // S_TNTBARREL_EXPL2 - {SPR_BARX, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_TNTBARREL_EXPL4}, // S_TNTBARREL_EXPL3 - {SPR_BARX, 2|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_TNTBARREL_EXPL5}, // S_TNTBARREL_EXPL4 - {SPR_BARX, 3|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_TNTBARREL_EXPL6}, // S_TNTBARREL_EXPL5 - {SPR_NULL, 0, 35, {NULL}, 0, 0, S_NULL}, // S_TNTBARREL_EXPL6 + {SPR_BARX, 0, 0, {A_RollAngle}, 0, 1, S_TNTBARREL_EXPL2}, // S_TNTBARREL_EXPL1 + {SPR_BARX, 0|FF_FULLBRIGHT, 3, {A_SetObjectFlags}, MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP, 0, S_TNTBARREL_EXPL3}, // S_TNTBARREL_EXPL2 + {SPR_BARX, 1|FF_FULLBRIGHT, 2, {A_TNTExplode}, MT_TNTDUST, 0, S_TNTBARREL_EXPL4}, // S_TNTBARREL_EXPL3 + {SPR_BARX, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_TNTBARREL_EXPL5}, // S_TNTBARREL_EXPL4 + {SPR_BARX, 2|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_TNTBARREL_EXPL6}, // S_TNTBARREL_EXPL5 + {SPR_BARX, 3|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_TNTBARREL_EXPL7}, // S_TNTBARREL_EXPL6 + {SPR_NULL, 0, 35, {NULL}, 0, 0, S_NULL}, // S_TNTBARREL_EXPL7 #ifndef ROTSPRITE {SPR_BARR, 1|FF_ANIMATE, -1, {NULL}, 7, 2, S_NULL}, // S_TNTBARREL_FLYING #else @@ -2393,7 +2395,7 @@ state_t states[NUMSTATES] = // Minecart {SPR_NULL, 0, 1, {NULL}, 0, 0, S_MINECART_IDLE}, // S_MINECART_IDLE - {SPR_NULL, 0, 0, {A_KillSegments}, 0, 0, S_TNTBARREL_EXPL3}, // S_MINECART_DTH1 + {SPR_NULL, 0, 0, {A_KillSegments}, 0, 0, S_TNTBARREL_EXPL4}, // S_MINECART_DTH1 {SPR_MCRT, 8|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_MINECARTEND {SPR_MCRT, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_MINECARTSEG_FRONT {SPR_MCRT, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_MINECARTSEG_BACK @@ -5734,7 +5736,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_spring, // activesound - MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING, // flags + MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags S_EGGMOBILE2_POGO5 // raisestate }, @@ -6486,10 +6488,10 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 24*FRACUNIT, // radius 24*FRACUNIT, // height 0, // display offset - 0, // mass + DMG_FIRE, // mass 1, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags + MF_NOBLOCKMAP|MF_MISSILE|MF_PAIN|MF_NOGRAVITY, // flags S_NULL // raisestate }, @@ -7850,7 +7852,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MT_SPARK, // painchance sfx_s3kb8, // painsound S_EGGMANSIGN, // meleestate - S_NULL, // missilestate + S_CLEARSIGN, // missilestate S_SIGNSTOP, // deathstate S_NULL, // xdeathstate sfx_s3k64, // deathsound diff --git a/src/info.h b/src/info.h index 33f597e61..4be336e85 100644 --- a/src/info.h +++ b/src/info.h @@ -2022,6 +2022,7 @@ typedef enum state S_SIGNSTOP, S_SIGNBOARD, S_EGGMANSIGN, + S_CLEARSIGN, // Spike Ball S_SPIKEBALL1, @@ -2503,6 +2504,7 @@ typedef enum state S_TNTBARREL_EXPL4, S_TNTBARREL_EXPL5, S_TNTBARREL_EXPL6, + S_TNTBARREL_EXPL7, S_TNTBARREL_FLYING, // TNT proximity shell diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 2a82ec512..66bd30e32 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2193,6 +2193,20 @@ static int lib_rPointInSubsector(lua_State *L) return 1; } +static int lib_rPointInSubsectorOrNil(lua_State *L) +{ + fixed_t x = luaL_checkfixed(L, 1); + fixed_t y = luaL_checkfixed(L, 2); + subsector_t *sub = R_PointInSubsectorOrNull(x, y); + //HUDSAFE + INLEVEL + if (sub) + LUA_PushUserdata(L, sub, META_SUBSECTOR); + else + lua_pushnil(L); + return 1; +} + // R_THINGS //////////// @@ -3127,6 +3141,7 @@ static luaL_Reg lib[] = { {"R_PointToDist",lib_rPointToDist}, {"R_PointToDist2",lib_rPointToDist2}, {"R_PointInSubsector",lib_rPointInSubsector}, + {"R_PointInSubsectorOrNil",lib_rPointInSubsectorOrNil}, // r_things (sprite) {"R_Char2Frame",lib_rChar2Frame}, diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index 9c426fad3..48f2e20a8 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -87,13 +87,7 @@ deny: CONS_Alert(CONS_WARNING, M_GetText("Illegal lua command received from %s\n"), player_names[playernum]); if (server) - { - UINT8 bufn[2]; - - bufn[0] = (UINT8)playernum; - bufn[1] = KICK_MSG_CON_FAIL; - SendNetXCmd(XD_KICK, &bufn, 2); - } + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } // Wrapper for COM_AddCommand commands diff --git a/src/lua_hook.h b/src/lua_hook.h index 0ef21490e..94d2239f7 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -102,7 +102,7 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj); // Hook for P_PlayerAfterThink Smiles mobj-following UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj); // Hook for P_PlayerCanDamage -void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting +void LUAh_PlayerQuit(player_t *plr, kickreason_t reason); // Hook for player quitting void LUAh_IntermissionThinker(void); // Hook for Y_Ticker boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh.... UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index e2c41f54f..306bf6839 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -1439,7 +1439,7 @@ UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj) return shouldCollide; } -void LUAh_PlayerQuit(player_t *plr, int reason) +void LUAh_PlayerQuit(player_t *plr, kickreason_t reason) { hook_p hookp; if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8)))) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index f451944e3..d14a43944 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -268,10 +268,14 @@ static int patch_get(lua_State *L) #endif enum patch field = luaL_checkoption(L, 2, NULL, patch_opt); - // patches are CURRENTLY always valid, expected to be cached with PU_STATIC - // this may change in the future, so patch.valid still exists - if (!patch) + // patches are invalidated when switching renderers + if (!patch) { + if (field == patch_valid) { + lua_pushboolean(L, 0); + return 1; + } return LUA_ErrInvalid(L, "patch_t"); + } switch (field) { @@ -424,7 +428,7 @@ static int libd_cachePatch(lua_State *L) return 1; } -// v.getSpritePatch(sprite, [frame, [angle]]) +// v.getSpritePatch(sprite, [frame, [angle, [rollangle]]]) static int libd_getSpritePatch(lua_State *L) { UINT32 i; // sprite prefix @@ -475,13 +479,31 @@ static int libd_getSpritePatch(lua_State *L) if (angle >= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range? return 0; +#ifdef ROTSPRITE + if (lua_isnumber(L, 4)) + { + // rotsprite????? + angle_t rollangle = luaL_checkangle(L, 4); + INT32 rot = R_GetRollAngle(rollangle); + + if (rot) { + if (!(sprframe->rotsprite.cached & (1<flip & (1<rotsprite.patch[angle][rot], META_PATCH); + lua_pushboolean(L, false); + lua_pushboolean(L, true); + return 3; + } + } +#endif + // push both the patch and it's "flip" value LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_PATCH), META_PATCH); lua_pushboolean(L, (sprframe->flip & (1<= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range? return 0; +#ifdef ROTSPRITE + if (lua_isnumber(L, 4)) + { + // rotsprite????? + angle_t rollangle = luaL_checkangle(L, 4); + INT32 rot = R_GetRollAngle(rollangle); + + if (rot) { + if (!(sprframe->rotsprite.cached & (1<flip & (1<rotsprite.patch[angle][rot], META_PATCH); + lua_pushboolean(L, false); + lua_pushboolean(L, true); + return 3; + } + } +#endif + // push both the patch and it's "flip" value LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_PATCH), META_PATCH); lua_pushboolean(L, (sprframe->flip & (1<y); return 1; + case vertex_floorzset: + lua_pushboolean(L, vertex->floorzset); + return 1; + case vertex_ceilingzset: + lua_pushboolean(L, vertex->ceilingzset); + return 1; + case vertex_floorz: + lua_pushfixed(L, vertex->floorz); + return 1; + case vertex_ceilingz: + lua_pushfixed(L, vertex->ceilingz); + return 1; } return 0; } @@ -2014,6 +2034,8 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->typeoflevel); else if (fastcmp(field,"nextlevel")) lua_pushinteger(L, header->nextlevel); + else if (fastcmp(field,"keywords")) + lua_pushstring(L, header->keywords); else if (fastcmp(field,"musname")) lua_pushstring(L, header->musname); else if (fastcmp(field,"mustrack")) diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 222487751..90733b2c6 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -88,7 +88,8 @@ enum mobj_e { #ifdef ESLOPE mobj_standingslope, #endif - mobj_colorized + mobj_colorized, + mobj_shadowscale }; static const char *const mobj_opt[] = { @@ -156,6 +157,7 @@ static const char *const mobj_opt[] = { "standingslope", #endif "colorized", + "shadowscale", 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]) @@ -390,6 +392,9 @@ 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; default: // extra custom variables in Lua memory lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); @@ -719,6 +724,9 @@ 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; default: lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index c501fbbb2..d1da97d70 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -120,6 +120,8 @@ static int player_get(lua_State *L) lua_pushfixed(L, plr->deltaviewheight); else if (fastcmp(field,"bob")) lua_pushfixed(L, plr->bob); + else if (fastcmp(field,"viewrollangle")) + lua_pushangle(L, plr->viewrollangle); else if (fastcmp(field,"aiming")) lua_pushangle(L, plr->aiming); else if (fastcmp(field,"drawangle")) @@ -362,6 +364,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->bot); else if (fastcmp(field,"jointime")) lua_pushinteger(L, plr->jointime); + else if (fastcmp(field,"quittime")) + lua_pushinteger(L, plr->quittime); #ifdef HWRENDER else if (fastcmp(field,"fovadd")) lua_pushfixed(L, plr->fovadd); @@ -415,6 +419,8 @@ static int player_set(lua_State *L) plr->deltaviewheight = luaL_checkfixed(L, 3); else if (fastcmp(field,"bob")) plr->bob = luaL_checkfixed(L, 3); + else if (fastcmp(field,"viewrollangle")) + plr->viewrollangle = luaL_checkangle(L, 3); else if (fastcmp(field,"aiming")) { plr->aiming = luaL_checkangle(L, 3); if (plr == &players[consoleplayer]) @@ -701,6 +707,8 @@ static int player_set(lua_State *L) return NOSET; else if (fastcmp(field,"jointime")) plr->jointime = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"quittime")) + plr->quittime = (tic_t)luaL_checkinteger(L, 3); #ifdef HWRENDER else if (fastcmp(field,"fovadd")) plr->fovadd = luaL_checkfixed(L, 3); diff --git a/src/lua_script.c b/src/lua_script.c index eb1afaf09..eb6c54ae0 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -102,6 +102,9 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word,"circuitmap")) { lua_pushboolean(L, circuitmap); return 1; + } else if (fastcmp(word,"stoppedclock")) { + lua_pushboolean(L, stoppedclock); + return 1; } else if (fastcmp(word,"netgame")) { lua_pushboolean(L, netgame); return 1; @@ -274,12 +277,6 @@ int LUA_PushGlobals(lua_State *L, const char *word) return 0; LUA_PushUserdata(L, &players[serverplayer], META_PLAYER); return 1; - } else if (fastcmp(word,"admin")) { // BACKWARDS COMPATIBILITY HACK: This was replaced with IsPlayerAdmin(), but some 2.1 Lua scripts still use the admin variable. It now points to the first admin player in the array. - LUA_Deprecated(L, "admin", "IsPlayerAdmin(player)"); - if (!playeringame[adminplayers[0]] || IsPlayerAdmin(serverplayer)) - return 0; - LUA_PushUserdata(L, &players[adminplayers[0]], META_PLAYER); - return 1; } else if (fastcmp(word,"emeralds")) { lua_pushinteger(L, emeralds); return 1; diff --git a/src/m_anigif.c b/src/m_anigif.c index 32fc2746d..faa8f29e1 100644 --- a/src/m_anigif.c +++ b/src/m_anigif.c @@ -19,6 +19,7 @@ #include "v_video.h" #include "i_video.h" #include "m_misc.h" +#include "st_stuff.h" // st_palette #ifdef HWRENDER #include "hardware/hw_main.h" @@ -29,11 +30,17 @@ consvar_t cv_gif_optimize = {"gif_optimize", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_gif_downscale = {"gif_downscale", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_gif_localcolortable = {"gif_localcolortable", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; #ifdef HAVE_ANIGIF static boolean gif_optimize = false; // So nobody can do something dumb static boolean gif_downscale = false; // like changing cvars mid output -static RGBA_t *gif_palette = NULL; + +// Palette handling +static boolean gif_localcolortable = false; +static boolean gif_colorprofile = false; +static RGBA_t *gif_headerpalette = NULL; +static RGBA_t *gif_framepalette = NULL; static FILE *gif_out = NULL; static INT32 gif_frames = 0; @@ -393,16 +400,47 @@ const UINT8 gifhead_nsid[19] = {0x21,0xFF,0x0B, // extension block + size 0x4E,0x45,0x54,0x53,0x43,0x41,0x50,0x45,0x32,0x2E,0x30, // NETSCAPE2.0 0x03,0x01,0xFF,0xFF,0x00}; // sub-block, repetitions + +// +// GIF_getpalette +// determine the palette for the current frame. +// +static RGBA_t *GIF_getpalette(size_t palnum) +{ + // In hardware mode, always returns the local palette +#ifdef HWRENDER + if (rendermode == render_opengl) + return pLocalPalette; + else +#endif + return (gif_colorprofile ? &pLocalPalette[palnum*256] : &pMasterPalette[palnum*256]); +} + +// +// GIF_palwrite +// writes the gif palette. +// used both for the header and local color tables. +// +static UINT8 *GIF_palwrite(UINT8 *p, RGBA_t *pal) +{ + INT32 i; + for (i = 0; i < 256; i++) + { + WRITEUINT8(p, pal[i].s.red); + WRITEUINT8(p, pal[i].s.green); + WRITEUINT8(p, pal[i].s.blue); + } + return p; +} + // // GIF_headwrite // writes the gif header to the currently open output file. -// NOTE that this code does not accomodate for palette changes. // static void GIF_headwrite(void) { UINT8 *gifhead = Z_Malloc(800, PU_STATIC, NULL); UINT8 *p = gifhead; - INT32 i; UINT16 rwidth, rheight; if (!gif_out) @@ -423,24 +461,17 @@ static void GIF_headwrite(void) rwidth = vid.width; rheight = vid.height; } + WRITEUINT16(p, rwidth); WRITEUINT16(p, rheight); // colors, aspect, etc - WRITEUINT8(p, 0xF7); + WRITEUINT8(p, 0xF7); // (0xF7 = 1111 0111) WRITEUINT8(p, 0x00); WRITEUINT8(p, 0x00); // write color table - { - RGBA_t *pal = gif_palette; - for (i = 0; i < 256; i++) - { - WRITEUINT8(p, pal[i].s.red); - WRITEUINT8(p, pal[i].s.green); - WRITEUINT8(p, pal[i].s.blue); - } - } + p = GIF_palwrite(p, gif_headerpalette); // write extension block WRITEMEM(p, gifhead_nsid, sizeof(gifhead_nsid)); @@ -468,7 +499,7 @@ static void hwrconvert(void) INT32 x, y; size_t i = 0; - InitColorLUT(gif_palette); + InitColorLUT(gif_framepalette); for (y = 0; y < vid.height; y++) { @@ -494,6 +525,7 @@ static void GIF_framewrite(void) UINT8 *p; UINT8 *movie_screen = screens[2]; INT32 blitx, blity, blitw, blith; + boolean palchanged; if (!gifframe_data) gifframe_data = Z_Malloc(gifframe_size, PU_STATIC, NULL); @@ -502,8 +534,18 @@ static void GIF_framewrite(void) if (!gif_out) return; + // Lactozilla: Compare the header's palette with the current frame's palette and see if it changed. + if (gif_localcolortable) + { + gif_framepalette = GIF_getpalette(max(st_palette, 0)); + palchanged = memcmp(gif_headerpalette, gif_framepalette, sizeof(RGBA_t) * 256); + } + else + palchanged = false; + // Compare image data (for optimizing GIF) - if (gif_optimize && gif_frames > 0) + // If the palette has changed, the entire frame is considered to be different. + if (gif_optimize && gif_frames > 0 && (!palchanged)) { // before blit movie_screen points to last frame, cur_screen points to this frame UINT8 *cur_screen = screens[0]; @@ -566,7 +608,20 @@ static void GIF_framewrite(void) WRITEUINT16(p, (UINT16)(blity / scrbuf_downscaleamt)); WRITEUINT16(p, (UINT16)(blitw / scrbuf_downscaleamt)); WRITEUINT16(p, (UINT16)(blith / scrbuf_downscaleamt)); - WRITEUINT8(p, 0); // no local table of colors + + if (!gif_localcolortable) + WRITEUINT8(p, 0); // no local table of colors + else + { + if (palchanged) + { + // The palettes are different, so write the Local Color Table! + WRITEUINT8(p, 0x87); // (0x87 = 1000 0111) + p = GIF_palwrite(p, gif_framepalette); + } + else + WRITEUINT8(p, 0); // They are equal, no Local Color Table needed. + } scrbuf_pos = movie_screen + blitx + (blity * vid.width); scrbuf_writeend = scrbuf_pos + (blitw - 1) + ((blith - 1) * vid.width); @@ -624,29 +679,15 @@ static void GIF_framewrite(void) // INT32 GIF_open(const char *filename) { -#if 0 - if (rendermode != render_soft) - { - CONS_Alert(CONS_WARNING, M_GetText("GIFs cannot be taken in non-software modes!\n")); - return 0; - } -#endif - gif_out = fopen(filename, "wb"); if (!gif_out) return 0; gif_optimize = (!!cv_gif_optimize.value); gif_downscale = (!!cv_gif_downscale.value); - - // GIF color table - // In hardware mode, uses the master palette - gif_palette = ((cv_screenshot_colorprofile.value -#ifdef HWRENDER - && (rendermode == render_soft) -#endif - ) ? pLocalPalette - : pMasterPalette); + gif_localcolortable = (!!cv_gif_localcolortable.value); + gif_colorprofile = (!!cv_screenshot_colorprofile.value); + gif_headerpalette = GIF_getpalette(0); GIF_headwrite(); gif_frames = 0; diff --git a/src/m_anigif.h b/src/m_anigif.h index 9bdf2cc7f..9eb6faa75 100644 --- a/src/m_anigif.h +++ b/src/m_anigif.h @@ -27,6 +27,6 @@ void GIF_frame(void); INT32 GIF_close(void); #endif -extern consvar_t cv_gif_optimize, cv_gif_downscale; +extern consvar_t cv_gif_optimize, cv_gif_downscale, cv_gif_localcolortable; #endif diff --git a/src/m_argv.c b/src/m_argv.c index 935f4b9e3..129706127 100644 --- a/src/m_argv.c +++ b/src/m_argv.c @@ -92,36 +92,21 @@ const char *M_GetNextParm(void) void M_PushSpecialParameters(void) { INT32 i; - char s[256]; - boolean onetime = false; - for (i = 1; i < myargc; i++) { if (myargv[i][0] == '+') { - strcpy(s, &myargv[i][1]); + COM_BufAddText(&myargv[i][1]); i++; // get the parameters of the command too for (; i < myargc && myargv[i][0] != '+' && myargv[i][0] != '-'; i++) { - strcat(s, " "); - if (!onetime) - { - strcat(s, "\""); - onetime = true; - } - strcat(s, myargv[i]); + COM_BufAddText(va(" \"%s\"", myargv[i])); } - if (onetime) - { - strcat(s, "\""); - onetime = false; - } - strcat(s, "\n"); // push it - COM_BufAddText(s); + COM_BufAddText("\n"); i--; } } diff --git a/src/m_cheat.c b/src/m_cheat.c index c284acf3e..980d9fc21 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -452,7 +452,7 @@ void Command_RTeleport_f(void) else inty = 0; - ss = R_IsPointInSubsector(p->mo->x + intx*FRACUNIT, p->mo->y + inty*FRACUNIT); + ss = R_PointInSubsectorOrNull(p->mo->x + intx*FRACUNIT, p->mo->y + inty*FRACUNIT); if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height) { CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n")); @@ -530,7 +530,7 @@ void Command_Teleport_f(void) inty = mt->y<z<sector->ceilingheight - ss->sector->floorheight < p->mo->height) { CONS_Alert(CONS_NOTICE, M_GetText("Spawnpoint not in a valid location.\n")); @@ -597,7 +597,7 @@ void Command_Teleport_f(void) return; } - ss = R_IsPointInSubsector(mo2->x, mo2->y); + ss = R_PointInSubsectorOrNull(mo2->x, mo2->y); if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height) { CONS_Alert(CONS_NOTICE, M_GetText("Starpost not in a valid location.\n")); @@ -653,7 +653,7 @@ void Command_Teleport_f(void) } } - ss = R_IsPointInSubsector(intx, inty); + ss = R_PointInSubsectorOrNull(intx, inty); if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height) { CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n")); diff --git a/src/m_menu.c b/src/m_menu.c index f2287177f..0d81baa46 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -231,6 +231,8 @@ static void M_Credits(INT32 choice); static void M_SoundTest(INT32 choice); static void M_PandorasBox(INT32 choice); static void M_EmblemHints(INT32 choice); +static void M_HandleEmblemHints(INT32 choice); +UINT32 hintpage = 1; static void M_HandleChecklist(INT32 choice); menu_t SR_MainDef, SR_UnlockChecklistDef; @@ -323,6 +325,7 @@ menu_t OP_DataOptionsDef, OP_ScreenshotOptionsDef, OP_EraseDataDef; menu_t OP_ServerOptionsDef; menu_t OP_MonitorToggleDef; static void M_ScreenshotOptions(INT32 choice); +static void M_SetupScreenshotMenu(void); static void M_EraseData(INT32 choice); static void M_Addons(INT32 choice); @@ -364,7 +367,6 @@ static void M_DrawMonitorToggles(void); static void M_OGL_DrawFogMenu(void); #endif #ifndef NONET -static void M_DrawScreenshotMenu(void); static void M_DrawConnectMenu(void); static void M_DrawMPMainMenu(void); static void M_DrawRoomMenu(void); @@ -727,8 +729,9 @@ static menuitem_t SR_SoundTestMenu[] = static menuitem_t SR_EmblemHintMenu[] = { - {IT_STRING|IT_CVAR, NULL, "Emblem Radar", &cv_itemfinder, 10}, - {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SPauseDef, 20} + {IT_STRING | IT_ARROWS, NULL, "Page", M_HandleEmblemHints, 10}, + {IT_STRING|IT_CVAR, NULL, "Emblem Radar", &cv_itemfinder, 20}, + {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SPauseDef, 30} }; // -------------------------------- @@ -1397,7 +1400,7 @@ static menuitem_t OP_OpenGLOptionsMenu[] = {IT_STRING|IT_CVAR, NULL, "Model lighting", &cv_grmodellighting, 32}, {IT_HEADER, NULL, "General", NULL, 51}, - {IT_STRING|IT_CVAR, NULL, "Field of view", &cv_grfov, 63}, + {IT_STRING|IT_CVAR, NULL, "Field of view", &cv_fov, 63}, {IT_STRING|IT_CVAR, NULL, "Quality", &cv_scr_depth, 73}, {IT_STRING|IT_CVAR, NULL, "Texture Filter", &cv_grfiltermode, 83}, {IT_STRING|IT_CVAR, NULL, "Anisotropic", &cv_granisotropicmode,93}, @@ -1446,8 +1449,9 @@ static menuitem_t OP_SoundOptionsMenu[] = {IT_HEADER, NULL, "Miscellaneous", NULL, 102}, {IT_STRING | IT_CVAR, NULL, "Closed Captioning", &cv_closedcaptioning, 114}, {IT_STRING | IT_CVAR, NULL, "Reset Music Upon Dying", &cv_resetmusic, 124}, + {IT_STRING | IT_CVAR, NULL, "Default 1-Up sound", &cv_1upsound, 134}, - {IT_STRING | IT_SUBMENU, NULL, "Advanced Settings...", &OP_SoundAdvancedDef, 144}, + {IT_STRING | IT_SUBMENU, NULL, "Advanced Settings...", &OP_SoundAdvancedDef, 154}, }; #ifdef HAVE_OPENMPT @@ -1513,6 +1517,7 @@ static menuitem_t OP_ScreenshotOptionsMenu[] = {IT_STRING|IT_CVAR, NULL, "Region Optimizing", &cv_gif_optimize, 95}, {IT_STRING|IT_CVAR, NULL, "Downscaling", &cv_gif_downscale, 100}, + {IT_STRING|IT_CVAR, NULL, "Local Color Table", &cv_gif_localcolortable, 105}, {IT_STRING|IT_CVAR, NULL, "Memory Level", &cv_zlib_memorya, 95}, {IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_levela, 100}, @@ -1523,13 +1528,14 @@ static menuitem_t OP_ScreenshotOptionsMenu[] = enum { op_screenshot_colorprofile = 1, + op_screenshot_storagelocation = 3, op_screenshot_folder = 4, op_movie_folder = 11, op_screenshot_capture = 12, op_screenshot_gif_start = 13, - op_screenshot_gif_end = 14, - op_screenshot_apng_start = 15, - op_screenshot_apng_end = 18, + op_screenshot_gif_end = 15, + op_screenshot_apng_start = 16, + op_screenshot_apng_end = 19, }; static menuitem_t OP_EraseDataMenu[] = @@ -1583,32 +1589,33 @@ static menuitem_t OP_ServerOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Players required for exit", &cv_playersforexit, 96}, {IT_STRING | IT_CVAR, NULL, "Starposts", &cv_coopstarposts, 101}, {IT_STRING | IT_CVAR, NULL, "Life sharing", &cv_cooplives, 106}, + {IT_STRING | IT_CVAR, NULL, "Post-goal free roaming", &cv_exitmove, 111}, - {IT_HEADER, NULL, "Race, Competition", NULL, 115}, - {IT_STRING | IT_CVAR, NULL, "Level completion countdown", &cv_countdowntime, 121}, - {IT_STRING | IT_CVAR, NULL, "Item Monitors", &cv_competitionboxes, 126}, + {IT_HEADER, NULL, "Race, Competition", NULL, 120}, + {IT_STRING | IT_CVAR, NULL, "Level completion countdown", &cv_countdowntime, 126}, + {IT_STRING | IT_CVAR, NULL, "Item Monitors", &cv_competitionboxes, 131}, - {IT_HEADER, NULL, "Ringslinger (Match, CTF, Tag, H&S)", NULL, 135}, - {IT_STRING | IT_CVAR, NULL, "Time Limit", &cv_timelimit, 141}, - {IT_STRING | IT_CVAR, NULL, "Score Limit", &cv_pointlimit, 146}, - {IT_STRING | IT_CVAR, NULL, "Overtime on Tie", &cv_overtime, 151}, - {IT_STRING | IT_CVAR, NULL, "Player respawn delay", &cv_respawntime, 156}, + {IT_HEADER, NULL, "Ringslinger (Match, CTF, Tag, H&S)", NULL, 140}, + {IT_STRING | IT_CVAR, NULL, "Time Limit", &cv_timelimit, 146}, + {IT_STRING | IT_CVAR, NULL, "Score Limit", &cv_pointlimit, 151}, + {IT_STRING | IT_CVAR, NULL, "Overtime on Tie", &cv_overtime, 156}, + {IT_STRING | IT_CVAR, NULL, "Player respawn delay", &cv_respawntime, 161}, - {IT_STRING | IT_CVAR, NULL, "Item Monitors", &cv_matchboxes, 166}, - {IT_STRING | IT_CVAR, NULL, "Weapon Rings", &cv_specialrings, 171}, - {IT_STRING | IT_CVAR, NULL, "Power Stones", &cv_powerstones, 176}, + {IT_STRING | IT_CVAR, NULL, "Item Monitors", &cv_matchboxes, 171}, + {IT_STRING | IT_CVAR, NULL, "Weapon Rings", &cv_specialrings, 176}, + {IT_STRING | IT_CVAR, NULL, "Power Stones", &cv_powerstones, 181}, - {IT_STRING | IT_CVAR, NULL, "Flag respawn delay", &cv_flagtime, 186}, - {IT_STRING | IT_CVAR, NULL, "Hiding time", &cv_hidetime, 191}, + {IT_STRING | IT_CVAR, NULL, "Flag respawn delay", &cv_flagtime, 191}, + {IT_STRING | IT_CVAR, NULL, "Hiding time", &cv_hidetime, 196}, - {IT_HEADER, NULL, "Teams", NULL, 200}, - {IT_STRING | IT_CVAR, NULL, "Autobalance sizes", &cv_autobalance, 206}, - {IT_STRING | IT_CVAR, NULL, "Scramble on Map Change", &cv_scrambleonchange, 211}, + {IT_HEADER, NULL, "Teams", NULL, 205}, + {IT_STRING | IT_CVAR, NULL, "Autobalance sizes", &cv_autobalance, 211}, + {IT_STRING | IT_CVAR, NULL, "Scramble on Map Change", &cv_scrambleonchange, 216}, #ifndef NONET - {IT_HEADER, NULL, "Advanced", NULL, 220}, - {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 226}, - {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 240}, + {IT_HEADER, NULL, "Advanced", NULL, 225}, + {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 231}, + {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 245}, #endif }; @@ -3026,7 +3033,8 @@ static void M_ChangeCvar(INT32 choice) || !(currentMenu->menuitems[itemOn].status & IT_CV_INTEGERSTEP)) { char s[20]; - sprintf(s,"%f",FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f)); + float n = FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f); + sprintf(s,"%ld%s",(long)n,M_Ftrim(n)); CV_Set(cv,s); } else @@ -3756,6 +3764,12 @@ void M_SetupNextMenu(menu_t *menudef) hidetitlemap = false; } +// Guess I'll put this here, idk +boolean M_MouseNeeded(void) +{ + return (currentMenu == &MessageDef && currentMenu->prevMenu == &OP_ChangeControlsDef); +} + // // M_Ticker // @@ -3777,6 +3791,9 @@ void M_Ticker(void) if (--vidm_testingmode == 0) setmodeneeded = vidm_previousmode + 1; } + + if (currentMenu == &OP_ScreenshotOptionsDef) + M_SetupScreenshotMenu(); } // @@ -5594,7 +5611,8 @@ static void M_DrawNightsAttackMountains(void) static INT32 bgscrollx; INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); patch_t *background = W_CachePatchName(curbgname, PU_PATCH); - INT32 x = FixedInt(bgscrollx) % SHORT(background->width); + INT16 w = SHORT(background->width); + INT32 x = FixedInt(-bgscrollx) % w; INT32 y = BASEVIDHEIGHT - SHORT(background->height)*2; if (vid.height != BASEVIDHEIGHT * dupz) @@ -5602,11 +5620,13 @@ static void M_DrawNightsAttackMountains(void) V_DrawFill(0, y+50, vid.width, BASEVIDHEIGHT, V_SNAPTOLEFT|31); V_DrawScaledPatch(x, y, V_SNAPTOLEFT, background); - x += SHORT(background->width); + x += w; if (x < BASEVIDWIDTH) V_DrawScaledPatch(x, y, V_SNAPTOLEFT, background); - bgscrollx -= (FRACUNIT/2); + bgscrollx += (FRACUNIT/2); + if (bgscrollx > w<level != gamemap || emblem->type > ET_SKIN) + continue; + if (++local > NUMHINTS*2) + break; + } + (void)choice; - SR_EmblemHintMenu[0].status = (M_SecretUnlocked(SECRET_ITEMFINDER)) ? (IT_CVAR|IT_STRING) : (IT_SECRET); + SR_EmblemHintMenu[0].status = (local > NUMHINTS*2) ? (IT_STRING | IT_ARROWS) : (IT_DISABLED); + SR_EmblemHintMenu[1].status = (M_SecretUnlocked(SECRET_ITEMFINDER)) ? (IT_CVAR|IT_STRING) : (IT_SECRET); + hintpage = 1; M_SetupNextMenu(&SR_EmblemHintDef); - itemOn = 1; // always start on back. + itemOn = 2; // always start on back. } static void M_DrawEmblemHints(void) { - INT32 i, j = 0, x, y; - UINT32 collected = 0, local = 0; + INT32 i, j = 0, x, y, left_hints = NUMHINTS, pageflag = 0; + UINT32 collected = 0, totalemblems = 0, local = 0; emblem_t *emblem; const char *hint; @@ -7243,13 +7278,35 @@ static void M_DrawEmblemHints(void) emblem = &emblemlocations[i]; if (emblem->level != gamemap || emblem->type > ET_SKIN) continue; - if (++local >= NUMHINTS*2) - break; + + local++; } x = (local > NUMHINTS ? 4 : 12); y = 8; + if (local > NUMHINTS){ + if (local > ((hintpage-1)*NUMHINTS*2) && local < ((hintpage)*NUMHINTS*2)){ + if (NUMHINTS % 2 == 1) + left_hints = (local - ((hintpage-1)*NUMHINTS*2) + 1) / 2; + else + left_hints = (local - ((hintpage-1)*NUMHINTS*2)) / 2; + }else{ + left_hints = NUMHINTS; + } + } + + if (local > NUMHINTS*2){ + if (itemOn == 0){ + pageflag = V_YELLOWMAP; + } + V_DrawString(currentMenu->x + 40, currentMenu->y + 10, pageflag, va("%d of %d",hintpage, local/(NUMHINTS*2) + 1)); + } + + // If there are more than 1 page's but less than 2 pages' worth of emblems on the last possible page, + // put half (rounded up) of the hints on the left, and half (rounded down) on the right + + if (!local) V_DrawCenteredString(160, 48, V_YELLOWMAP, "No hidden emblems on this map."); else for (i = 0; i < numemblems; i++) @@ -7258,42 +7315,80 @@ static void M_DrawEmblemHints(void) if (emblem->level != gamemap || emblem->type > ET_SKIN) continue; - if (emblem->collected) - { - collected = V_GREENMAP; - V_DrawMappedPatch(x, y+4, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH), - R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE)); - } - else - { - collected = 0; - V_DrawScaledPatch(x, y+4, 0, W_CachePatchName("NEEDIT", PU_PATCH)); - } + totalemblems++; - if (emblem->hint[0]) - hint = emblem->hint; - else - hint = M_GetText("No hint available for this emblem."); - hint = V_WordWrap(40, BASEVIDWIDTH-12, 0, hint); - if (local > NUMHINTS) - V_DrawThinString(x+28, y, V_RETURN8|V_ALLOWLOWERCASE|collected, hint); - else - V_DrawString(x+28, y, V_RETURN8|V_ALLOWLOWERCASE|collected, hint); + if (totalemblems >= ((hintpage-1)*(NUMHINTS*2) + 1) && totalemblems < (hintpage*NUMHINTS*2)+1){ - y += 28; + if (emblem->collected) + { + collected = V_GREENMAP; + V_DrawMappedPatch(x, y+4, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH), + R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE)); + } + else + { + collected = 0; + V_DrawScaledPatch(x, y+4, 0, W_CachePatchName("NEEDIT", PU_PATCH)); + } - if (++j == NUMHINTS) - { - x = 4+(BASEVIDWIDTH/2); - y = 8; + if (emblem->hint[0]) + hint = emblem->hint; + else + hint = M_GetText("No hint available for this emblem."); + hint = V_WordWrap(40, BASEVIDWIDTH-12, 0, hint); + //always draw tiny if we have more than NUMHINTS*2, visually more appealing + if (local > NUMHINTS) + V_DrawThinString(x+28, y, V_RETURN8|V_ALLOWLOWERCASE|collected, hint); + else + V_DrawString(x+28, y, V_RETURN8|V_ALLOWLOWERCASE|collected, hint); + + y += 28; + + // If there are more than 1 page's but less than 2 pages' worth of emblems on the last possible page, + // put half (rounded up) of the hints on the left, and half (rounded down) on the right + + if (++j == left_hints) + { + x = 4+(BASEVIDWIDTH/2); + y = 8; + } + else if (j >= NUMHINTS*2) + break; } - else if (j >= NUMHINTS*2) - break; } M_DrawGenericMenu(); } + +static void M_HandleEmblemHints(INT32 choice) +{ + INT32 i; + emblem_t *emblem; + UINT32 stageemblems = 0; + + for (i = 0; i < numemblems; i++) + { + emblem = &emblemlocations[i]; + if (emblem->level != gamemap || emblem->type > ET_SKIN) + continue; + + stageemblems++; + } + + + if (choice == 0){ + if (hintpage > 1){ + hintpage--; + } + }else{ + if (hintpage < ((stageemblems-1)/(NUMHINTS*2) + 1)){ + hintpage++; + } + } + +} + /*static void M_DrawSkyRoom(void) { INT32 i, y = 0; @@ -10626,8 +10721,8 @@ static void M_ServerOptions(INT32 choice) OP_ServerOptionsMenu[ 2].status = IT_GRAYEDOUT; // Max players OP_ServerOptionsMenu[ 3].status = IT_GRAYEDOUT; // Allow add-on downloading OP_ServerOptionsMenu[ 4].status = IT_GRAYEDOUT; // Allow players to join - OP_ServerOptionsMenu[34].status = IT_GRAYEDOUT; // Master server - OP_ServerOptionsMenu[35].status = IT_GRAYEDOUT; // Attempts to resynchronise + OP_ServerOptionsMenu[35].status = IT_GRAYEDOUT; // Master server + OP_ServerOptionsMenu[36].status = IT_GRAYEDOUT; // Attempts to resynchronise } else { @@ -10635,10 +10730,10 @@ static void M_ServerOptions(INT32 choice) OP_ServerOptionsMenu[ 2].status = IT_STRING | IT_CVAR; OP_ServerOptionsMenu[ 3].status = IT_STRING | IT_CVAR; OP_ServerOptionsMenu[ 4].status = IT_STRING | IT_CVAR; - OP_ServerOptionsMenu[34].status = (netgame + OP_ServerOptionsMenu[35].status = (netgame ? IT_GRAYEDOUT : (IT_STRING | IT_CVAR | IT_CV_STRING)); - OP_ServerOptionsMenu[35].status = IT_STRING | IT_CVAR; + OP_ServerOptionsMenu[36].status = IT_STRING | IT_CVAR; } #endif @@ -10830,7 +10925,6 @@ static void M_HandleConnectIP(INT32 choice) default: // otherwise do nothing. break; } - break; // don't check for typed keys } if (l >= 28-1) @@ -11334,9 +11428,27 @@ static void M_ScreenshotOptions(INT32 choice) Screenshot_option_Onchange(); Moviemode_mode_Onchange(); + M_SetupScreenshotMenu(); M_SetupNextMenu(&OP_ScreenshotOptionsDef); } +static void M_SetupScreenshotMenu(void) +{ + menuitem_t *item = &OP_ScreenshotOptionsMenu[op_screenshot_colorprofile]; + +#ifdef HWRENDER + // Hide some options based on render mode + if (rendermode == render_opengl) + { + item->status = IT_GRAYEDOUT; + if ((currentMenu == &OP_ScreenshotOptionsDef) && (itemOn == op_screenshot_colorprofile)) // Can't select that + itemOn = op_screenshot_storagelocation; + } + else +#endif + item->status = (IT_STRING | IT_CVAR); +} + // ============= // JOYSTICK MENU // ============= @@ -12279,6 +12391,15 @@ static void M_HandleVideoMode(INT32 ch) static void M_DrawScreenshotMenu(void) { M_DrawGenericScrollMenu(); +#ifdef HWRENDER + if ((rendermode == render_opengl) && (itemOn < 7)) // where it starts to go offscreen; change this number if you change the layout of the screenshot menu + { + INT32 y = currentMenu->y+currentMenu->menuitems[op_screenshot_colorprofile].alphaKey*2; + if (itemOn == 6) + y -= 10; + V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, y, V_REDMAP, "Yes"); + } +#endif } // =============== diff --git a/src/m_menu.h b/src/m_menu.h index a563a18dc..862303426 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -322,6 +322,9 @@ typedef struct menu_s void M_SetupNextMenu(menu_t *menudef); void M_ClearMenus(boolean callexitmenufunc); +// Maybe this goes here????? Who knows. +boolean M_MouseNeeded(void); + extern menu_t *currentMenu; extern menu_t MainDef; diff --git a/src/m_misc.c b/src/m_misc.c index a5091c257..d762712b6 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -1594,16 +1594,19 @@ boolean M_ScreenshotResponder(event_t *ev) // M_StartupLocale. // Sets up gettext to translate SRB2's strings. #ifdef GETTEXT -#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) -#define GETTEXTDOMAIN1 "/usr/share/locale" -#define GETTEXTDOMAIN2 "/usr/local/share/locale" -#elif defined (_WIN32) -#define GETTEXTDOMAIN1 "." -#endif + #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) + #define GETTEXTDOMAIN1 "/usr/share/locale" + #define GETTEXTDOMAIN2 "/usr/local/share/locale" + #elif defined (_WIN32) + #define GETTEXTDOMAIN1 "." + #endif +#endif // GETTEXT void M_StartupLocale(void) { +#ifdef GETTEXT char *textdomhandle = NULL; +#endif //GETTEXT CONS_Printf("M_StartupLocale...\n"); @@ -1612,6 +1615,7 @@ void M_StartupLocale(void) // Do not set numeric locale as that affects atof setlocale(LC_NUMERIC, "C"); +#ifdef GETTEXT // FIXME: global name define anywhere? #ifdef GETTEXTDOMAIN1 textdomhandle = bindtextdomain("srb2", GETTEXTDOMAIN1); @@ -1632,8 +1636,8 @@ void M_StartupLocale(void) textdomain("srb2"); else CONS_Printf("Could not find locale text domain!\n"); +#endif //GETTEXT } -#endif // ========================================================================== // MISC STRING FUNCTIONS @@ -2571,3 +2575,58 @@ void M_MkdirEach(const char *path, int start, int mode) { M_MkdirEachUntil(path, start, -1, mode); } + +int M_JumpWord(const char *line) +{ + int c; + + c = line[0]; + + if (isspace(c)) + return strspn(line, " "); + else if (ispunct(c)) + return strspn(line, PUNCTUATION); + else + { + if (isspace(line[1])) + return 1 + strspn(&line[1], " "); + else + return strcspn(line, " "PUNCTUATION); + } +} + +int M_JumpWordReverse(const char *line, int offset) +{ + int (*is)(int); + int c; + c = line[--offset]; + if (isspace(c)) + is = isspace; + else if (ispunct(c)) + is = ispunct; + else + is = isalnum; + c = (*is)(line[offset]); + while (offset > 0 && + (*is)(line[offset - 1]) == c) + offset--; + return offset; +} + +const char * M_Ftrim (double f) +{ + static char dig[9];/* "0." + 6 digits (6 is printf's default) */ + int i; + /* I know I said it's the default, but just in case... */ + sprintf(dig, "%.6f", fabs(modf(f, &f))); + /* trim trailing zeroes */ + for (i = strlen(dig)-1; dig[i] == '0'; --i) + ; + if (dig[i] == '.')/* :NOTHING: */ + return ""; + else + { + dig[i + 1] = '\0'; + return &dig[1];/* skip the 0 */ + } +} diff --git a/src/m_misc.h b/src/m_misc.h index e0a73e0b7..ad12517c1 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -101,6 +101,20 @@ boolean M_IsPathAbsolute (const char *path); void M_MkdirEach (const char *path, int start, int mode); void M_MkdirEachUntil (const char *path, int start, int end, int mode); +/* Return offset to the first word in a string. */ +/* E.g. cursor += M_JumpWord(line + cursor); */ +int M_JumpWord (const char *s); + +/* Return index of the last word behind offset bytes in a string. */ +/* E.g. cursor = M_JumpWordReverse(line, cursor); */ +int M_JumpWordReverse (const char *line, int offset); + +/* +Return dot and then the fractional part of a float, without +trailing zeros, or "" if the fractional part is zero. +*/ +const char * M_Ftrim (double); + // counting bits, for weapon ammo code, usually FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size); diff --git a/src/mserv.c b/src/mserv.c index ddabdb6c0..4ddab05b9 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -323,13 +323,9 @@ static INT32 GetServersList(void) // // MS_Connect() // -static INT32 MS_Connect(const char *ip_addr, const char *str_port, INT32 async) +#ifndef NONET +static INT32 MS_SubConnect(const char *ip_addr, const char *str_port, INT32 async, struct sockaddr *bindaddr, socklen_t bindaddrlen) { -#ifdef NONET - (void)ip_addr; - (void)str_port; - (void)async; -#else struct my_addrinfo *ai, *runp, hints; int gaie; @@ -356,50 +352,100 @@ static INT32 MS_Connect(const char *ip_addr, const char *str_port, INT32 async) socket_fd = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (socket_fd != (SOCKET_TYPE)ERRSOCKET) { - if (async) // do asynchronous connection + if (!bindaddr || bind(socket_fd, bindaddr, bindaddrlen) == 0) { + if (async) // do asynchronous connection + { #ifdef FIONBIO #ifdef WATTCP - char res = 1; + char res = 1; #else - unsigned long res = 1; + unsigned long res = 1; #endif - ioctl(socket_fd, FIONBIO, &res); + ioctl(socket_fd, FIONBIO, &res); #endif - if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) == ERRSOCKET) - { -#ifdef _WIN32 // humm, on win32/win64 it doesn't work with EINPROGRESS (stupid windows) - if (WSAGetLastError() != WSAEWOULDBLOCK) -#else - if (errno != EINPROGRESS) -#endif + if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) == ERRSOCKET) { - con_state = MSCS_FAILED; - CloseConnection(); - I_freeaddrinfo(ai); - return MS_CONNECT_ERROR; +#ifdef _WIN32 // humm, on win32/win64 it doesn't work with EINPROGRESS (stupid windows) + if (WSAGetLastError() != WSAEWOULDBLOCK) +#else + if (errno != EINPROGRESS) +#endif + { + con_state = MSCS_FAILED; + CloseConnection(); + I_freeaddrinfo(ai); + return MS_CONNECT_ERROR; + } } + con_state = MSCS_WAITING; + FD_ZERO(&wset); + FD_SET(socket_fd, &wset); + select_timeout.tv_sec = 0, select_timeout.tv_usec = 0; + I_freeaddrinfo(ai); + return 0; + } + else if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) != ERRSOCKET) + { + I_freeaddrinfo(ai); + return 0; } - con_state = MSCS_WAITING; - FD_ZERO(&wset); - FD_SET(socket_fd, &wset); - select_timeout.tv_sec = 0, select_timeout.tv_usec = 0; - I_freeaddrinfo(ai); - return 0; } - else if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) != ERRSOCKET) + close(socket_fd); + } + runp = runp->ai_next; + } + I_freeaddrinfo(ai); + return MS_CONNECT_ERROR; +} +#endif/*NONET xd*/ + +static INT32 MS_Connect(const char *ip_addr, const char *str_port, INT32 async) +{ +#ifdef NONET + (void)ip_addr; + (void)str_port; + (void)async; + return MS_CONNECT_ERROR; +#else + const char *lhost; + struct my_addrinfo hints; + struct my_addrinfo *ai, *aip; + int c; + if (M_CheckParm("-bindaddr") && ( lhost = M_GetNextParm() )) + { + memset (&hints, 0x00, sizeof(hints)); +#ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; +#endif + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if (( c = I_getaddrinfo(lhost, 0, &hints, &ai) ) != 0) + { + CONS_Printf( + "mserv.c: bind to %s: %s\n", + lhost, + gai_strerror(c)); + return MS_GETHOSTBYNAME_ERROR; + } + for (aip = ai; aip; aip = aip->ai_next) + { + c = MS_SubConnect(ip_addr, str_port, async, aip->ai_addr, aip->ai_addrlen); + if (c == 0) { I_freeaddrinfo(ai); return 0; } } - runp = runp->ai_next; + I_freeaddrinfo(ai); + return c; } - I_freeaddrinfo(ai); -#endif - return MS_CONNECT_ERROR; + else + return MS_SubConnect(ip_addr, str_port, async, 0, 0); +#endif/*NONET xd*/ } #define NUM_LIST_SERVER MAXSERVERLIST diff --git a/src/p_enemy.c b/src/p_enemy.c index b7ccdb01b..9b7393cd8 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -747,6 +747,9 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed if (player->bot) continue; // ignore bots + if (player->quittime) + continue; // Ignore uncontrolled bodies + if (dist > 0 && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist) continue; // Too far away @@ -5178,6 +5181,8 @@ void A_SignPlayer(mobj_t *actor) if (signcolor) ; + else if (!skin->sprites[SPR2_SIGN].numframes) + signcolor = facecolor; else if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor? signcolor = skin->prefoppositecolor; else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor. @@ -5188,33 +5193,25 @@ void A_SignPlayer(mobj_t *actor) else if (locvar1 != -3) // set to a defined skin { // I turned this function into a fucking mess. I'm so sorry. -Lach - if (locvar1 == -2) // next skin + if (locvar1 == -2) // random skin { +#define skincheck(num) (player ? !R_SkinUsable(player-players, num) : skins[num].availability > 0) player_t *player = actor->target ? actor->target->player : NULL; UINT8 skinnum; -#define skincheck(num) (player ? !R_SkinUsable(player-players, num) : skins[num].availability > 0) - if (ov->skin == NULL) // pick a random skin to start with! + UINT8 skincount = 0; + for (skinnum = 0; skinnum < numskins; skinnum++) + if (!skincheck(skinnum)) + skincount++; + skinnum = P_RandomKey(skincount); + for (skincount = 0; skincount < numskins; skincount++) { - UINT8 skincount = 0; - for (skincount = 0; skincount < numskins; skincount++) - if (!skincheck(skincount)) - skincount++; - skinnum = P_RandomKey(skincount); - for (skincount = 0; skincount < numskins; skincount++) - { - if (skincount > skinnum) - break; - if (skincheck(skincount)) - skinnum++; - } + if (skincount > skinnum) + break; + if (skincheck(skincount)) + skinnum++; } - else // otherwise, advance 1 skin - { - skinnum = (skin_t*)ov->skin-skins; - while ((skinnum = (skinnum + 1) % numskins) && skincheck(skinnum)); - } -#undef skincheck skin = &skins[skinnum]; +#undef skincheck } else // specific skin skin = &skins[locvar1]; @@ -5222,22 +5219,37 @@ void A_SignPlayer(mobj_t *actor) facecolor = skin->prefcolor; if (signcolor) ; + else if (!skin->sprites[SPR2_SIGN].numframes) + signcolor = facecolor; else if (skin->prefoppositecolor) signcolor = skin->prefoppositecolor; else if (facecolor) signcolor = Color_Opposite[facecolor - 1][0]; } - if (skin && skin->sprites[SPR2_SIGN].numframes) // player face + if (skin) { - ov->color = facecolor; - ov->skin = skin; - P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN + if (skin->sprites[SPR2_SIGN].numframes) // player face + { + ov->color = facecolor; + ov->skin = skin; + if ((statenum_t)(ov->state-states) != actor->info->seestate) + P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN + } + else // CLEAR! sign + { + ov->color = SKINCOLOR_NONE; + ov->skin = NULL; // needs to be NULL in the case of SF_HIRES characters + if ((statenum_t)(ov->state-states) != actor->info->missilestate) + P_SetMobjState(ov, actor->info->missilestate); // S_CLEARSIGN + } } else // Eggman face { ov->color = SKINCOLOR_NONE; - P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN + ov->skin = NULL; + if ((statenum_t)(ov->state-states) != actor->info->meleestate) + P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN if (!signcolor) signcolor = SKINCOLOR_CARBON; } diff --git a/src/p_inter.c b/src/p_inter.c index 71740822e..667245f03 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -433,7 +433,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) || special->state == &states[S_FANG_BOUNCE4] || special->state == &states[S_FANG_PINCHBOUNCE3] || special->state == &states[S_FANG_PINCHBOUNCE4]) - && P_MobjFlip(special)*((special->z + special->height/2) - (toucher->z - toucher->height/2)) > (special->height/4)) + && P_MobjFlip(special)*((special->z + special->height/2) - (toucher->z + toucher->height/2)) > (toucher->height/2)) { P_DamageMobj(toucher, special, special, 1, 0); P_SetTarget(&special->tracer, toucher); @@ -1428,98 +1428,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Misc touchables // // *************** // case MT_STARPOST: - if (player->bot) - return; - // In circuit, player must have touched all previous starposts - if (circuitmap - && special->health - player->starpostnum > 1) - { - // blatant reuse of a variable that's normally unused in circuit - if (!player->tossdelay) - S_StartSound(toucher, sfx_lose); - player->tossdelay = 3; - return; - } - - // We could technically have 91.1 Star Posts. 90 is cleaner. - if (special->health > 90) - { - CONS_Debug(DBG_GAMELOGIC, "Bad Starpost Number!\n"); - return; - } - - if (player->starpostnum >= special->health) - return; // Already hit this post - - if (cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (netgame || multiplayer)) - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - if (players[i].bot) // ignore dumb, stupid tails - continue; - - players[i].starposttime = leveltime; - players[i].starpostx = player->mo->x>>FRACBITS; - players[i].starposty = player->mo->y>>FRACBITS; - players[i].starpostz = special->z>>FRACBITS; - players[i].starpostangle = special->angle; - players[i].starpostscale = player->mo->destscale; - if (special->flags2 & MF2_OBJECTFLIP) - { - players[i].starpostscale *= -1; - players[i].starpostz += special->height>>FRACBITS; - } - players[i].starpostnum = special->health; - - if (cv_coopstarposts.value == 2 && (players[i].playerstate == PST_DEAD || players[i].spectator) && P_GetLives(&players[i])) - P_SpectatorJoinGame(&players[i]); //players[i].playerstate = PST_REBORN; - } - } - S_StartSound(NULL, special->info->painsound); - } - else - { - // Save the player's time and position. - player->starposttime = leveltime; - player->starpostx = toucher->x>>FRACBITS; - player->starposty = toucher->y>>FRACBITS; - player->starpostz = special->z>>FRACBITS; - player->starpostangle = special->angle; - player->starpostscale = player->mo->destscale; - if (special->flags2 & MF2_OBJECTFLIP) - { - player->starpostscale *= -1; - player->starpostz += special->height>>FRACBITS; - } - player->starpostnum = special->health; - S_StartSound(toucher, special->info->painsound); - } - - P_ClearStarPost(special->health); - - // Find all starposts in the level with this value - INCLUDING this one! - if (!(netgame && circuitmap && player != &players[consoleplayer])) - { - thinker_t *th; - mobj_t *mo2; - - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo2 = (mobj_t *)th; - - if (mo2->type != MT_STARPOST) - continue; - if (mo2->health != special->health) - continue; - - P_SetMobjState(mo2, mo2->info->painstate); - } - } + P_TouchStarPost(special, player, special->spawnpoint && (special->spawnpoint->options & MTF_OBJECTSPECIAL)); return; case MT_FAKEMOBILE: @@ -1869,6 +1778,113 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) S_StartSound(toucher, special->info->deathsound); // was NULL, but changed to player so you could hear others pick up rings P_KillMobj(special, NULL, toucher, 0); + special->shadowscale = 0; +} + +/** Saves a player's level progress at a star post + * + * \param post The star post to trigger + * \param player The player that should receive the checkpoint + * \param snaptopost If true, the respawn point will use the star post's position, otherwise player x/y and star post z + */ +void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost) +{ + size_t i; + mobj_t *toucher = player->mo; + mobj_t *checkbase = snaptopost ? post : toucher; + + if (player->bot) + return; + // In circuit, player must have touched all previous starposts + if (circuitmap + && post->health - player->starpostnum > 1) + { + // blatant reuse of a variable that's normally unused in circuit + if (!player->tossdelay) + S_StartSound(toucher, sfx_lose); + player->tossdelay = 3; + return; + } + + // With the parameter + angle setup, we can go up to 1365 star posts. Who needs that many? + if (post->health > 1365) + { + CONS_Debug(DBG_GAMELOGIC, "Bad Starpost Number!\n"); + return; + } + + if (player->starpostnum >= post->health) + return; // Already hit this post + + if (cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (netgame || multiplayer)) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (players[i].bot) // ignore dumb, stupid tails + continue; + + players[i].starposttime = leveltime; + players[i].starpostx = checkbase->x>>FRACBITS; + players[i].starposty = checkbase->y>>FRACBITS; + players[i].starpostz = post->z>>FRACBITS; + players[i].starpostangle = post->angle; + players[i].starpostscale = player->mo->destscale; + if (post->flags2 & MF2_OBJECTFLIP) + { + players[i].starpostscale *= -1; + players[i].starpostz += post->height>>FRACBITS; + } + players[i].starpostnum = post->health; + + if (cv_coopstarposts.value == 2 && (players[i].playerstate == PST_DEAD || players[i].spectator) && P_GetLives(&players[i])) + P_SpectatorJoinGame(&players[i]); //players[i].playerstate = PST_REBORN; + } + } + S_StartSound(NULL, post->info->painsound); + } + else + { + // Save the player's time and position. + player->starposttime = leveltime; + player->starpostx = checkbase->x>>FRACBITS; + player->starposty = checkbase->y>>FRACBITS; + player->starpostz = post->z>>FRACBITS; + player->starpostangle = post->angle; + player->starpostscale = player->mo->destscale; + if (post->flags2 & MF2_OBJECTFLIP) + { + player->starpostscale *= -1; + player->starpostz += post->height>>FRACBITS; + } + player->starpostnum = post->health; + S_StartSound(toucher, post->info->painsound); + } + + P_ClearStarPost(post->health); + + // Find all starposts in the level with this value - INCLUDING this one! + if (!(netgame && circuitmap && player != &players[consoleplayer])) + { + thinker_t *th; + mobj_t *mo2; + + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo2 = (mobj_t *)th; + + if (mo2->type != MT_STARPOST) + continue; + if (mo2->health != post->health) + continue; + + P_SetMobjState(mo2, mo2->info->painstate); + } + } } /** Prints death messages relating to a dying or hit player. @@ -2256,9 +2272,9 @@ void P_CheckSurvivors(void) { if (players[i].spectator) spectators++; - else if (players[i].pflags & PF_TAGIT) + else if ((players[i].pflags & PF_TAGIT) && players[i].quittime < 30 * TICRATE) taggers++; - else if (!(players[i].pflags & PF_GAMETYPEOVER)) + else if (!(players[i].pflags & PF_GAMETYPEOVER) && players[i].quittime < 30 * TICRATE) { survivorarray[survivors] = i; survivors++; diff --git a/src/p_local.h b/src/p_local.h index a5f3d313c..8056d137c 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -486,6 +486,7 @@ void P_PlayerWeaponPanelOrAmmoBurst(player_t *player); void P_PlayerEmeraldBurst(player_t *player, boolean toss); void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck); +void P_TouchStarPost(mobj_t *starpost, player_t *player, boolean snaptopost); void P_PlayerFlagBurst(player_t *player, boolean toss); void P_CheckTimeLimit(void); void P_CheckPointLimit(void); diff --git a/src/p_maputl.c b/src/p_maputl.c index afa020504..b4043d643 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -643,7 +643,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) delta1 = abs(mobj->z - (bottomheight + ((topheight - bottomheight)/2))); delta2 = abs(thingtop - (bottomheight + ((topheight - bottomheight)/2))); - if (delta1 >= delta2 && (rover->flags & FF_INTANGABLEFLATS) != FF_PLATFORM) // thing is below FOF + if (delta1 >= delta2 && (rover->flags & FF_INTANGIBLEFLATS) != FF_PLATFORM) // thing is below FOF { if (bottomheight < opentop) { opentop = bottomheight; @@ -656,7 +656,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) highceiling = bottomheight; } - if (delta1 < delta2 && (rover->flags & FF_INTANGABLEFLATS) != FF_REVERSEPLATFORM) // thing is above FOF + if (delta1 < delta2 && (rover->flags & FF_INTANGIBLEFLATS) != FF_REVERSEPLATFORM) // thing is above FOF { if (topheight > openbottom) { openbottom = topheight; @@ -689,7 +689,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) delta1 = abs(mobj->z - (bottomheight + ((topheight - bottomheight)/2))); delta2 = abs(thingtop - (bottomheight + ((topheight - bottomheight)/2))); - if (delta1 >= delta2 && (rover->flags & FF_INTANGABLEFLATS) != FF_PLATFORM) // thing is below FOF + if (delta1 >= delta2 && (rover->flags & FF_INTANGIBLEFLATS) != FF_PLATFORM) // thing is below FOF { if (bottomheight < opentop) { opentop = bottomheight; @@ -702,7 +702,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) highceiling = bottomheight; } - if (delta1 < delta2 && (rover->flags & FF_INTANGABLEFLATS) != FF_REVERSEPLATFORM) // thing is above FOF + if (delta1 < delta2 && (rover->flags & FF_INTANGIBLEFLATS) != FF_REVERSEPLATFORM) // thing is above FOF { if (topheight > openbottom) { openbottom = topheight; diff --git a/src/p_mobj.c b/src/p_mobj.c index a4231fa74..10898f70e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2350,6 +2350,7 @@ static void P_RingZMovement(mobj_t *mo) if (mo->eflags & MFE_APPLYPMOMZ && !P_IsObjectOnGround(mo)) { mo->momz += mo->pmomz; + mo->pmomz = 0; mo->eflags &= ~MFE_APPLYPMOMZ; } mo->z += mo->momz; @@ -2419,6 +2420,7 @@ static boolean P_ZMovement(mobj_t *mo) if (mo->eflags & MFE_APPLYPMOMZ && !P_IsObjectOnGround(mo)) { mo->momz += mo->pmomz; + mo->pmomz = 0; mo->eflags &= ~MFE_APPLYPMOMZ; } mo->z += mo->momz; @@ -2907,6 +2909,7 @@ static void P_PlayerZMovement(mobj_t *mo) if (mo->eflags & MFE_APPLYPMOMZ && !P_IsObjectOnGround(mo)) { mo->momz += mo->pmomz; + mo->pmomz = 0; mo->eflags &= ~MFE_APPLYPMOMZ; } @@ -3157,6 +3160,7 @@ static boolean P_SceneryZMovement(mobj_t *mo) if (mo->eflags & MFE_APPLYPMOMZ && !P_IsObjectOnGround(mo)) { mo->momz += mo->pmomz; + mo->pmomz = 0; mo->eflags &= ~MFE_APPLYPMOMZ; } mo->z += mo->momz; @@ -3396,7 +3400,7 @@ void P_MobjCheckWater(mobj_t *mobj) if (!((p->powers[pw_super]) || (p->powers[pw_invulnerability]))) { boolean electric = !!(p->powers[pw_shield] & SH_PROTECTELECTRIC); - if (electric || ((p->powers[pw_shield] & SH_PROTECTFIRE) && !(p->powers[pw_shield] & SH_PROTECTWATER))) + if (electric || ((p->powers[pw_shield] & SH_PROTECTFIRE) && !(p->powers[pw_shield] & SH_PROTECTWATER) && !(mobj->eflags & MFE_TOUCHLAVA))) { // Water removes electric and non-water fire shields... P_FlashPal(p, electric @@ -3902,11 +3906,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->powers[pw_carry] == CR_MACESPIN) { P_CheckPosition(mobj, mobj->x, mobj->y); + mobj->floorz = tmfloorz; + mobj->ceilingz = tmceilingz; goto animonly; } } @@ -10453,6 +10461,61 @@ void P_SceneryThinker(mobj_t *mobj) // GAME SPAWN FUNCTIONS // +static fixed_t P_DefaultMobjShadowScale (mobj_t *thing) +{ + switch (thing->type) + { + case MT_PLAYER: + case MT_ROLLOUTROCK: + + case MT_EGGMOBILE4_MACE: + case MT_SMALLMACE: + case MT_BIGMACE: + + case MT_SMALLGRABCHAIN: + case MT_BIGGRABCHAIN: + + case MT_YELLOWSPRINGBALL: + case MT_REDSPRINGBALL: + + return FRACUNIT; + + case MT_RING: + case MT_FLINGRING: + + case MT_BLUESPHERE: + case MT_FLINGBLUESPHERE: + case MT_BOMBSPHERE: + + case MT_REDTEAMRING: + case MT_BLUETEAMRING: + case MT_REDFLAG: + case MT_BLUEFLAG: + + case MT_EMBLEM: + + case MT_TOKEN: + case MT_EMERALD1: + case MT_EMERALD2: + case MT_EMERALD3: + case MT_EMERALD4: + case MT_EMERALD5: + case MT_EMERALD6: + case MT_EMERALD7: + case MT_EMERHUNT: + case MT_FLINGEMERALD: + + return 2*FRACUNIT/3; + + default: + + if (thing->flags & (MF_ENEMY|MF_BOSS)) + return FRACUNIT; + else + return 0; + } +} + // // P_SpawnMobj // @@ -10553,6 +10616,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) else mobj->z = z; + // Set shadowscale here, before spawn hook so that Lua can change it + mobj->shadowscale = 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! @@ -11094,7 +11160,7 @@ void P_SpawnPrecipitation(void) x = basex + ((M_RandomKey(MAPBLOCKUNITS<<3)<>3); y = basey + ((M_RandomKey(MAPBLOCKUNITS<<3)<>3); - precipsector = R_IsPointInSubsector(x, y); + precipsector = R_PointInSubsectorOrNull(x, y); // No sector? Stop wasting time, // move on to the next entry in the blockmap @@ -12911,7 +12977,16 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean thinker_t* th; mobj_t* mo2; boolean foundanother = false; - mobj->health = (mthing->angle/360) + 1; + + if (mthing->extrainfo) + // Allow thing Parameter to define star post num too! + // For starposts above param 15 (the 16th), add 360 to the angle like before and start parameter from 1 (NOT 0)! + // So the 16th starpost is angle=0 param=15, the 17th would be angle=360 param=1. + // This seems more intuitive for mappers to use until UDMF is ready, since most SP maps won't have over 16 consecutive star posts. + mobj->health = mthing->extrainfo + (mthing->angle/360)*15 + 1; + else + // Old behavior if Parameter is 0; add 360 to the angle for each consecutive star post. + mobj->health = (mthing->angle/360) + 1; // See if other starposts exist in this level that have the same value. for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) diff --git a/src/p_mobj.h b/src/p_mobj.h index 92160d9e2..fd0c95a56 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -375,6 +375,7 @@ typedef struct mobj_s #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 // WARNING: New fields must be added separately to savegame and Lua. } mobj_t; diff --git a/src/p_polyobj.c b/src/p_polyobj.c index cd0a44bb4..a206d0171 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -1001,15 +1001,35 @@ static void Polyobj_pushThing(polyobj_t *po, line_t *line, mobj_t *mo) // static void Polyobj_slideThing(mobj_t *mo, fixed_t dx, fixed_t dy) { - if (mo->player) { // Do something similar to conveyor movement. -Red - mo->player->cmomx += dx; - mo->player->cmomy += dy; + if (mo->player) { // Finally this doesn't suck eggs -fickle + fixed_t cdx, cdy; - dx = FixedMul(dx, CARRYFACTOR); - dy = FixedMul(dy, CARRYFACTOR); + cdx = FixedMul(dx, FRACUNIT-CARRYFACTOR); + cdy = FixedMul(dy, FRACUNIT-CARRYFACTOR); - mo->player->cmomx -= dx; - mo->player->cmomy -= dy; + if (mo->player->onconveyor == 1) + { + mo->momx += cdx; + mo->momy += cdy; + + // Multiple slides in the same tic, somehow + mo->player->cmomx += cdx; + mo->player->cmomy += cdy; + } + else + { + if (mo->player->onconveyor == 3) + { + mo->momx += cdx - mo->player->cmomx; + mo->momy += cdy - mo->player->cmomy; + } + + mo->player->cmomx = cdx; + mo->player->cmomy = cdy; + } + + dx = FixedMul(dx, FRACUNIT - mo->friction); + dy = FixedMul(dy, FRACUNIT - mo->friction); if (mo->player->pflags & PF_SPINNING && (mo->player->rmomx || mo->player->rmomy) && !(mo->player->pflags & PF_STARTDASH)) { #define SPINMULT 5184 // Consider this a substitute for properly calculating FRACUNIT-friction. I'm tired. -Red @@ -1282,7 +1302,8 @@ static void Polyobj_rotateThings(polyobj_t *po, vertex_t origin, angle_t delta, { static INT32 pomovecount = 10000; INT32 x, y; - angle_t deltafine = delta >> ANGLETOFINESHIFT; + angle_t deltafine = (((po->angle + delta) >> ANGLETOFINESHIFT) - (po->angle >> ANGLETOFINESHIFT)) & FINEMASK; + // This fineshift trickery replaces the old delta>>ANGLETOFINESHIFT; doing it this way avoids loss of precision causing objects to slide off -fickle pomovecount++; @@ -1334,19 +1355,10 @@ static void Polyobj_rotateThings(polyobj_t *po, vertex_t origin, angle_t delta, oldxoff = mo->x-origin.x; oldyoff = mo->y-origin.y; - if (mo->player) // Hack to fix players sliding off of spinning polys -Red - { - fixed_t temp; + newxoff = FixedMul(oldxoff, c)-FixedMul(oldyoff, s) - oldxoff; + newyoff = FixedMul(oldyoff, c)+FixedMul(oldxoff, s) - oldyoff; - temp = FixedMul(oldxoff, c)-FixedMul(oldyoff, s); - oldyoff = FixedMul(oldyoff, c)+FixedMul(oldxoff, s); - oldxoff = temp; - } - - newxoff = FixedMul(oldxoff, c)-FixedMul(oldyoff, s); - newyoff = FixedMul(oldyoff, c)+FixedMul(oldxoff, s); - - Polyobj_slideThing(mo, newxoff-oldxoff, newyoff-oldyoff); + Polyobj_slideThing(mo, newxoff, newyoff); if (turnthings == 2 || (turnthings == 1 && !mo->player)) { mo->angle += delta; diff --git a/src/p_saveg.c b/src/p_saveg.c index 2b6a474bf..f0106863b 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -116,6 +116,7 @@ static void P_NetArchivePlayers(void) WRITEANGLE(save_p, players[i].aiming); WRITEANGLE(save_p, players[i].drawangle); + WRITEANGLE(save_p, players[i].viewrollangle); WRITEANGLE(save_p, players[i].awayviewaiming); WRITEINT32(save_p, players[i].awayviewtics); WRITEINT16(save_p, players[i].rings); @@ -255,6 +256,7 @@ static void P_NetArchivePlayers(void) WRITEINT32(save_p, players[i].onconveyor); WRITEUINT32(save_p, players[i].jointime); + WRITEUINT32(save_p, players[i].quittime); WRITEUINT16(save_p, flags); @@ -325,6 +327,7 @@ static void P_NetUnArchivePlayers(void) players[i].aiming = READANGLE(save_p); players[i].drawangle = READANGLE(save_p); + players[i].viewrollangle = READANGLE(save_p); players[i].awayviewaiming = READANGLE(save_p); players[i].awayviewtics = READINT32(save_p); players[i].rings = READINT16(save_p); @@ -446,6 +449,7 @@ static void P_NetUnArchivePlayers(void) players[i].onconveyor = READINT32(save_p); players[i].jointime = READUINT32(save_p); + players[i].quittime = READUINT32(save_p); flags = READUINT16(save_p); @@ -607,7 +611,7 @@ static void P_NetArchiveColormaps(void) WRITEUINT8(save_p, exc->fadestart); WRITEUINT8(save_p, exc->fadeend); - WRITEUINT8(save_p, exc->fog); + WRITEUINT8(save_p, exc->flags); WRITEINT32(save_p, exc->rgba); WRITEINT32(save_p, exc->fadergba); @@ -637,7 +641,7 @@ static void P_NetUnArchiveColormaps(void) for (exc = net_colormaps; i < num_net_colormaps; i++, exc = exc_next) { - UINT8 fadestart, fadeend, fog; + UINT8 fadestart, fadeend, flags; INT32 rgba, fadergba; #ifdef EXTRACOLORMAPLUMPS char lumpname[9]; @@ -645,7 +649,7 @@ static void P_NetUnArchiveColormaps(void) fadestart = READUINT8(save_p); fadeend = READUINT8(save_p); - fog = READUINT8(save_p); + flags = READUINT8(save_p); rgba = READINT32(save_p); fadergba = READINT32(save_p); @@ -677,7 +681,7 @@ static void P_NetUnArchiveColormaps(void) exc->fadestart = fadestart; exc->fadeend = fadeend; - exc->fog = fog; + exc->flags = flags; exc->rgba = rgba; exc->fadergba = fadergba; @@ -687,7 +691,7 @@ static void P_NetUnArchiveColormaps(void) exc->lumpname[0] = 0; #endif - existing_exc = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, fog); + existing_exc = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, flags); if (existing_exc) exc->colormap = existing_exc->colormap; @@ -1253,6 +1257,7 @@ typedef enum #endif MD2_COLORIZED = 1<<12, MD2_ROLLANGLE = 1<<13, + MD2_SHADOWSCALE = 1<<14, } mobj_diff2_t; typedef enum @@ -1474,6 +1479,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_COLORIZED; if (mobj->rollangle) diff2 |= MD2_ROLLANGLE; + if (mobj->shadowscale) + diff2 |= MD2_SHADOWSCALE; if (diff2 != 0) diff |= MD_MORE; @@ -1640,6 +1647,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) WRITEUINT8(save_p, mobj->colorized); if (diff2 & MD2_ROLLANGLE) WRITEANGLE(save_p, mobj->rollangle); + if (diff2 & MD2_SHADOWSCALE) + WRITEFIXED(save_p, mobj->shadowscale); WRITEUINT32(save_p, mobj->mobjnum); } @@ -2719,6 +2728,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) mobj->colorized = READUINT8(save_p); if (diff2 & MD2_ROLLANGLE) mobj->rollangle = READANGLE(save_p); + if (diff2 & MD2_SHADOWSCALE) + mobj->shadowscale = READFIXED(save_p); if (diff & MD_REDFLAG) { diff --git a/src/p_setup.c b/src/p_setup.c index 20dfc633b..55490d7fa 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -222,6 +222,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) mapheaderinfo[num]->typeoflevel = 0; mapheaderinfo[num]->nextlevel = (INT16)(i + 1); mapheaderinfo[num]->startrings = 0; + mapheaderinfo[num]->keywords[0] = '\0'; snprintf(mapheaderinfo[num]->musname, 7, "%sM", G_BuildMapName(i)); mapheaderinfo[num]->musname[6] = 0; mapheaderinfo[num]->mustrack = 0; @@ -410,7 +411,7 @@ levelflat refers to an array of level flats, or NULL if we want to allocate it now. */ static INT32 -Ploadflat (levelflat_t *levelflat, const char *flatname) +Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize) { #ifndef NO_PNG_LUMPS UINT8 buffer[8]; @@ -421,31 +422,26 @@ Ploadflat (levelflat_t *levelflat, const char *flatname) size_t i; - if (levelflat) + // Scan through the already found flats, return if it matches. + for (i = 0; i < numlevelflats; i++) { - // Scan through the already found flats, return if it matches. - for (i = 0; i < numlevelflats; i++) - { - if (strnicmp(levelflat[i].name, flatname, 8) == 0) - return i; - } + if (strnicmp(levelflat[i].name, flatname, 8) == 0) + return i; } -#ifndef ZDEBUG - CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); -#endif - - if (numlevelflats >= MAXLEVELFLATS) - I_Error("Too many flats in level\n"); - - if (levelflat) - levelflat += numlevelflats; - else + if (resize) { // allocate new flat memory levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL); levelflat = levelflats + numlevelflats; } + else + { + if (numlevelflats >= MAXLEVELFLATS) + I_Error("Too many flats in level\n"); + + levelflat += numlevelflats; + } // Store the name. strlcpy(levelflat->name, flatname, sizeof (levelflat->name)); @@ -500,6 +496,10 @@ flatfound: levelflat->u.flat.baselumpnum = LUMPERROR; } +#ifndef ZDEBUG + CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name); +#endif + return ( numlevelflats++ ); } @@ -507,7 +507,7 @@ flatfound: // allocate an id for it, and set the levelflat (to speedup search) INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat) { - return Ploadflat(levelflat, flatname); + return Ploadflat(levelflat, flatname, false); } // help function for Lua and $$$.sav reading @@ -516,7 +516,7 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat) // INT32 P_AddLevelFlatRuntime(const char *flatname) { - return Ploadflat(levelflats, flatname); + return Ploadflat(levelflats, flatname, true); } // help function for $$$.sav checking @@ -846,6 +846,8 @@ static void P_LoadVertices(UINT8 *data) { v->x = SHORT(mv->x)<y = SHORT(mv->y)<floorzset = v->ceilingzset = false; + v->floorz = v->ceilingz = 0; } } @@ -1370,6 +1372,16 @@ static void ParseTextmapVertexParameter(UINT32 i, char *param, char *val) vertexes[i].x = FLOAT_TO_FIXED(atof(val)); else if (fastcmp(param, "y")) vertexes[i].y = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "zfloor")) + { + vertexes[i].floorz = FLOAT_TO_FIXED(atof(val)); + vertexes[i].floorzset = true; + } + else if (fastcmp(param, "zceiling")) + { + vertexes[i].ceilingz = FLOAT_TO_FIXED(atof(val)); + vertexes[i].ceilingzset = true; + } } static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val) @@ -1577,6 +1589,8 @@ static void P_LoadTextmap(void) { // Defaults. vt->x = vt->y = INT32_MAX; + vt->floorzset = vt->ceilingzset = false; + vt->floorz = vt->ceilingz = 0; TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter); @@ -3086,7 +3100,7 @@ static void P_InitTagGametype(void) //Also, you'd never have to loop through all 32 players slots to find anything ever again. for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && !players[i].spectator) + if (playeringame[i] && !(players[i].spectator && players[i].quittime)) { playersactive[realnumplayers] = i; //stores the player's node in the array. realnumplayers++; @@ -3108,7 +3122,7 @@ static void P_InitTagGametype(void) if (players[playersactive[i]].mo) P_RemoveMobj(players[playersactive[i]].mo); - G_SpawnPlayer(playersactive[i], false); //respawn the lucky player in his dedicated spawn location. + G_SpawnPlayer(playersactive[i]); //respawn the lucky player in his dedicated spawn location. } static void P_SetupCamera(void) @@ -3286,7 +3300,7 @@ static void P_InitPlayers(void) G_DoReborn(i); else // gametype is GT_COOP or GT_RACE { - G_SpawnPlayer(i, players[i].starposttime); + G_SpawnPlayer(i); if (players[i].starposttime) P_ClearStarPost(players[i].starpostnum); } @@ -3571,11 +3585,11 @@ boolean P_LoadLevel(boolean fromnetsave) return false; // init gravity, tag lists, - // anything that P_ResetDynamicSlopes/P_LoadThings needs to know + // anything that P_SpawnSlopes/P_LoadThings needs to know P_InitSpecials(); #ifdef ESLOPE - P_ResetDynamicSlopes(fromnetsave); + P_SpawnSlopes(fromnetsave); #endif P_SpawnMapThings(!fromnetsave); @@ -3674,8 +3688,7 @@ boolean P_LoadLevel(boolean fromnetsave) return true; // If so... - if ((!(mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD)) && (*mapheaderinfo[gamemap-1]->lvlttl != '\0')) - G_PreLevelTitleCard(); + G_PreLevelTitleCard(); return true; } diff --git a/src/p_slopes.c b/src/p_slopes.c index 2cf2c74ba..d584bb92d 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -458,8 +458,8 @@ static pslope_t *MakeViaMapthings(INT16 tag1, INT16 tag2, INT16 tag3, UINT8 flag return ret; } -/// Create vertex based slopes. -static void line_SpawnViaVertexes(const int linenum, const boolean spawnthinker) +/// Create vertex based slopes using tagged mapthings. +static void line_SpawnViaMapthingVertexes(const int linenum, const boolean spawnthinker) { line_t *line = lines + linenum; side_t *side; @@ -507,6 +507,55 @@ static void line_SpawnViaVertexes(const int linenum, const boolean spawnthinker) side->sector->hasslope = true; } +/// Spawn textmap vertex slopes. +static void SpawnVertexSlopes(void) +{ + line_t *l1, *l2; + sector_t* sc; + vertex_t *v1, *v2, *v3; + size_t i; + for (i = 0, sc = sectors; i < numsectors; i++, sc++) + { + // The vertex slopes only work for 3-vertex sectors (and thus 3-sided sectors). + if (sc->linecount != 3) + continue; + + l1 = sc->lines[0]; + l2 = sc->lines[1]; + + // Determine the vertexes. + v1 = l1->v1; + v2 = l1->v2; + if ((l2->v1 != v1) && (l2->v1 != v2)) + v3 = l2->v1; + else + v3 = l2->v2; + + if (v1->floorzset || v2->floorzset || v3->floorzset) + { + vector3_t vtx[3] = { + {v1->x, v1->y, v1->floorzset ? v1->floorz : sc->floorheight}, + {v2->x, v2->y, v2->floorzset ? v2->floorz : sc->floorheight}, + {v3->x, v3->y, v3->floorzset ? v3->floorz : sc->floorheight}}; + pslope_t *slop = Slope_Add(0); + sc->f_slope = slop; + sc->hasslope = true; + ReconfigureViaVertexes(slop, vtx[0], vtx[1], vtx[2]); + } + + if (v1->ceilingzset || v2->ceilingzset || v3->ceilingzset) + { + vector3_t vtx[3] = { + {v1->x, v1->y, v1->ceilingzset ? v1->ceilingz : sc->ceilingheight}, + {v2->x, v2->y, v2->ceilingzset ? v2->ceilingz : sc->ceilingheight}, + {v3->x, v3->y, v3->ceilingzset ? v3->ceilingz : sc->ceilingheight}}; + pslope_t *slop = Slope_Add(0); + sc->c_slope = slop; + sc->hasslope = true; + ReconfigureViaVertexes(slop, vtx[0], vtx[1], vtx[2]); + } + } +} // // P_CopySectorSlope @@ -551,12 +600,16 @@ pslope_t *P_SlopeById(UINT16 id) return ret; } -/// Reset slopes and read them from special lines. -void P_ResetDynamicSlopes(const boolean fromsave) { +/// Initializes and reads the slopes from the map data. +void P_SpawnSlopes(const boolean fromsave) { size_t i; + slopelist = NULL; slopecount = 0; + /// Generates vertex slopes. + SpawnVertexSlopes(); + /// Generates line special-defined slopes. for (i = 0; i < numlines; i++) { @@ -577,7 +630,7 @@ void P_ResetDynamicSlopes(const boolean fromsave) { case 705: case 714: case 715: - line_SpawnViaVertexes(i, !fromsave); + line_SpawnViaMapthingVertexes(i, !fromsave); break; default: diff --git a/src/p_slopes.h b/src/p_slopes.h index 96764051b..0bf03f26d 100644 --- a/src/p_slopes.h +++ b/src/p_slopes.h @@ -23,7 +23,7 @@ extern UINT16 slopecount; void P_LinkSlopeThinkers (void); void P_CalculateSlopeNormal(pslope_t *slope); -void P_ResetDynamicSlopes(const boolean fromsave); +void P_SpawnSlopes(const boolean fromsave); // // P_CopySectorSlope diff --git a/src/p_spec.c b/src/p_spec.c index c7558582a..556569be2 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3529,7 +3529,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) false, // subtract FadeA (no flag for this, just pass negative alpha) false, // subtract FadeStart (we ran out of flags) false, // subtract FadeEnd (we ran out of flags) - false, // ignore Fog (we ran out of flags) + false, // ignore Flags (we ran out of flags) line->flags & ML_DONTPEGBOTTOM, (line->flags & ML_DONTPEGBOTTOM) ? (sides[line->sidenum[0]].textureoffset >> FRACBITS) : 0, (line->flags & ML_DONTPEGBOTTOM) ? (sides[line->sidenum[0]].rowoffset >> FRACBITS) : 0, @@ -3896,7 +3896,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) false, // subtract FadeA (no flag for this, just pass negative alpha) false, // subtract FadeStart (we ran out of flags) false, // subtract FadeEnd (we ran out of flags) - false, // ignore Fog (we ran out of flags) + false, // ignore Flags (we ran out of flags) line->flags & ML_DONTPEGBOTTOM, (line->flags & ML_DONTPEGBOTTOM) ? (sides[line->sidenum[0]].textureoffset >> FRACBITS) : 0, (line->flags & ML_DONTPEGBOTTOM) ? (sides[line->sidenum[0]].rowoffset >> FRACBITS) : 0, @@ -4455,10 +4455,18 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers case 6: // Death Pit (Camera Mod) case 7: // Death Pit (No Camera Mod) if (roversector || P_MobjReadyToTrigger(player->mo, sector)) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DEATHPIT); + { + if (player->quittime) + G_MovePlayerToSpawnOrStarpost(player - players); + else + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DEATHPIT); + } break; case 8: // Instant Kill - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); + if (player->quittime) + G_MovePlayerToSpawnOrStarpost(player - players); + else + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); break; case 9: // Ring Drainer (Floor Touch) case 10: // Ring Drainer (No Floor Touch) @@ -4713,7 +4721,7 @@ DoneSection2: if (!post) break; - P_TouchSpecialThing(post, player->mo, false); + P_TouchStarPost(post, player, false); break; } @@ -6382,7 +6390,7 @@ static void P_RunLevelLoadExecutors(void) } /** Before things are loaded, initialises certain stuff in case they're needed - * by P_ResetDynamicSlopes or P_LoadThings. This was split off from + * by P_SpawnSlopes or P_LoadThings. This was split off from * P_SpawnSpecials, in case you couldn't tell. * * \sa P_SpawnSpecials, P_InitTagLists @@ -6960,7 +6968,7 @@ void P_SpawnSpecials(boolean fromnetsave) break; case 146: // Intangible floor/ceiling with solid sides (fences/hoops maybe?) - P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERSIDES|FF_ALLSIDES|FF_INTANGABLEFLATS, secthinkers); + P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERSIDES|FF_ALLSIDES|FF_INTANGIBLEFLATS, secthinkers); break; case 150: // Air bobbing platform @@ -7102,10 +7110,9 @@ void P_SpawnSpecials(boolean fromnetsave) case 202: // Fog ffloorflags = FF_EXISTS|FF_RENDERALL|FF_FOG|FF_BOTHPLANES|FF_INVERTPLANES|FF_ALLSIDES|FF_INVERTSIDES|FF_CUTEXTRA|FF_EXTRA|FF_DOUBLESHADOW|FF_CUTSPRITES; sec = sides[*lines[i].sidenum].sector - sectors; - // SoM: Because it's fog, check for an extra colormap and set - // the fog flag... + // SoM: Because it's fog, check for an extra colormap and set the fog flag... if (sectors[sec].extra_colormap) - sectors[sec].extra_colormap->fog = 1; + sectors[sec].extra_colormap->flags = CMF_FOG; P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); break; @@ -8499,7 +8506,7 @@ void T_FadeColormap(fadecolormap_t *d) extracolormap_t *exc; INT32 duration = d->ticbased ? d->duration : 256; fixed_t factor = min(FixedDiv(duration - d->timer, duration), 1*FRACUNIT); - INT16 cr, cg, cb, ca, fadestart, fadeend, fog; + INT16 cr, cg, cb, ca, fadestart, fadeend, flags; INT32 rgba, fadergba; // NULL failsafes (or intentionally set to signify default) @@ -8548,7 +8555,7 @@ void T_FadeColormap(fadecolormap_t *d) fadestart = APPLYFADE(d->dest_exc->fadestart, d->source_exc->fadestart, d->sector->extra_colormap->fadestart); fadeend = APPLYFADE(d->dest_exc->fadeend, d->source_exc->fadeend, d->sector->extra_colormap->fadeend); - fog = abs(factor) > FRACUNIT/2 ? d->dest_exc->fog : d->source_exc->fog; // set new fog flag halfway through fade + flags = abs(factor) > FRACUNIT/2 ? d->dest_exc->flags : d->source_exc->flags; // set new flags halfway through fade #undef APPLYFADE @@ -8556,12 +8563,12 @@ void T_FadeColormap(fadecolormap_t *d) // setup new colormap ////////////////// - if (!(d->sector->extra_colormap = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, fog))) + if (!(d->sector->extra_colormap = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, flags))) { exc = R_CreateDefaultColormap(false); exc->fadestart = fadestart; exc->fadeend = fadeend; - exc->fog = (boolean)fog; + exc->flags = flags; exc->rgba = rgba; exc->fadergba = fadergba; exc->colormap = R_CreateLightTable(exc); diff --git a/src/p_tick.c b/src/p_tick.c index 9b56ee1c2..74472b71f 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -590,10 +590,24 @@ void P_Ticker(boolean run) { INT32 i; - //Increment jointime even if paused. + // Increment jointime and quittime even if paused for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) - ++players[i].jointime; + { + players[i].jointime++; + + if (players[i].quittime) + { + players[i].quittime++; + + if (players[i].quittime == 30 * TICRATE && G_TagGametype()) + P_CheckSurvivors(); + + if (server && players[i].quittime >= (tic_t)FixedMul(cv_rejointimeout.value, 60 * TICRATE) + && !(players[i].quittime % TICRATE)) + SendKick(i, KICK_MSG_PLAYER_QUIT); + } + } if (objectplacing) { diff --git a/src/p_user.c b/src/p_user.c index 288f0dbb5..cdd65f9f8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1466,6 +1466,13 @@ void P_PlayLivesJingle(player_t *player) S_StartSound(NULL, sfx_oneup); else if (mariomode) S_StartSound(NULL, sfx_marioa); + else if (cv_1upsound.value) + { + if (S_sfx[sfx_oneup].lumpnum != LUMPERROR) + S_StartSound(NULL, sfx_oneup); + else + S_StartSound(NULL, sfx_chchng);/* at least play something! */ + } else { P_PlayJingle(player, JT_1UP); @@ -2373,6 +2380,8 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) } } } + else if (player->charability == CA_GLIDEANDCLIMB && (player->mo->state-states == S_PLAY_GLIDE_LANDING)) + ; else if (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2) ; else if (player->panim != PA_IDLE && player->panim != PA_WALK && player->panim != PA_RUN && player->panim != PA_DASH) @@ -3153,7 +3162,7 @@ static void P_DoClimbing(player_t *player) platx = P_ReturnThrustX(player->mo, player->mo->angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale)); platy = P_ReturnThrustY(player->mo, player->mo->angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale)); - glidesector = R_IsPointInSubsector(player->mo->x + platx, player->mo->y + platy); + glidesector = R_PointInSubsectorOrNull(player->mo->x + platx, player->mo->y + platy); { boolean floorclimb = false; @@ -4530,16 +4539,14 @@ void P_DoJump(player_t *player, boolean soundandstate) player->mo->z--; if (player->mo->pmomz < 0) player->mo->momz += player->mo->pmomz; // Add the platform's momentum to your jump. - else - player->mo->pmomz = 0; + player->mo->pmomz = 0; } else { player->mo->z++; if (player->mo->pmomz > 0) player->mo->momz += player->mo->pmomz; // Add the platform's momentum to your jump. - else - player->mo->pmomz = 0; + player->mo->pmomz = 0; } player->mo->eflags &= ~MFE_APPLYPMOMZ; @@ -5496,10 +5503,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) ; // Can't do anything if you're a fish out of water! else if (player->powers[pw_tailsfly]) // If currently flying, give an ascend boost. { - if (!player->fly1) - player->fly1 = 20; - else - player->fly1 = 2; + player->fly1 = 20; if (player->charability == CA_SWIM) player->fly1 /= 2; @@ -7432,6 +7436,8 @@ static void P_NiGHTSMovement(player_t *player) else // AngleFixed(R_PointToAngle2()) results in slight inaccuracy! Don't use it unless movement is on both axises. newangle = (INT16)FixedInt(AngleFixed(R_PointToAngle2(0,0, cmd->sidemove*FRACUNIT, cmd->forwardmove*FRACUNIT))); + newangle -= player->viewrollangle / ANG1; + if (newangle < 0 && moved) newangle = (INT16)(360+newangle); } @@ -8480,7 +8486,11 @@ static void P_MovePlayer(player_t *player) // Descend if (cmd->buttons & BT_USE && !(player->pflags & PF_STASIS) && !player->exiting && !(player->mo->eflags & MFE_GOOWATER)) if (P_MobjFlip(player->mo)*player->mo->momz > -FixedMul(5*actionspd, player->mo->scale)) + { + if (player->fly1 > 2) + player->fly1 = 2; P_SetObjectMomZ(player->mo, -actionspd/2, true); + } } else @@ -8672,7 +8682,7 @@ static void P_MovePlayer(player_t *player) } #ifdef HWRENDER - if (rendermode != render_soft && rendermode != render_none && cv_grfovchange.value) + if (rendermode != render_soft && rendermode != render_none && cv_fovchange.value) { fixed_t speed; const fixed_t runnyspeed = 20*FRACUNIT; @@ -9185,7 +9195,7 @@ mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction, case MT_TNTBARREL: if (lockonflags & LOCK_INTERESTS) break; - /*fallthru*/ + /*FALLTHRU*/ case MT_PLAYER: // Don't chase other players! case MT_DETON: continue; // Don't be STUPID, Sonic! @@ -9203,7 +9213,7 @@ mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction, case MT_EGGSTATUE: if (tutorialmode) break; // Always focus egg statue in the tutorial - /*fallthru*/ + /*FALLTHRU*/ default: if ((lockonflags & LOCK_BOSS) && ((mo->flags & (MF_BOSS|MF_SHOOTABLE)) == (MF_BOSS|MF_SHOOTABLE))) // allows if it has the flags desired XOR it has the invert aimable flag @@ -9279,6 +9289,7 @@ mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction, // If nonenemies is true, includes monitors and springs! // If bullet is true, you can look up and the distance is further, // but your total angle span you can look is limited to compensate. (Also, allows monitors.) +// If you modify this, please also modify P_HomingAttack. // mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet) { @@ -9374,15 +9385,18 @@ boolean P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target if (!enemy) return false; - if (!enemy->health) + if (enemy->flags & MF_NOCLIPTHING) + return false; + + if (enemy->health <= 0) // dead + return false; + + if (!((enemy->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) && (enemy->flags & MF_SHOOTABLE)) || (enemy->flags & MF_SPRING)) == !(enemy->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag return false; if (enemy->flags2 & MF2_FRET) return false; - if (!(enemy->flags & (MF_SHOOTABLE|MF_SPRING)) == !(enemy->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag - return false; - // change angle source->angle = R_PointToAngle2(source->x, source->y, enemy->x, enemy->y); if (source->player) @@ -10105,13 +10119,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall { dist = camdist; - // x1.5 dist for splitscreen - if (splitscreen) - { - dist = FixedMul(dist, 3*FRACUNIT/2); - camheight = FixedMul(camheight, 3*FRACUNIT/2); - } - if (sign) // signpost camera has specific placement { camheight = mo->scale << 7; @@ -10231,7 +10238,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } // move camera down to move under lower ceilings - newsubsec = R_IsPointInSubsector(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1)); + newsubsec = R_PointInSubsectorOrNull(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1)); if (!newsubsec) newsubsec = thiscam->subsector; @@ -10511,13 +10518,17 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (!(multiplayer || netgame) && !splitscreen) { fixed_t vx = thiscam->x, vy = thiscam->y; + fixed_t vz = thiscam->z + thiscam->height / 2; if (player->awayviewtics && player->awayviewmobj != NULL && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist { vx = player->awayviewmobj->x; vy = player->awayviewmobj->y; + vz = player->awayviewmobj->z + player->awayviewmobj->height / 2; } - if (P_AproxDistance(vx - mo->x, vy - mo->y) < FixedMul(48*FRACUNIT, mo->scale)) + /* check z distance too for orbital camera */ + if (P_AproxDistance(P_AproxDistance(vx - mo->x, vy - mo->y), + vz - ( mo->z + mo->height / 2 )) < FixedMul(48*FRACUNIT, mo->scale)) mo->flags2 |= MF2_SHADOW; else mo->flags2 &= ~MF2_SHADOW; @@ -11035,10 +11046,21 @@ static void P_MinecartThink(player_t *player) if (angdiff + minecart->angle != player->mo->angle && (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)) { + angle_t *ang = NULL; + if (player == &players[consoleplayer]) - localangle = player->mo->angle; + ang = &localangle; else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; + ang = &localangle2; + + if (ang) + { + angdiff = *ang - minecart->angle; + if (angdiff < ANGLE_180 && angdiff > MINECARTCONEMAX) + *ang = minecart->angle + MINECARTCONEMAX; + else if (angdiff > ANGLE_180 && angdiff < InvAngle(MINECARTCONEMAX)) + *ang = minecart->angle - MINECARTCONEMAX; + } } } @@ -11748,6 +11770,8 @@ void P_PlayerThink(player_t *player) { if (!playeringame[i] || players[i].spectator || players[i].bot) continue; + if (players[i].quittime > 30 * TICRATE) + continue; if (players[i].lives <= 0) continue; @@ -12152,7 +12176,9 @@ void P_PlayerThink(player_t *player) #ifdef POLYOBJECTS if (player->onconveyor == 1) - player->cmomy = player->cmomx = 0; + player->onconveyor = 3; + else if (player->onconveyor == 3) + player->cmomy = player->cmomx = 0; #endif P_DoSuperStuff(player); @@ -12200,6 +12226,11 @@ void P_PlayerThink(player_t *player) player->pflags &= ~PF_USEDOWN; } + // IF PLAYER NOT HERE THEN FLASH END IF + if (player->quittime && player->powers[pw_flashing] < flashingtics - 1 + && !(G_TagGametype() && !(player->pflags & PF_TAGIT)) && !player->gotflag) + player->powers[pw_flashing] = flashingtics - 1; + // Counters, time dependent power ups. // Time Bonus & Ring Bonus count settings @@ -12243,12 +12274,12 @@ void P_PlayerThink(player_t *player) else player->powers[pw_underwater] = 0; } - else if (player->powers[pw_underwater] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer + else if (player->powers[pw_underwater] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && (player->spectator || player->quittime))) // underwater timer player->powers[pw_underwater]--; if (player->powers[pw_spacetime] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_PROTECTWATER))) player->powers[pw_spacetime] = 0; - else if (player->powers[pw_spacetime] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer + else if (player->powers[pw_spacetime] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && (player->spectator || player->quittime))) // underwater timer player->powers[pw_spacetime]--; if (player->powers[pw_gravityboots] && player->powers[pw_gravityboots] < UINT16_MAX) diff --git a/src/r_data.c b/src/r_data.c index db18a8833..f5a24c2c4 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -699,254 +699,29 @@ void R_FlushTextureCache(void) int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum); void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); -// -// R_LoadTextures -// Initializes the texture list with the textures from the world map. -// -#define TX_START "TX_START" -#define TX_END "TX_END" -void R_LoadTextures(void) +#ifdef WALLFLATS +static INT32 +Rloadflats (INT32 i, INT32 w) { - INT32 i, w; UINT16 j; - UINT16 texstart, texend, texturesLumpPos; - patch_t *patchlump; - texpatch_t *patch; + UINT16 texstart, texend; texture_t *texture; + texpatch_t *patch; - // Free previous memory before numtextures change. - if (numtextures) + // Yes + if (wadfiles[w]->type == RET_PK3) { - for (i = 0; i < numtextures; i++) - { - Z_Free(textures[i]); - Z_Free(texturecache[i]); - } - Z_Free(texturetranslation); - Z_Free(textures); - Z_Free(texflats); + texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); + texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); } - // Load patches and textures. - - // Get the number of textures to check. - // NOTE: Make SURE the system does not process - // the markers. - // This system will allocate memory for all duplicate/patched textures even if it never uses them, - // but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures. - for (w = 0, numtextures = 0; w < numwadfiles; w++) + if (!( texstart == INT16_MAX || texend == INT16_MAX )) { - // Count the textures from TEXTURES lumps - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - while (texturesLumpPos != INT16_MAX) - { - numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); - } - - // Count single-patch textures - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) -#ifdef WALLFLATS - goto countflats; -#else - continue; -#endif - - texstart++; // Do not count the first marker - - // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) - { - for (j = texstart; j < texend; j++) - { - if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it - numtextures++; - } - } - else // Add all the textures between TX_START and TX_END - { - numtextures += (UINT32)(texend - texstart); - } - -#ifdef WALLFLATS -countflats: - // Count flats - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); - texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) - continue; - - texstart++; // Do not count the first marker - - // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) - { - for (j = texstart; j < texend; j++) - { - if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it - numtextures++; - } - } - else // Add all the textures between F_START and F_END - { - numtextures += (UINT32)(texend - texstart); - } -#endif - } - - // If no textures found by this point, bomb out - if (!numtextures) - I_Error("No textures detected in any WADs!\n"); - - // Allocate memory and initialize to 0 for all the textures we are initialising. - // There are actually 5 buffers allocated in one for convenience. - textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL); - texflats = Z_Calloc((numtextures * sizeof(*texflats)), PU_STATIC, NULL); - - // Allocate texture column offset table. - texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); - // Allocate texture referencing cache. - texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); - // Allocate texture width table. - texturewidth = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); - // Allocate texture height table. - textureheight = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4)); - // Create translation table for global animation. - texturetranslation = Z_Malloc((numtextures + 1) * sizeof(*texturetranslation), PU_STATIC, NULL); - - for (i = 0; i < numtextures; i++) - texturetranslation[i] = i; - - for (i = 0, w = 0; w < numwadfiles; w++) - { - // Get the lump numbers for the markers in the WAD, if they exist. - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - while (texturesLumpPos != INT16_MAX) - { - R_ParseTEXTURESLump(w, texturesLumpPos, &i); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); - } - } - else - { - texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - if (texturesLumpPos != INT16_MAX) - R_ParseTEXTURESLump(w, texturesLumpPos, &i); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) -#ifdef WALLFLATS - goto checkflats; -#else - continue; -#endif - - texstart++; // Do not count the first marker - - // Work through each lump between the markers in the WAD. - for (j = 0; j < (texend - texstart); j++) - { - UINT16 wadnum = (UINT16)w; - lumpnum_t lumpnum = texstart + j; -#ifndef NO_PNG_LUMPS - size_t lumplength; -#endif - - if (wadfiles[w]->type == RET_PK3) - { - if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder - continue; // If it is then SKIP IT - } - - patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); -#ifndef NO_PNG_LUMPS - lumplength = W_LumpLengthPwad(wadnum, lumpnum); -#endif - - //CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height); - texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); - - // Set texture properties. - M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)patchlump, lumplength)) - { - INT16 width, height; - R_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); - texture->width = width; - texture->height = height; - } - else -#endif - { - texture->width = SHORT(patchlump->width); - texture->height = SHORT(patchlump->height); - } - - texture->type = TEXTURETYPE_SINGLEPATCH; - texture->patchcount = 1; - texture->holes = false; - texture->flip = 0; - - // Allocate information for the texture's patches. - patch = &texture->patches[0]; - - patch->originx = patch->originy = 0; - patch->wad = (UINT16)w; - patch->lump = texstart + j; - patch->flip = 0; - - Z_Unlock(patchlump); - - texturewidth[i] = texture->width; - textureheight[i] = texture->height << FRACBITS; - i++; - } - -#ifdef WALLFLATS -checkflats: - // Yes - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); - texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) - continue; - texstart++; // Do not count the first marker // Work through each lump between the markers in the WAD. @@ -1029,7 +804,248 @@ checkflats: textureheight[i] = texture->height << FRACBITS; i++; } + } + + return i; +} +#endif/*WALLFLATS*/ + +#define TX_START "TX_START" +#define TX_END "TX_END" + +static INT32 +Rloadtextures (INT32 i, INT32 w) +{ + UINT16 j; + UINT16 texstart, texend, texturesLumpPos; + texture_t *texture; + patch_t *patchlump; + texpatch_t *patch; + + // Get the lump numbers for the markers in the WAD, if they exist. + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + while (texturesLumpPos != INT16_MAX) + { + R_ParseTEXTURESLump(w, texturesLumpPos, &i); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); + } + } + else + { + texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); + texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + if (texturesLumpPos != INT16_MAX) + R_ParseTEXTURESLump(w, texturesLumpPos, &i); + } + + if (!( texstart == INT16_MAX || texend == INT16_MAX )) + { + texstart++; // Do not count the first marker + + // Work through each lump between the markers in the WAD. + for (j = 0; j < (texend - texstart); j++) + { + UINT16 wadnum = (UINT16)w; + lumpnum_t lumpnum = texstart + j; +#ifndef NO_PNG_LUMPS + size_t lumplength; #endif + + if (wadfiles[w]->type == RET_PK3) + { + if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder + continue; // If it is then SKIP IT + } + + patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); +#ifndef NO_PNG_LUMPS + lumplength = W_LumpLengthPwad(wadnum, lumpnum); +#endif + + //CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height); + texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); + + // Set texture properties. + M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + +#ifndef NO_PNG_LUMPS + if (R_IsLumpPNG((UINT8 *)patchlump, lumplength)) + { + INT16 width, height; + R_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); + texture->width = width; + texture->height = height; + } + else +#endif + { + texture->width = SHORT(patchlump->width); + texture->height = SHORT(patchlump->height); + } + + texture->type = TEXTURETYPE_SINGLEPATCH; + texture->patchcount = 1; + texture->holes = false; + texture->flip = 0; + + // Allocate information for the texture's patches. + patch = &texture->patches[0]; + + patch->originx = patch->originy = 0; + patch->wad = (UINT16)w; + patch->lump = texstart + j; + patch->flip = 0; + + Z_Unlock(patchlump); + + texturewidth[i] = texture->width; + textureheight[i] = texture->height << FRACBITS; + i++; + } + } + + return i; +} + +// +// R_LoadTextures +// Initializes the texture list with the textures from the world map. +// +void R_LoadTextures(void) +{ + INT32 i, w; + UINT16 j; + UINT16 texstart, texend, texturesLumpPos; + + // Free previous memory before numtextures change. + if (numtextures) + { + for (i = 0; i < numtextures; i++) + { + Z_Free(textures[i]); + Z_Free(texturecache[i]); + } + Z_Free(texturetranslation); + Z_Free(textures); + Z_Free(texflats); + } + + // Load patches and textures. + + // Get the number of textures to check. + // NOTE: Make SURE the system does not process + // the markers. + // This system will allocate memory for all duplicate/patched textures even if it never uses them, + // but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures. + for (w = 0, numtextures = 0; w < numwadfiles; w++) + { +#ifdef WALLFLATS + // Count flats + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); + texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); + } + + if (!( texstart == INT16_MAX || texend == INT16_MAX )) + { + texstart++; // Do not count the first marker + + // PK3s have subfolders, so we can't just make a simple sum + if (wadfiles[w]->type == RET_PK3) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it + numtextures++; + } + } + else // Add all the textures between F_START and F_END + { + numtextures += (UINT32)(texend - texstart); + } + } +#endif/*WALLFLATS*/ + + // Count the textures from TEXTURES lumps + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + while (texturesLumpPos != INT16_MAX) + { + numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); + } + + // Count single-patch textures + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); + texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); + } + + if (texstart == INT16_MAX || texend == INT16_MAX) + continue; + + texstart++; // Do not count the first marker + + // PK3s have subfolders, so we can't just make a simple sum + if (wadfiles[w]->type == RET_PK3) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it + numtextures++; + } + } + else // Add all the textures between TX_START and TX_END + { + numtextures += (UINT32)(texend - texstart); + } + } + + // If no textures found by this point, bomb out + if (!numtextures) + I_Error("No textures detected in any WADs!\n"); + + // Allocate memory and initialize to 0 for all the textures we are initialising. + // There are actually 5 buffers allocated in one for convenience. + textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL); + texflats = Z_Calloc((numtextures * sizeof(*texflats)), PU_STATIC, NULL); + + // Allocate texture column offset table. + texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); + // Allocate texture referencing cache. + texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); + // Allocate texture width table. + texturewidth = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); + // Allocate texture height table. + textureheight = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4)); + // Create translation table for global animation. + texturetranslation = Z_Malloc((numtextures + 1) * sizeof(*texturetranslation), PU_STATIC, NULL); + + for (i = 0; i < numtextures; i++) + texturetranslation[i] = i; + + for (i = 0, w = 0; w < numwadfiles; w++) + { +#ifdef WALLFLATS + i = Rloadflats(i, w); +#endif + i = Rloadtextures(i, w); } #ifdef HWRENDER @@ -1783,7 +1799,7 @@ extracolormap_t *R_CreateDefaultColormap(boolean lighttable) extracolormap_t *exc = Z_Calloc(sizeof (*exc), PU_LEVEL, NULL); exc->fadestart = 0; exc->fadeend = 31; - exc->fog = 0; + exc->flags = 0; exc->rgba = 0; exc->fadergba = 0x19000000; exc->colormap = lighttable ? R_CreateLightTable(exc) : NULL; @@ -1887,17 +1903,17 @@ void R_AddColormapToList(extracolormap_t *extra_colormap) // #ifdef EXTRACOLORMAPLUMPS boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams, - INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog, lumpnum_t lump) + INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags, lumpnum_t lump) #else boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams, - INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog) + INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags) #endif { return ( (!checkparams ? true : (fadestart == 0 && fadeend == 31 - && !fog) + && !flags) ) && (!checkrgba ? true : rgba == 0) && (!checkfadergba ? true : fadergba == 0x19000000) @@ -1914,9 +1930,9 @@ boolean R_CheckDefaultColormap(extracolormap_t *extra_colormap, boolean checkrgb return true; #ifdef EXTRACOLORMAPLUMPS - return R_CheckDefaultColormapByValues(checkrgba, checkfadergba, checkparams, extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->fog, extra_colormap->lump); + return R_CheckDefaultColormapByValues(checkrgba, checkfadergba, checkparams, extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->flags, extra_colormap->lump); #else - return R_CheckDefaultColormapByValues(checkrgba, checkfadergba, checkparams, extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->fog); + return R_CheckDefaultColormapByValues(checkrgba, checkfadergba, checkparams, extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->flags); #endif } @@ -1936,7 +1952,7 @@ boolean R_CheckEqualColormaps(extracolormap_t *exc_a, extracolormap_t *exc_b, bo (!checkparams ? true : (exc_a->fadestart == exc_b->fadestart && exc_a->fadeend == exc_b->fadeend - && exc_a->fog == exc_b->fog) + && exc_a->flags == exc_b->flags) ) && (!checkrgba ? true : exc_a->rgba == exc_b->rgba) && (!checkfadergba ? true : exc_a->fadergba == exc_b->fadergba) @@ -1952,9 +1968,9 @@ boolean R_CheckEqualColormaps(extracolormap_t *exc_a, extracolormap_t *exc_b, bo // NOTE: Returns NULL if no match is found // #ifdef EXTRACOLORMAPLUMPS -extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog, lumpnum_t lump) +extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags, lumpnum_t lump) #else -extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog) +extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags) #endif { extracolormap_t *exc; @@ -1966,7 +1982,7 @@ extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 && fadergba == exc->fadergba && fadestart == exc->fadestart && fadeend == exc->fadeend - && fog == exc->fog + && flags == exc->flags #ifdef EXTRACOLORMAPLUMPS && (lump != LUMPERROR && lump == exc->lump) #endif @@ -1985,9 +2001,9 @@ extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 extracolormap_t *R_GetColormapFromList(extracolormap_t *extra_colormap) { #ifdef EXTRACOLORMAPLUMPS - return R_GetColormapFromListByValues(extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->fog, extra_colormap->lump); + return R_GetColormapFromListByValues(extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->flags, extra_colormap->lump); #else - return R_GetColormapFromListByValues(extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->fog); + return R_GetColormapFromListByValues(extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->flags); #endif } @@ -2019,7 +2035,7 @@ extracolormap_t *R_ColormapForName(char *name) // is no real way to tell how GL should handle a colormap lump anyway.. exc->fadestart = 0; exc->fadeend = 31; - exc->fog = 0; + exc->flags = 0; exc->rgba = 0; exc->fadergba = 0x19000000; @@ -2176,7 +2192,7 @@ extracolormap_t *R_CreateColormap(char *p1, char *p2, char *p3) // default values UINT8 cr = 0, cg = 0, cb = 0, ca = 0, cfr = 0, cfg = 0, cfb = 0, cfa = 25; UINT32 fadestart = 0, fadeend = 31; - UINT8 fog = 0; + UINT8 flags = 0; INT32 rgba = 0, fadergba = 0x19000000; #define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0) @@ -2225,12 +2241,12 @@ extracolormap_t *R_CreateColormap(char *p1, char *p2, char *p3) #define NUMFROMCHAR(c) (c >= '0' && c <= '9' ? c - '0' : 0) - // Get parameters like fadestart, fadeend, and the fogflag + // Get parameters like fadestart, fadeend, and flags if (p2[0] == '#') { if (p2[1]) { - fog = NUMFROMCHAR(p2[1]); + flags = NUMFROMCHAR(p2[1]); if (p2[2] && p2[3]) { fadestart = NUMFROMCHAR(p2[3]) + (NUMFROMCHAR(p2[2]) * 10); @@ -2297,18 +2313,18 @@ extracolormap_t *R_CreateColormap(char *p1, char *p2, char *p3) // Did we just make a default colormap? #ifdef EXTRACOLORMAPLUMPS - if (R_CheckDefaultColormapByValues(true, true, true, rgba, fadergba, fadestart, fadeend, fog, LUMPERROR)) + if (R_CheckDefaultColormapByValues(true, true, true, rgba, fadergba, fadestart, fadeend, flags, LUMPERROR)) return NULL; #else - if (R_CheckDefaultColormapByValues(true, true, true, rgba, fadergba, fadestart, fadeend, fog)) + if (R_CheckDefaultColormapByValues(true, true, true, rgba, fadergba, fadestart, fadeend, flags)) return NULL; #endif // Look for existing colormaps #ifdef EXTRACOLORMAPLUMPS - exc = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, fog, LUMPERROR); + exc = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, flags, LUMPERROR); #else - exc = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, fog); + exc = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, flags); #endif if (exc) return exc; @@ -2320,7 +2336,7 @@ extracolormap_t *R_CreateColormap(char *p1, char *p2, char *p3) extra_colormap->fadestart = (UINT16)fadestart; extra_colormap->fadeend = (UINT16)fadeend; - extra_colormap->fog = fog; + extra_colormap->flags = flags; extra_colormap->rgba = rgba; extra_colormap->fadergba = fadergba; @@ -2347,7 +2363,7 @@ extracolormap_t *R_CreateColormap(char *p1, char *p2, char *p3) extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *exc_addend, boolean subR, boolean subG, boolean subB, boolean subA, boolean subFadeR, boolean subFadeG, boolean subFadeB, boolean subFadeA, - boolean subFadeStart, boolean subFadeEnd, boolean ignoreFog, + boolean subFadeStart, boolean subFadeEnd, boolean ignoreFlags, boolean useAltAlpha, INT16 altAlpha, INT16 altFadeAlpha, boolean lighttable) { @@ -2435,8 +2451,8 @@ extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *ex // HACK: fadeend defaults to 31, so don't add anything in this case , 31), 0); - if (!ignoreFog) // overwrite fog with new value - exc_augend->fog = exc_addend->fog; + if (!ignoreFlags) // overwrite flags with new value + exc_augend->flags = exc_addend->flags; /////////////////// // put it together @@ -2449,16 +2465,20 @@ extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *ex // Thanks to quake2 source! // utils3/qdata/images.c -UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b) +UINT8 NearestPaletteColor(UINT8 r, UINT8 g, UINT8 b, RGBA_t *palette) { int dr, dg, db; int distortion, bestdistortion = 256 * 256 * 4, bestcolor = 0, i; + // Use master palette if none specified + if (palette == NULL) + palette = pMasterPalette; + for (i = 0; i < 256; i++) { - dr = r - pMasterPalette[i].s.red; - dg = g - pMasterPalette[i].s.green; - db = b - pMasterPalette[i].s.blue; + dr = r - palette[i].s.red; + dg = g - palette[i].s.green; + db = b - palette[i].s.blue; distortion = dr*dr + dg*dg + db*db; if (distortion < bestdistortion) { diff --git a/src/r_data.h b/src/r_data.h index f028f2f5d..0569893cd 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -135,12 +135,12 @@ void R_AddColormapToList(extracolormap_t *extra_colormap); #ifdef EXTRACOLORMAPLUMPS boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams, - INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog, lumpnum_t lump); -extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog, lumpnum_t lump); + INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags, lumpnum_t lump); +extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags, lumpnum_t lump); #else boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams, - INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog); -extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 fog); + INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags); +extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags); #endif boolean R_CheckDefaultColormap(extracolormap_t *extra_colormap, boolean checkrgba, boolean checkfadergba, boolean checkparams); boolean R_CheckEqualColormaps(extracolormap_t *exc_a, extracolormap_t *exc_b, boolean checkrgba, boolean checkfadergba, boolean checkparams); @@ -151,7 +151,7 @@ extracolormap_t *R_CreateColormap(char *p1, char *p2, char *p3); extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *exc_addend, boolean subR, boolean subG, boolean subB, boolean subA, boolean subFadeR, boolean subFadeG, boolean subFadeB, boolean subFadeA, - boolean subFadeStart, boolean subFadeEnd, boolean ignoreFog, + boolean subFadeStart, boolean subFadeEnd, boolean ignoreFlags, boolean useAltAlpha, INT16 altAlpha, INT16 altFadeAlpha, boolean lighttable); #ifdef EXTRACOLORMAPLUMPS @@ -171,7 +171,8 @@ const char *R_NameForColormap(extracolormap_t *extra_colormap); #define R_PutRgbaRGB(r, g, b) (R_PutRgbaR(r) + R_PutRgbaG(g) + R_PutRgbaB(b)) #define R_PutRgbaRGBA(r, g, b, a) (R_PutRgbaRGB(r, g, b) + R_PutRgbaA(a)) -UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); +UINT8 NearestPaletteColor(UINT8 r, UINT8 g, UINT8 b, RGBA_t *palette); +#define NearestColor(r, g, b) NearestPaletteColor(r, g, b, NULL) extern INT32 numtextures; diff --git a/src/r_defs.h b/src/r_defs.h index 52b9bea95..f2774edbc 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -53,11 +53,14 @@ typedef struct // Could even use more than 32 levels. typedef UINT8 lighttable_t; +#define CMF_FADEFULLBRIGHTSPRITES 1 +#define CMF_FOG 4 + // ExtraColormap type. Use for extra_colormaps from now on. typedef struct extracolormap_s { UINT8 fadestart, fadeend; - UINT8 fog; // categorical value, not boolean + UINT8 flags; // store rgba values in combined bitwise // also used in OpenGL instead lighttables @@ -84,6 +87,8 @@ typedef struct extracolormap_s typedef struct { fixed_t x, y; + boolean floorzset, ceilingzset; + fixed_t floorz, ceilingz; } vertex_t; // Forward of linedefs, for sectors. @@ -142,7 +147,7 @@ typedef enum FF_QUICKSAND = 0x1000000, ///< Quicksand! FF_PLATFORM = 0x2000000, ///< You can jump up through this to the top. FF_REVERSEPLATFORM = 0x4000000, ///< A fall-through floor in normal gravity, a platform in reverse gravity. - FF_INTANGABLEFLATS = 0x6000000, ///< Both flats are intangable, but the sides are still solid. + FF_INTANGIBLEFLATS = 0x6000000, ///< Both flats are intangible, but the sides are still solid. FF_SHATTER = 0x8000000, ///< Used with ::FF_BUSTUP. Bustable on mere touch. FF_SPINBUST = 0x10000000, ///< Used with ::FF_BUSTUP. Also bustable if you're in your spinning frames. FF_STRONGBUST = 0x20000000, ///< Used with ::FF_BUSTUP. Only bustable by "strong" characters (Knuckles) and abilities (bouncing, twinspin, melee). diff --git a/src/r_draw8.c b/src/r_draw8.c index 015dac2a7..2f6bdcfa4 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -644,6 +644,7 @@ void R_CalcTiltedLighting(fixed_t start, fixed_t end) } } +#define PLANELIGHTFLOAT (BASEVIDWIDTH * BASEVIDWIDTH / vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f * FIXED_TO_FLOAT(fovtan)) /** \brief The R_DrawTiltedSpan_8 function Draw slopes! Holy sheit! @@ -669,7 +670,7 @@ void R_DrawTiltedSpan_8(void) // Lighting is simple. It's just linear interpolation from start to end { - float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f; + float planelightfloat = PLANELIGHTFLOAT; float lightstart, lightend; lightend = (iz + ds_szp->x*width) * planelightfloat; @@ -805,7 +806,7 @@ void R_DrawTiltedTranslucentSpan_8(void) // Lighting is simple. It's just linear interpolation from start to end { - float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f; + float planelightfloat = PLANELIGHTFLOAT; float lightstart, lightend; lightend = (iz + ds_szp->x*width) * planelightfloat; @@ -942,7 +943,7 @@ void R_DrawTiltedTranslucentWaterSpan_8(void) // Lighting is simple. It's just linear interpolation from start to end { - float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f; + float planelightfloat = PLANELIGHTFLOAT; float lightstart, lightend; lightend = (iz + ds_szp->x*width) * planelightfloat; @@ -1078,7 +1079,7 @@ void R_DrawTiltedSplat_8(void) // Lighting is simple. It's just linear interpolation from start to end { - float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f; + float planelightfloat = PLANELIGHTFLOAT; float lightstart, lightend; lightend = (iz + ds_szp->x*width) * planelightfloat; diff --git a/src/r_draw8_npo2.c b/src/r_draw8_npo2.c index 748ca195c..aa38ee2d9 100644 --- a/src/r_draw8_npo2.c +++ b/src/r_draw8_npo2.c @@ -62,6 +62,9 @@ void R_DrawSpan_NPO2_8 (void) } #ifdef ESLOPE + +#define PLANELIGHTFLOAT (BASEVIDWIDTH * BASEVIDWIDTH / vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f * FIXED_TO_FLOAT(fovtan)) + /** \brief The R_DrawTiltedSpan_NPO2_8 function Draw slopes! Holy sheit! */ @@ -86,7 +89,7 @@ void R_DrawTiltedSpan_NPO2_8(void) // Lighting is simple. It's just linear interpolation from start to end { - float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f; + float planelightfloat = PLANELIGHTFLOAT; float lightstart, lightend; lightend = (iz + ds_szp->x*width) * planelightfloat; @@ -282,7 +285,7 @@ void R_DrawTiltedTranslucentSpan_NPO2_8(void) // Lighting is simple. It's just linear interpolation from start to end { - float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f; + float planelightfloat = PLANELIGHTFLOAT; float lightstart, lightend; lightend = (iz + ds_szp->x*width) * planelightfloat; @@ -476,7 +479,7 @@ void R_DrawTiltedSplat_NPO2_8(void) // Lighting is simple. It's just linear interpolation from start to end { - float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f; + float planelightfloat = PLANELIGHTFLOAT; float lightstart, lightend; lightend = (iz + ds_szp->x*width) * planelightfloat; @@ -869,7 +872,7 @@ void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void) // Lighting is simple. It's just linear interpolation from start to end { - float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f; + float planelightfloat = PLANELIGHTFLOAT; float lightstart, lightend; lightend = (iz + ds_szp->x*width) * planelightfloat; diff --git a/src/r_main.c b/src/r_main.c index a2674b543..643d8ea49 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -59,6 +59,7 @@ INT32 centerx, centery; fixed_t centerxfrac, centeryfrac; fixed_t projection; fixed_t projectiony; // aspect ratio +fixed_t fovtan; // field of view // just for profiling purposes size_t framecount; @@ -70,6 +71,7 @@ angle_t viewangle, aimingangle; fixed_t viewcos, viewsin; sector_t *viewsector; player_t *viewplayer; +mobj_t *r_viewmobj; // // precalculated math tables @@ -108,10 +110,12 @@ static CV_PossibleValue_t drawdist_precip_cons_t[] = { {1024, "1024"}, {1536, "1536"}, {2048, "2048"}, {0, "None"}, {0, NULL}}; +static CV_PossibleValue_t fov_cons_t[] = {{60*FRACUNIT, "MIN"}, {179*FRACUNIT, "MAX"}, {0, NULL}}; static CV_PossibleValue_t translucenthud_cons_t[] = {{0, "MIN"}, {10, "MAX"}, {0, NULL}}; static CV_PossibleValue_t maxportals_cons_t[] = {{0, "MIN"}, {12, "MAX"}, {0, NULL}}; // lmao rendering 32 portals, you're a card static CV_PossibleValue_t homremoval_cons_t[] = {{0, "No"}, {1, "Yes"}, {2, "Flash"}, {0, NULL}}; +static void Fov_OnChange(void); static void ChaseCam_OnChange(void); static void ChaseCam2_OnChange(void); static void FlipCam_OnChange(void); @@ -125,12 +129,7 @@ consvar_t cv_chasecam2 = {"chasecam2", "On", CV_CALL, CV_OnOff, ChaseCam2_OnChan consvar_t cv_flipcam = {"flipcam", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; -#if defined(FLOORSPLATS) || defined(GLBADSHADOWS) -consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -#endif //#if defined(FLOORSPLATS) || defined(GLBADSHADOWS) -#ifdef GLBADSHADOWS -consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -#endif //#ifdef GLBADSHADOWS +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_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL}; @@ -141,6 +140,7 @@ consvar_t cv_drawdist = {"drawdist", "Infinite", CV_SAVE, drawdist_cons_t, NULL, consvar_t cv_drawdist_nights = {"drawdist_nights", "2048", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_drawdist_precip = {"drawdist_precip", "1024", CV_SAVE, drawdist_precip_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //consvar_t cv_precipdensity = {"precipdensity", "Moderate", CV_SAVE, precipdensity_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_fov = {"fov", "90", CV_FLOAT|CV_CALL, fov_cons_t, Fov_OnChange, 0, NULL, NULL, 0, 0, NULL}; // Okay, whoever said homremoval causes a performance hit should be shot. consvar_t cv_homremoval = {"homremoval", "No", CV_SAVE, homremoval_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -184,6 +184,14 @@ void SplitScreen_OnChange(void) } } } +static void Fov_OnChange(void) +{ + // Shouldn't be needed with render parity? + //if ((netgame || multiplayer) && !cv_debug && cv_fov.value != 90*FRACUNIT) + // CV_Set(&cv_fov, cv_fov.defaultvalue); + + R_SetViewSize(); +} static void ChaseCam_OnChange(void) { @@ -448,8 +456,8 @@ static void R_InitTextureMapping(void) // // Calc focallength // so FIELDOFVIEW angles covers SCREENWIDTH. - focallength = FixedDiv(centerxfrac, - FINETANGENT(FINEANGLES/4+/*cv_fov.value*/ FIELDOFVIEW/2)); + focallength = FixedDiv(projection, + FINETANGENT(FINEANGLES/4+FIELDOFVIEW/2)); #ifdef ESLOPE focallengthf = FIXED_TO_FLOAT(focallength); @@ -457,9 +465,9 @@ static void R_InitTextureMapping(void) for (i = 0; i < FINEANGLES/2; i++) { - if (FINETANGENT(i) > FRACUNIT*2) + if (FINETANGENT(i) > fovtan*2) t = -1; - else if (FINETANGENT(i) < -FRACUNIT*2) + else if (FINETANGENT(i) < -fovtan*2) t = viewwidth+1; else { @@ -539,6 +547,303 @@ static inline void R_InitLightTables(void) } } +//#define WOUGHMP_WOUGHMP // I got a fish-eye lens - I'll make a rap video with a couple of friends +// it's kinda laggy sometimes + +static struct { + angle_t rollangle; // pre-shifted by fineshift +#ifdef WOUGHMP_WOUGHMP + fixed_t fisheye; +#endif + + fixed_t zoomneeded; + INT32 *scrmap; + INT32 scrmapsize; + + INT32 x1; // clip rendering horizontally for efficiency + INT16 ceilingclip[MAXVIDWIDTH], floorclip[MAXVIDWIDTH]; + + boolean use; +} viewmorph = { + 0, +#ifdef WOUGHMP_WOUGHMP + 0, +#endif + + FRACUNIT, + NULL, + 0, + + 0, + {}, {}, + + false +}; + +void R_CheckViewMorph(void) +{ + float zoomfactor, rollcos, rollsin; + float x1, y1, x2, y2; + fixed_t temp; + INT32 end, vx, vy, pos, usedpos; + INT32 usedx, usedy, halfwidth = vid.width/2, halfheight = vid.height/2; +#ifdef WOUGHMP_WOUGHMP + float fisheyemap[MAXVIDWIDTH/2 + 1]; +#endif + + angle_t rollangle = players[displayplayer].viewrollangle; +#ifdef WOUGHMP_WOUGHMP + fixed_t fisheye = cv_cam2_turnmultiplier.value; // temporary test value +#endif + + rollangle >>= ANGLETOFINESHIFT; + rollangle = ((rollangle+2) & ~3) & FINEMASK; // Limit the distinct number of angles to reduce recalcs from angles changing a lot. + +#ifdef WOUGHMP_WOUGHMP + fisheye &= ~0x7FF; // Same +#endif + + if (rollangle == viewmorph.rollangle && +#ifdef WOUGHMP_WOUGHMP + fisheye == viewmorph.fisheye && +#endif + viewmorph.scrmapsize == vid.width*vid.height) + return; // No change + + viewmorph.rollangle = rollangle; +#ifdef WOUGHMP_WOUGHMP + viewmorph.fisheye = fisheye; +#endif + + if (viewmorph.rollangle == 0 +#ifdef WOUGHMP_WOUGHMP + && viewmorph.fisheye == 0 +#endif + ) + { + viewmorph.use = false; + viewmorph.x1 = 0; + if (viewmorph.zoomneeded != FRACUNIT) + R_SetViewSize(); + viewmorph.zoomneeded = FRACUNIT; + + return; + } + + if (viewmorph.scrmapsize != vid.width*vid.height) + { + if (viewmorph.scrmap) + free(viewmorph.scrmap); + viewmorph.scrmap = malloc(vid.width*vid.height * sizeof(INT32)); + viewmorph.scrmapsize = vid.width*vid.height; + } + + temp = FINECOSINE(rollangle); + rollcos = FIXED_TO_FLOAT(temp); + temp = FINESINE(rollangle); + rollsin = FIXED_TO_FLOAT(temp); + + // Calculate maximum zoom needed + x1 = (vid.width*fabsf(rollcos) + vid.height*fabsf(rollsin)) / vid.width; + y1 = (vid.height*fabsf(rollcos) + vid.width*fabsf(rollsin)) / vid.height; + +#ifdef WOUGHMP_WOUGHMP + if (fisheye) + { + float f = FIXED_TO_FLOAT(fisheye); + for (vx = 0; vx <= halfwidth; vx++) + fisheyemap[vx] = 1.0f / cos(atan(vx * f / halfwidth)); + + f = cos(atan(f)); + if (f < 1.0f) + { + x1 /= f; + y1 /= f; + } + } +#endif + + temp = max(x1, y1)*FRACUNIT; + if (temp < FRACUNIT) + temp = FRACUNIT; + else + temp |= 0x3FFF; // Limit how many times the viewport needs to be recalculated + + //CONS_Printf("Setting zoom to %f\n", FIXED_TO_FLOAT(temp)); + + if (temp != viewmorph.zoomneeded) + { + viewmorph.zoomneeded = temp; + R_SetViewSize(); + } + + zoomfactor = FIXED_TO_FLOAT(viewmorph.zoomneeded); + + end = vid.width * vid.height - 1; + + pos = 0; + + // Pre-multiply rollcos and rollsin to use for positional stuff + rollcos /= zoomfactor; + rollsin /= zoomfactor; + + x1 = -(halfwidth * rollcos - halfheight * rollsin); + y1 = -(halfheight * rollcos + halfwidth * rollsin); + +#ifdef WOUGHMP_WOUGHMP + if (fisheye) + viewmorph.x1 = (INT32)(halfwidth - (halfwidth * fabsf(rollcos) + halfheight * fabsf(rollsin)) * fisheyemap[halfwidth]); + else +#endif + viewmorph.x1 = (INT32)(halfwidth - (halfwidth * fabsf(rollcos) + halfheight * fabsf(rollsin))); + //CONS_Printf("saving %d cols\n", viewmorph.x1); + + // Set ceilingclip and floorclip + for (vx = 0; vx < vid.width; vx++) + { + viewmorph.ceilingclip[vx] = vid.height; + viewmorph.floorclip[vx] = -1; + } + x2 = x1; + y2 = y1; + for (vx = 0; vx < vid.width; vx++) + { + INT16 xa, ya, xb, yb; + xa = x2+halfwidth; + ya = y2+halfheight-1; + xb = vid.width-1-xa; + yb = vid.height-1-ya; + + viewmorph.ceilingclip[xa] = min(viewmorph.ceilingclip[xa], ya); + viewmorph.floorclip[xa] = max(viewmorph.floorclip[xa], ya); + viewmorph.ceilingclip[xb] = min(viewmorph.ceilingclip[xb], yb); + viewmorph.floorclip[xb] = max(viewmorph.floorclip[xb], yb); + x2 += rollcos; + y2 += rollsin; + } + x2 = x1; + y2 = y1; + for (vy = 0; vy < vid.height; vy++) + { + INT16 xa, ya, xb, yb; + xa = x2+halfwidth; + ya = y2+halfheight; + xb = vid.width-1-xa; + yb = vid.height-1-ya; + + viewmorph.ceilingclip[xa] = min(viewmorph.ceilingclip[xa], ya); + viewmorph.floorclip[xa] = max(viewmorph.floorclip[xa], ya); + viewmorph.ceilingclip[xb] = min(viewmorph.ceilingclip[xb], yb); + viewmorph.floorclip[xb] = max(viewmorph.floorclip[xb], yb); + x2 -= rollsin; + y2 += rollcos; + } + + //CONS_Printf("Top left corner is %f %f\n", x1, y1); + +#ifdef WOUGHMP_WOUGHMP + if (fisheye) + { + for (vy = 0; vy < halfheight; vy++) + { + x2 = x1; + y2 = y1; + x1 -= rollsin; + y1 += rollcos; + + for (vx = 0; vx < vid.width; vx++) + { + usedx = halfwidth + x2*fisheyemap[(int) floorf(fabsf(y2*zoomfactor))]; + usedy = halfheight + y2*fisheyemap[(int) floorf(fabsf(x2*zoomfactor))]; + + usedpos = usedx + usedy*vid.width; + + viewmorph.scrmap[pos] = usedpos; + viewmorph.scrmap[end-pos] = end-usedpos; + + x2 += rollcos; + y2 += rollsin; + pos++; + } + } + } + else + { +#endif + x1 += halfwidth; + y1 += halfheight; + + for (vy = 0; vy < halfheight; vy++) + { + x2 = x1; + y2 = y1; + x1 -= rollsin; + y1 += rollcos; + + for (vx = 0; vx < vid.width; vx++) + { + usedx = x2; + usedy = y2; + + usedpos = usedx + usedy*vid.width; + + viewmorph.scrmap[pos] = usedpos; + viewmorph.scrmap[end-pos] = end-usedpos; + + x2 += rollcos; + y2 += rollsin; + pos++; + } + } +#ifdef WOUGHMP_WOUGHMP + } +#endif + + viewmorph.use = true; +} + +void R_ApplyViewMorph(void) +{ + UINT8 *tmpscr = screens[4]; + UINT8 *srcscr = screens[0]; + INT32 p, end = vid.width * vid.height; + + if (!viewmorph.use) + return; + + if (cv_debug & DBG_VIEWMORPH) + { + UINT8 border = 32; + UINT8 grid = 160; + INT32 ws = vid.width / 4; + INT32 hs = vid.width * (vid.height / 4); + + memcpy(tmpscr, srcscr, vid.width*vid.height); + for (p = 0; p < vid.width; p++) + { + tmpscr[viewmorph.scrmap[p]] = border; + tmpscr[viewmorph.scrmap[p + hs]] = grid; + tmpscr[viewmorph.scrmap[p + hs*2]] = grid; + tmpscr[viewmorph.scrmap[p + hs*3]] = grid; + tmpscr[viewmorph.scrmap[end - 1 - p]] = border; + } + for (p = vid.width; p < end; p += vid.width) + { + tmpscr[viewmorph.scrmap[p]] = border; + tmpscr[viewmorph.scrmap[p + ws]] = grid; + tmpscr[viewmorph.scrmap[p + ws*2]] = grid; + tmpscr[viewmorph.scrmap[p + ws*3]] = grid; + tmpscr[viewmorph.scrmap[end - 1 - p]] = border; + } + } + else + for (p = 0; p < end; p++) + tmpscr[p] = srcscr[viewmorph.scrmap[p]]; + + VID_BlitLinearScreen(tmpscr, screens[0], + vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.width); +} + // // R_SetViewSize @@ -563,6 +868,7 @@ void R_ExecuteSetViewSize(void) INT32 j; INT32 level; INT32 startmapl; + angle_t fov; setsizeneeded = false; @@ -585,9 +891,12 @@ void R_ExecuteSetViewSize(void) centerxfrac = centerx<> ANGLETOFINESHIFT), viewmorph.zoomneeded); + if (splitscreen == 1) // Splitscreen FOV should be adjusted to maintain expected vertical view + fovtan = 17*fovtan/10; + + projection = projectiony = FixedDiv(centerxfrac, fovtan); R_InitViewBuffer(scaledviewwidth, viewheight); @@ -613,7 +922,7 @@ void R_ExecuteSetViewSize(void) for (i = 0; i < j; i++) { dy = ((i - viewheight*8)<>ANGLETOFINESHIFT)) & FINEMASK)*160)>>FRACBITS) +#define AIMINGTODY(a) ((FINETANGENT((2048+(((INT32)a)>>ANGLETOFINESHIFT)) & FINEMASK)*160)/fovtan) // recalc necessary stuff for mouseaiming // slopes are already calculated for the full possible view (which is 4*viewheight). @@ -789,16 +1096,16 @@ void R_SetupFrame(player_t *player) if (player->awayviewtics) { // cut-away view stuff - viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN - I_Assert(viewmobj != NULL); - viewz = viewmobj->z + 20*FRACUNIT; + r_viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN + I_Assert(r_viewmobj != NULL); + viewz = r_viewmobj->z + 20*FRACUNIT; aimingangle = player->awayviewaiming; - viewangle = viewmobj->angle; + viewangle = r_viewmobj->angle; } else if (!player->spectator && chasecam) // use outside cam view { - viewmobj = NULL; + r_viewmobj = NULL; viewz = thiscam->z + (thiscam->height>>1); aimingangle = thiscam->aiming; viewangle = thiscam->angle; @@ -808,11 +1115,11 @@ void R_SetupFrame(player_t *player) { viewz = player->viewz; - viewmobj = player->mo; - I_Assert(viewmobj != NULL); + r_viewmobj = player->mo; + I_Assert(r_viewmobj != NULL); aimingangle = player->aiming; - viewangle = viewmobj->angle; + viewangle = r_viewmobj->angle; if (!demoplayback && player->playerstate != PST_DEAD) { @@ -846,13 +1153,13 @@ void R_SetupFrame(player_t *player) } else { - viewx = viewmobj->x; - viewy = viewmobj->y; + viewx = r_viewmobj->x; + viewy = r_viewmobj->y; viewx += quake.x; viewy += quake.y; - if (viewmobj->subsector) - viewsector = viewmobj->subsector->sector; + if (r_viewmobj->subsector) + viewsector = r_viewmobj->subsector->sector; else viewsector = R_PointInSubsector(viewx, viewy)->sector; } @@ -874,12 +1181,12 @@ void R_SkyboxFrame(player_t *player) thiscam = &camera; // cut-away view stuff - viewmobj = skyboxmo[0]; + r_viewmobj = skyboxmo[0]; #ifdef PARANOIA - if (!viewmobj) + if (!r_viewmobj) { const size_t playeri = (size_t)(player - players); - I_Error("R_SkyboxFrame: viewmobj null (player %s)", sizeu1(playeri)); + I_Error("R_SkyboxFrame: r_viewmobj null (player %s)", sizeu1(playeri)); } #endif if (player->awayviewtics) @@ -910,13 +1217,13 @@ void R_SkyboxFrame(player_t *player) } } } - viewangle += viewmobj->angle; + viewangle += r_viewmobj->angle; viewplayer = player; - viewx = viewmobj->x; - viewy = viewmobj->y; - viewz = viewmobj->z; // 26/04/17: use actual Z position instead of spawnpoint angle! + viewx = r_viewmobj->x; + viewy = r_viewmobj->y; + viewz = r_viewmobj->z; // 26/04/17: use actual Z position instead of spawnpoint angle! if (mapheaderinfo[gamemap-1]) { @@ -956,29 +1263,29 @@ void R_SkyboxFrame(player_t *player) else if (mh->skybox_scaley < 0) y = (campos.y - skyboxmo[1]->y) * -mh->skybox_scaley; - if (viewmobj->angle == 0) + if (r_viewmobj->angle == 0) { viewx += x; viewy += y; } - else if (viewmobj->angle == ANGLE_90) + else if (r_viewmobj->angle == ANGLE_90) { viewx -= y; viewy += x; } - else if (viewmobj->angle == ANGLE_180) + else if (r_viewmobj->angle == ANGLE_180) { viewx -= x; viewy -= y; } - else if (viewmobj->angle == ANGLE_270) + else if (r_viewmobj->angle == ANGLE_270) { viewx += y; viewy -= x; } else { - angle_t ang = viewmobj->angle>>ANGLETOFINESHIFT; + angle_t ang = r_viewmobj->angle>>ANGLETOFINESHIFT; viewx += FixedMul(x,FINECOSINE(ang)) - FixedMul(y, FINESINE(ang)); viewy += FixedMul(x, FINESINE(ang)) + FixedMul(y,FINECOSINE(ang)); } @@ -989,8 +1296,8 @@ void R_SkyboxFrame(player_t *player) viewz += campos.z * -mh->skybox_scalez; } - if (viewmobj->subsector) - viewsector = viewmobj->subsector->sector; + if (r_viewmobj->subsector) + viewsector = r_viewmobj->subsector->sector; else viewsector = R_PointInSubsector(viewx, viewy)->sector; @@ -1071,9 +1378,22 @@ void R_RenderPlayerView(player_t *player) validcount++; // Clear buffers. - R_ClearClipSegs(); - R_ClearDrawSegs(); R_ClearPlanes(); + if (viewmorph.use) + { + portalclipstart = viewmorph.x1; + portalclipend = viewwidth-viewmorph.x1-1; + R_PortalClearClipSegs(portalclipstart, portalclipend); + memcpy(ceilingclip, viewmorph.ceilingclip, sizeof(INT16)*vid.width); + memcpy(floorclip, viewmorph.floorclip, sizeof(INT16)*vid.width); + } + else + { + portalclipstart = 0; + portalclipend = viewwidth-1; + R_ClearClipSegs(); + } + R_ClearDrawSegs(); R_ClearSprites(); #ifdef FLOORSPLATS R_ClearVisibleFloorSplats(); @@ -1203,15 +1523,12 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_drawdist); CV_RegisterVar(&cv_drawdist_nights); CV_RegisterVar(&cv_drawdist_precip); + CV_RegisterVar(&cv_fov); CV_RegisterVar(&cv_chasecam); CV_RegisterVar(&cv_chasecam2); -#if defined(FLOORSPLATS) || defined(GLBADSHADOWS) + CV_RegisterVar(&cv_shadow); -#endif //#if defined(FLOORSPLATS) || defined(GLBADSHADOWS) -#ifdef GLBADSHADOWS - CV_RegisterVar(&cv_shadowoffs); -#endif //#ifdef GLBADSHADOWS CV_RegisterVar(&cv_skybox); CV_RegisterVar(&cv_cam_dist); diff --git a/src/r_main.h b/src/r_main.h index 143cc1de0..8cf3d7cbc 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -26,6 +26,7 @@ extern INT32 centerx, centery; extern fixed_t centerxfrac, centeryfrac; extern fixed_t projection, projectiony; +extern fixed_t fovtan; // field of view extern size_t validcount, linecount, loopcount, framecount; @@ -45,6 +46,8 @@ extern size_t validcount, linecount, loopcount, framecount; #define MAXLIGHTZ 128 #define LIGHTZSHIFT 20 +#define LIGHTRESOLUTIONFIX (640*fovtan/vid.width) + extern lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; extern lighttable_t *scalelightfixed[MAXLIGHTSCALE]; extern lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; @@ -64,7 +67,7 @@ fixed_t R_PointToDist2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1); fixed_t R_ScaleFromGlobalAngle(angle_t visangle); subsector_t *R_PointInSubsector(fixed_t x, fixed_t y); -subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y); +subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y); boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixed_t bottomh, fixed_t toph); @@ -76,14 +79,11 @@ extern consvar_t cv_showhud, cv_translucenthud; extern consvar_t cv_homremoval; extern consvar_t cv_chasecam, cv_chasecam2; extern consvar_t cv_flipcam, cv_flipcam2; -#if defined(FLOORSPLATS) || defined(GLBADSHADOWS) + extern consvar_t cv_shadow; -#endif -#ifdef GLBADSHADOWS -extern conscar_t cv_shadowoffs; -#endif //#ifdef GLBADSHADOWS extern consvar_t cv_translucency; extern consvar_t cv_drawdist, cv_drawdist_nights, cv_drawdist_precip; +extern consvar_t cv_fov; extern consvar_t cv_skybox; extern consvar_t cv_tailspickup; @@ -94,6 +94,9 @@ void R_InitHardwareMode(void); #endif void R_ReloadHUDGraphics(void); +void R_CheckViewMorph(void); +void R_ApplyViewMorph(void); + // just sets setsizeneeded true extern boolean setsizeneeded; void R_SetViewSize(void); diff --git a/src/r_patch.c b/src/r_patch.c index b4127f825..80c0f846f 100644 --- a/src/r_patch.c +++ b/src/r_patch.c @@ -1196,7 +1196,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp INT32 angle; patch_t *patch; patch_t *newpatch; - UINT16 *rawsrc, *rawdst; + UINT16 *rawdst; size_t size; INT32 bflip = (flip != 0x00); @@ -1242,16 +1242,6 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp leftoffset = width - leftoffset; } - // Draw the sprite to a temporary buffer. - size = (width*height); - rawsrc = Z_Malloc(size * sizeof(UINT16), PU_STATIC, NULL); - - // can't memset here - for (i = 0; i < size; i++) - rawsrc[i] = 0xFF00; - - R_PatchToMaskedFlat(patch, rawsrc, bflip); - // Don't cache angle = 0 for (angle = 1; angle < ROTANGLES; angle++) { diff --git a/src/r_plane.c b/src/r_plane.c index 5d5e1f20d..e62e571e4 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -44,9 +44,6 @@ // Quincunx antialiasing of flats! //#define QUINCUNX -// good night sweet prince -#define SHITPLANESPARENCY - //SoM: 3/23/2000: Use Boom visplane hashing. visplane_t *visplanes[MAXVISPLANES]; @@ -995,11 +992,7 @@ void R_DrawSinglePlane(visplane_t *pl) else // Opaque, but allow transparent flat pixels spanfunctype = SPANDRAWFUNC_SPLAT; -#ifdef SHITPLANESPARENCY - if ((spanfunctype == SPANDRAWFUNC_SPLAT) != (pl->extra_colormap && (pl->extra_colormap->fog & 4))) -#else - if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2)) -#endif + if ((spanfunctype == SPANDRAWFUNC_SPLAT) || (pl->extra_colormap && (pl->extra_colormap->flags & CMF_FOG))) light = (pl->lightlevel >> LIGHTSEGSHIFT); else light = LIGHTLEVELS-1; @@ -1053,11 +1046,7 @@ void R_DrawSinglePlane(visplane_t *pl) else // Opaque, but allow transparent flat pixels spanfunctype = SPANDRAWFUNC_SPLAT; -#ifdef SHITPLANESPARENCY - if ((spanfunctype == SPANDRAWFUNC_SPLAT) != (pl->extra_colormap && (pl->extra_colormap->fog & 4))) -#else - if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2)) -#endif + if ((spanfunctype == SPANDRAWFUNC_SPLAT) || (pl->extra_colormap && (pl->extra_colormap->flags & CMF_FOG))) light = (pl->lightlevel >> LIGHTSEGSHIFT); else light = LIGHTLEVELS-1; diff --git a/src/r_segs.c b/src/r_segs.c index dcb5fc160..057518fb5 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -199,7 +199,7 @@ static void R_DrawWallSplats(void) // draw the columns for (dc_x = x1; dc_x <= x2; dc_x++, spryscale += rw_scalestep) { - pindex = FixedMul(spryscale, FixedDiv(640, vid.width))>>LIGHTSCALESHIFT; + pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; if (pindex >= MAXLIGHTSCALE) pindex = MAXLIGHTSCALE - 1; dc_colormap = walllights[pindex]; @@ -418,14 +418,14 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) rlight->extra_colormap = *light->extra_colormap; rlight->flags = light->flags; - if (rlight->flags & FF_FOG || (rlight->extra_colormap && rlight->extra_colormap->fog)) + if ((colfunc != colfuncs[COLDRAWFUNC_FUZZY]) + || (rlight->flags & FF_FOG) + || (rlight->extra_colormap && (rlight->extra_colormap->flags & CMF_FOG))) lightnum = (rlight->lightlevel >> LIGHTSEGSHIFT); - else if (colfunc == colfuncs[COLDRAWFUNC_FUZZY]) - lightnum = LIGHTLEVELS - 1; else - lightnum = (rlight->lightlevel >> LIGHTSEGSHIFT); + lightnum = LIGHTLEVELS - 1; - if (rlight->extra_colormap && rlight->extra_colormap->fog) + if (rlight->extra_colormap && (rlight->extra_colormap->flags & CMF_FOG)) ; else if (curline->v1->y == curline->v2->y) lightnum--; @@ -437,18 +437,14 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) } else { - if (colfunc == colfuncs[COLDRAWFUNC_FUZZY]) - { - if (frontsector->extra_colormap && frontsector->extra_colormap->fog) - lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT); - else - lightnum = LIGHTLEVELS - 1; - } - else + if ((colfunc != colfuncs[COLDRAWFUNC_FUZZY]) + || (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG))) lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT); + else + lightnum = LIGHTLEVELS - 1; if (colfunc == colfuncs[COLDRAWFUNC_FOG] - || (frontsector->extra_colormap && frontsector->extra_colormap->fog)) + || (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG))) ; else if (curline->v1->y == curline->v2->y) lightnum--; @@ -599,7 +595,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) else xwalllights = scalelight[rlight->lightnum]; - pindex = FixedMul(spryscale, FixedDiv(640, vid.width))>>LIGHTSCALESHIFT; + pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; if (pindex >= MAXLIGHTSCALE) pindex = MAXLIGHTSCALE - 1; @@ -644,7 +640,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) } // calculate lighting - pindex = FixedMul(spryscale, FixedDiv(640, vid.width))>>LIGHTSCALESHIFT; + pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; if (pindex >= MAXLIGHTSCALE) pindex = MAXLIGHTSCALE - 1; @@ -947,7 +943,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) else rlight->lightnum = (rlight->lightlevel >> LIGHTSEGSHIFT); - if (pfloor->flags & FF_FOG || rlight->flags & FF_FOG || (rlight->extra_colormap && rlight->extra_colormap->fog)) + if (pfloor->flags & FF_FOG || rlight->flags & FF_FOG || (rlight->extra_colormap && (rlight->extra_colormap->flags & CMF_FOG))) ; else if (curline->v1->y == curline->v2->y) rlight->lightnum--; @@ -962,7 +958,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) else { // Get correct light level! - if ((frontsector->extra_colormap && frontsector->extra_colormap->fog)) + if ((frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG))) lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT); else if (pfloor->flags & FF_FOG) lightnum = (pfloor->master->frontsector->lightlevel >> LIGHTSEGSHIFT); @@ -972,7 +968,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) lightnum = R_FakeFlat(frontsector, &tempsec, &templight, &templight, false) ->lightlevel >> LIGHTSEGSHIFT; - if (pfloor->flags & FF_FOG || (frontsector->extra_colormap && frontsector->extra_colormap->fog)); + if (pfloor->flags & FF_FOG || (frontsector->extra_colormap && (frontsector->extra_colormap->flags & CMF_FOG))); else if (curline->v1->y == curline->v2->y) lightnum--; else if (curline->v1->x == curline->v2->x) @@ -1188,7 +1184,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) else xwalllights = scalelight[lightnum]; - pindex = FixedMul(spryscale, FixedDiv(640, vid.width))>>LIGHTSCALESHIFT; + pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; if (pindex >= MAXLIGHTSCALE) pindex = MAXLIGHTSCALE-1; @@ -1281,7 +1277,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) } // calculate lighting - pindex = FixedMul(spryscale, FixedDiv(640, vid.width))>>LIGHTSCALESHIFT; + pindex = FixedMul(spryscale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; if (pindex >= MAXLIGHTSCALE) pindex = MAXLIGHTSCALE - 1; @@ -1486,7 +1482,7 @@ static void R_RenderSegLoop (void) if (segtextured) { // calculate lighting - pindex = FixedMul(rw_scale, FixedDiv(640, vid.width))>>LIGHTSCALESHIFT; + pindex = FixedMul(rw_scale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; if (pindex >= MAXLIGHTSCALE) pindex = MAXLIGHTSCALE-1; @@ -1521,7 +1517,7 @@ static void R_RenderSegLoop (void) else xwalllights = scalelight[lightnum]; - pindex = FixedMul(rw_scale, FixedDiv(640, vid.width))>>LIGHTSCALESHIFT; + pindex = FixedMul(rw_scale, LIGHTRESOLUTIONFIX)>>LIGHTSCALESHIFT; if (pindex >= MAXLIGHTSCALE) pindex = MAXLIGHTSCALE-1; diff --git a/src/r_sky.c b/src/r_sky.c index c9d28cebc..da36eb937 100644 --- a/src/r_sky.c +++ b/src/r_sky.c @@ -76,5 +76,5 @@ void R_SetupSkyDraw(void) void R_SetSkyScale(void) { fixed_t difference = vid.fdupx-(vid.dupx<patch; fixed_t this_scale = vis->mobj->scale; @@ -891,6 +889,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); } @@ -906,28 +905,51 @@ static void R_DrawVisSprite(vissprite_t *vis) if (vis->x2 >= vid.width) vis->x2 = vid.width-1; - 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) { -#ifdef RANGECHECK + // Papersprite drawing loop - 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])); -#endif - if (vis->scalestep) + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, spryscale += vis->scalestep) { + 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); + + 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])); +#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); - spryscale += vis->scalestep; } colfunc = colfuncs[BASEDRAWFUNC]; @@ -1072,10 +1094,10 @@ static void R_SplitSprite(vissprite_t *sprite) newsprite->extra_colormap = *sector->lightlist[i].extra_colormap; - if (!((newsprite->cut & SC_FULLBRIGHT) - && (!newsprite->extra_colormap || !(newsprite->extra_colormap->fog & 1)))) + if (!(newsprite->cut & SC_FULLBRIGHT) + || (newsprite->extra_colormap && (newsprite->extra_colormap->flags & CMF_FADEFULLBRIGHTSPRITES))) { - lindex = FixedMul(sprite->xscale, FixedDiv(640, vid.width))>>(LIGHTSCALESHIFT); + lindex = FixedMul(sprite->xscale, LIGHTRESOLUTIONFIX)>>(LIGHTSCALESHIFT); if (lindex >= MAXLIGHTSCALE) lindex = MAXLIGHTSCALE-1; @@ -1086,7 +1108,255 @@ static void R_SplitSprite(vissprite_t *sprite) } } -//#define PROPERPAPER // This was reverted less than 7 hours before 2.2's release because of very strange, frequent crashes. +// +// 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); + + 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(); + shadow->patch = patch; + 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 + shadow->patch->height * shadowyscale / 2; + shadow->gz = shadow->gzt - shadow->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 < portalclipstart ? portalclipstart : x1; + shadow->x2 = x2 >= portalclipend ? portalclipend-1 : x2; + + 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<colormap = scalelight[0][0]; // full dark! + + objectsdrawn++; +} // // R_ProjectSprite @@ -1124,7 +1394,11 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t iscale; fixed_t scalestep; fixed_t offset, offset2; + + fixed_t basetx; // drop shadows + boolean papersprite = !!(thing->frame & FF_PAPERSPRITE); + fixed_t paperoffset = 0, paperdistance = 0; angle_t centerangle = 0; INT32 dispoffset = thing->info->dispoffset; @@ -1142,10 +1416,6 @@ static void R_ProjectSprite(mobj_t *thing) INT32 rollangle = 0; #endif -#ifndef PROPERPAPER - fixed_t ang_scale = FRACUNIT; -#endif - // transform the origin point tr_x = thing->x - viewx; tr_y = thing->y - viewy; @@ -1156,15 +1426,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 @@ -1228,13 +1498,7 @@ static void R_ProjectSprite(mobj_t *thing) #endif if (sprframe->rotate != SRF_SINGLE || papersprite) - { ang = R_PointToAngle (thing->x, thing->y) - (thing->player ? thing->player->drawangle : thing->angle); -#ifndef PROPERPAPER - if (papersprite) - ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT)); -#endif - } if (sprframe->rotate == SRF_SINGLE) { @@ -1300,31 +1564,11 @@ static void R_ProjectSprite(mobj_t *thing) else offset = -spr_offset; offset = FixedMul(offset, this_scale); -#ifndef PROPERPAPER - tx += FixedMul(offset, ang_scale); - x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS; - - // off the right side? - if (x1 > viewwidth) - return; -#endif offset2 = FixedMul(spr_width, this_scale); -#ifndef PROPERPAPER - tx += FixedMul(offset2, ang_scale); - x2 = ((centerxfrac + FixedMul (tx,xscale)) >> FRACBITS) - (papersprite ? 2 : 1); - - // off the left side - if (x2 < 0) - return; -#endif if (papersprite) { - fixed_t -#ifdef PROPERPAPER - xscale2, -#endif - yscale2, cosmul, sinmul, tz2; + fixed_t xscale2, yscale2, cosmul, sinmul, tx2, tz2; INT32 range; if (ang >= ANGLE_180) @@ -1342,19 +1586,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 -#ifdef PROPERPAPER gxt = -FixedMul(tr_x, viewsin); gyt = FixedMul(tr_y, viewcos); tx = -(gyt + gxt); xscale = FixedDiv(projection, tz); x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; - // off the right side? - if (x1 > viewwidth) - return; -#endif + // 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); @@ -1362,38 +1610,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 -#ifdef PROPERPAPER gxt = -FixedMul(tr_x, viewsin); gyt = FixedMul(tr_y, viewcos); - tx = -(gyt + gxt); + tx2 = -(gyt + gxt); xscale2 = FixedDiv(projection, tz2); - x2 = (centerxfrac + FixedMul(tx,xscale2))>>FRACBITS; x2--; + x2 = ((centerxfrac + FixedMul(tx2,xscale2))>>FRACBITS); + + if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier + return; + + // 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; -#endif - - if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier - return; if ((range = x2 - x1) <= 0) return; -#ifdef PROPERPAPER range++; // fencepost problem -#endif - scalestep = (yscale2 - yscale)/range; - xscale = -#ifdef PROPERPAPER - FixedDiv(range<>FRACBITS; @@ -1417,7 +1678,6 @@ static void R_ProjectSprite(mobj_t *thing) // off the left side if (x2 < 0) return; -#endif } if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY) @@ -1426,7 +1686,7 @@ static void R_ProjectSprite(mobj_t *thing) thing = thing->tracer; - if (thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW) + if (! R_ThingVisible(thing)) return; tr_x = thing->x - viewx; @@ -1538,20 +1798,16 @@ 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 - vis->x1 = x1 < 0 ? 0 : x1; - vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; - - // PORTAL SEMI-CLIPPING - if (portalrender) - { - if (vis->x1 < portalclipstart) - vis->x1 = portalclipstart; - if (vis->x2 >= portalclipend) - vis->x2 = portalclipend-1; - } + vis->x1 = x1 < portalclipstart ? portalclipstart : x1; + vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2; vis->xscale = xscale; //SoM: 4/17/2000 vis->sector = thing->subsector->sector; @@ -1608,7 +1864,7 @@ static void R_ProjectSprite(mobj_t *thing) vis->cut |= SC_FULLBRIGHT; if (vis->cut & SC_FULLBRIGHT - && (!vis->extra_colormap || !(vis->extra_colormap->fog & 1))) + && (!vis->extra_colormap || !(vis->extra_colormap->flags & CMF_FADEFULLBRIGHTSPRITES))) { // full bright: goggles vis->colormap = colormaps; @@ -1616,7 +1872,7 @@ static void R_ProjectSprite(mobj_t *thing) else { // diminished light - lindex = FixedMul(xscale, FixedDiv(640, vid.width))>>(LIGHTSCALESHIFT); + lindex = FixedMul(xscale, LIGHTRESOLUTIONFIX)>>(LIGHTSCALESHIFT); if (lindex >= MAXLIGHTSCALE) lindex = MAXLIGHTSCALE-1; @@ -1630,6 +1886,9 @@ static void R_ProjectSprite(mobj_t *thing) if (thing->subsector->sector->numlights) R_SplitSprite(vis); + if (oldthing->shadowscale && cv_shadow.value) + R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, tz); + // Debug ++objectsdrawn; } @@ -1753,18 +2012,12 @@ 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; - - // PORTAL SEMI-CLIPPING - if (portalrender) - { - if (vis->x1 < portalclipstart) - vis->x1 = portalclipstart; - if (vis->x2 >= portalclipend) - vis->x2 = portalclipend-1; - } + vis->x1 = x1 < portalclipstart ? portalclipstart : x1; + vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2; vis->xscale = xscale; //SoM: 4/17/2000 vis->sector = thing->subsector->sector; @@ -1818,7 +2071,7 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) mobj_t *thing; precipmobj_t *precipthing; // Tails 08-25-2002 INT32 lightnum; - fixed_t approx_dist, limit_dist, hoop_limit_dist; + fixed_t limit_dist, hoop_limit_dist; if (rendermode != render_soft) return; @@ -1851,35 +2104,10 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) // If a limit exists, handle things a tiny bit different. limit_dist = (fixed_t)(cv_drawdist.value) << FRACBITS; hoop_limit_dist = (fixed_t)(cv_drawdist_nights.value) << FRACBITS; - if (limit_dist || hoop_limit_dist) + for (thing = sec->thinglist; thing; thing = thing->snext) { - for (thing = sec->thinglist; thing; thing = thing->snext) - { - if (thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW) - continue; - - approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y); - - if (thing->sprite == SPR_HOOP) - { - if (hoop_limit_dist && approx_dist > hoop_limit_dist) - continue; - } - else - { - if (limit_dist && approx_dist > limit_dist) - continue; - } - + if (R_ThingVisibleWithinDist(thing, limit_dist, hoop_limit_dist)) R_ProjectSprite(thing); - } - } - else - { - // Draw everything in sector, no checks - for (thing = sec->thinglist; thing; thing = thing->snext) - if (!(thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW)) - R_ProjectSprite(thing); } // no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off @@ -1887,15 +2115,8 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) { for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) { - if (precipthing->precipflags & PCF_INVISIBLE) - continue; - - approx_dist = P_AproxDistance(viewx-precipthing->x, viewy-precipthing->y); - - if (approx_dist > limit_dist) - continue; - - R_ProjectPrecipitationSprite(precipthing); + if (R_PrecipThingVisible(precipthing, limit_dist)) + R_ProjectPrecipitationSprite(precipthing); } } } @@ -1945,6 +2166,9 @@ static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 e if (!(ds->cut & SC_LINKDRAW)) continue; + if (ds->cut & SC_SHADOW) + continue; + // reuse dsfirst... for (dsfirst = unsorted.prev; dsfirst != &unsorted; dsfirst = dsfirst->prev) { @@ -1952,6 +2176,10 @@ static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 e if (dsfirst->cut & SC_LINKDRAW) continue; + // don't connect to your shadow! + if (dsfirst->cut & SC_SHADOW) + continue; + // don't connect if it's not the tracer if (dsfirst->mobj != ds->mobj) continue; @@ -2590,6 +2818,55 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal) } } +/* Check if thing may be drawn from our current view. */ +boolean R_ThingVisible (mobj_t *thing) +{ + return (!( + thing->sprite == SPR_NULL || + ( thing->flags2 & (MF2_DONTDRAW) ) || + thing == r_viewmobj + )); +} + +boolean R_ThingVisibleWithinDist (mobj_t *thing, + fixed_t limit_dist, + fixed_t hoop_limit_dist) +{ + fixed_t approx_dist; + + if (! R_ThingVisible(thing)) + return false; + + approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y); + + if (thing->sprite == SPR_HOOP) + { + if (hoop_limit_dist && approx_dist > hoop_limit_dist) + return false; + } + else + { + if (limit_dist && approx_dist > limit_dist) + return false; + } + + return true; +} + +/* Check if precipitation may be drawn from our current view. */ +boolean R_PrecipThingVisible (precipmobj_t *precipthing, + fixed_t limit_dist) +{ + fixed_t approx_dist; + + if (( precipthing->precipflags & PCF_INVISIBLE )) + return false; + + approx_dist = P_AproxDistance(viewx-precipthing->x, viewy-precipthing->y); + + return ( approx_dist <= limit_dist ); +} + // // R_DrawMasked // @@ -3159,6 +3436,7 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) GETFLAG(DASHMODE) GETFLAG(FASTEDGE) GETFLAG(MULTIABILITY) + GETFLAG(NONIGHTSROTATION) #undef GETFLAG else // let's check if it's a sound, otherwise error out diff --git a/src/r_things.h b/src/r_things.h index b4ef66a62..217179148 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -51,12 +51,23 @@ void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight); // (only sprites from namelist are added or replaced) void R_AddSpriteDefs(UINT16 wadnum); +fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope); + //SoM: 6/5/2000: Light sprites correctly! void R_AddSprites(sector_t *sec, INT32 lightlevel); void R_InitSprites(void); void R_ClearSprites(void); void R_ClipSprites(drawseg_t* dsstart, portal_t* portal); +boolean R_ThingVisible (mobj_t *thing); + +boolean R_ThingVisibleWithinDist (mobj_t *thing, + fixed_t draw_dist, + fixed_t nights_draw_dist); + +boolean R_PrecipThingVisible (precipmobj_t *precipthing, + fixed_t precip_draw_dist); + /** Used to count the amount of masked elements * per portal to later group them in separate * drawnode lists. @@ -149,7 +160,8 @@ typedef enum SC_LINKDRAW = 1<<3, SC_FULLBRIGHT = 1<<4, SC_VFLIP = 1<<5, - SC_ISSCALED = 1>>6, + SC_ISSCALED = 1<<6, + SC_SHADOW = 1<<7, // masks SC_CUTMASK = SC_TOP|SC_BOTTOM, SC_FLAGMASK = ~SC_CUTMASK @@ -177,8 +189,16 @@ typedef struct vissprite_s fixed_t startfrac; // horizontal position of x1 fixed_t scale, sortscale; // sortscale only differs from scale for paper sprites and MF2_LINKDRAW fixed_t scalestep; // only for paper 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; patch_t *patch; diff --git a/src/s_sound.c b/src/s_sound.c index 0235d8376..d84e20ab4 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -117,6 +117,13 @@ static consvar_t surround = {"surround", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL consvar_t cv_resetmusic = {"resetmusic", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_resetmusicbyheader = {"resetmusicbyheader", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t cons_1upsound_t[] = { + {0, "Jingle"}, + {1, "Sound"}, + {0, NULL} +}; +consvar_t cv_1upsound = {"1upsound", "Jingle", CV_SAVE, cons_1upsound_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + // Sound system toggles, saved into the config consvar_t cv_gamedigimusic = {"digimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameDigiMusic_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_gamemidimusic = {"midimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameMIDIMusic_OnChange, 0, NULL, NULL, 0, 0, NULL}; @@ -287,6 +294,7 @@ void S_RegisterSoundStuff(void) CV_RegisterVar(&cv_samplerate); CV_RegisterVar(&cv_resetmusic); CV_RegisterVar(&cv_resetmusicbyheader); + CV_RegisterVar(&cv_1upsound); CV_RegisterVar(&cv_playsoundsifunfocused); CV_RegisterVar(&cv_playmusicifunfocused); CV_RegisterVar(&cv_gamesounds); @@ -1433,6 +1441,12 @@ static tic_t pause_starttic; /// Music Definitions /// ------------------------ +enum +{ + MUSICDEF_220, + MUSICDEF_221, +}; + musicdef_t soundtestsfx = { "_STSFX", // prevents exactly one valid track name from being used on the sound test "Sound Effects", @@ -1464,178 +1478,264 @@ static UINT16 W_CheckForMusicDefInPwad(UINT16 wadid) return INT16_MAX; // not found } -void S_LoadMusicDefs(UINT16 wadnum) +static void +MusicDefStrcpy (char *p, const char *s, size_t n, int version) { - UINT16 lump; - char *buf; - char *buf2; - char *stoken; - char *value; - size_t size; - INT32 i; - musicdef_t *def = NULL; - UINT16 line = 1; // for better error msgs - - lump = W_CheckForMusicDefInPwad(wadnum); - if (lump == INT16_MAX) - return; - - buf = W_CacheLumpNumPwad(wadnum, lump, PU_CACHE); - size = W_LumpLengthPwad(wadnum, lump); - - // for strtok - buf2 = malloc(size+1); - if (!buf2) - I_Error("S_LoadMusicDefs: No more free memory\n"); - M_Memcpy(buf2,buf,size); - buf2[size] = '\0'; - - stoken = strtok (buf2, "\r\n "); - // Find music def - while (stoken) + strlcpy(p, s, n); + if (version == MUSICDEF_220) { - /*if ((stoken[0] == '/' && stoken[1] == '/') - || (stoken[0] == '#')) // skip comments + while (( p = strchr(p, '_') )) + *p++ = ' '; // turn _ into spaces. + } +} + +static boolean +ReadMusicDefFields (UINT16 wadnum, int line, boolean fields, char *stoken, + musicdef_t **defp, int *versionp) +{ + musicdef_t *def; + int version; + + char *value; + char *textline; + int i; + + if (!stricmp(stoken, "lump")) + { + value = strtok(NULL, " "); + if (!value) { - stoken = strtok(NULL, "\r\n"); // skip end of line - if (def) - stoken = strtok(NULL, "\r\n= "); - else - stoken = strtok(NULL, "\r\n "); - line++; - } - else*/ if (!stricmp(stoken, "lump")) - { - value = strtok(NULL, "\r\n "); - - if (!value) - { - CONS_Alert(CONS_WARNING, "MUSICDEF: Lump '%s' is missing name. (file %s, line %d)\n", stoken, wadfiles[wadnum]->filename, line); - stoken = strtok(NULL, "\r\n"); // skip end of line - goto skip_lump; - } - - // No existing musicdefs - /*if (!musicdefstart) - { - musicdefstart = Z_Calloc(sizeof (musicdef_t), PU_STATIC, NULL); - STRBUFCPY(musicdefstart->name, value); - strlwr(musicdefstart->name); - def = musicdefstart; - //CONS_Printf("S_LoadMusicDefs: Initialized musicdef w/ song '%s'\n", def->name); - } - else*/ - { - musicdef_t *prev = NULL; - def = musicdefstart; - - // Search if this is a replacement - //CONS_Printf("S_LoadMusicDefs: Searching for song replacement...\n"); - while (def) - { - if (!stricmp(def->name, value)) - { - //CONS_Printf("S_LoadMusicDefs: Found song replacement '%s'\n", def->name); - break; - } - - prev = def; - def = def->next; - } - - // Nothing found, add to the end. - if (!def) - { - def = Z_Calloc(sizeof (musicdef_t), PU_STATIC, NULL); - STRBUFCPY(def->name, value); - strlwr(def->name); - def->bpm = TICRATE<<(FRACBITS-1); // FixedDiv((60*TICRATE)<next = def; - //CONS_Printf("S_LoadMusicDefs: Added song '%s'\n", def->name); - } - } - -skip_lump: - stoken = strtok(NULL, "\r\n "); - line++; + CONS_Alert(CONS_WARNING, + "MUSICDEF: Field '%s' is missing name. (file %s, line %d)\n", + stoken, wadfiles[wadnum]->filename, line); + return false; } else { - value = strtok(NULL, "\r\n= "); + musicdef_t *prev = NULL; + def = musicdefstart; + // Search if this is a replacement + //CONS_Printf("S_LoadMusicDefs: Searching for song replacement...\n"); + while (def) + { + if (!stricmp(def->name, value)) + { + //CONS_Printf("S_LoadMusicDefs: Found song replacement '%s'\n", def->name); + break; + } + + prev = def; + def = def->next; + } + + // Nothing found, add to the end. + if (!def) + { + def = Z_Calloc(sizeof (musicdef_t), PU_STATIC, NULL); + STRBUFCPY(def->name, value); + strlwr(def->name); + def->bpm = TICRATE<<(FRACBITS-1); // FixedDiv((60*TICRATE)<next = def; + //CONS_Printf("S_LoadMusicDefs: Added song '%s'\n", def->name); + } + + (*defp) = def; + } + } + else if (!stricmp(stoken, "version")) + { + if (fields)/* is this not the first field? */ + { + CONS_Alert(CONS_WARNING, + "MUSICDEF: Field '%s' must come first. (file %s, line %d)\n", + stoken, wadfiles[wadnum]->filename, line); + return false; + } + else + { + value = strtok(NULL, " "); if (!value) { - CONS_Alert(CONS_WARNING, "MUSICDEF: Field '%s' is missing value. (file %s, line %d)\n", stoken, wadfiles[wadnum]->filename, line); - stoken = strtok(NULL, "\r\n"); // skip end of line - goto skip_field; + CONS_Alert(CONS_WARNING, + "MUSICDEF: Field '%s' is missing version. (file %s, line %d)\n", + stoken, wadfiles[wadnum]->filename, line); + return false; } + else + { + if (strcasecmp(value, "2.2.0")) + (*versionp) = MUSICDEF_221; + } + } + } + else + { + version = (*versionp); + + if (version == MUSICDEF_220) + value = strtok(NULL, " ="); + else + { + value = strtok(NULL, ""); + + if (value) + { + // Find the equals sign. + value = strchr(value, '='); + } + } + + if (!value) + { + CONS_Alert(CONS_WARNING, + "MUSICDEF: Field '%s' is missing value. (file %s, line %d)\n", + stoken, wadfiles[wadnum]->filename, line); + return false; + } + else + { + def = (*defp); if (!def) { - CONS_Alert(CONS_ERROR, "MUSICDEF: No music definition before field '%s'. (file %s, line %d)\n", stoken, wadfiles[wadnum]->filename, line); - free(buf2); - return; + CONS_Alert(CONS_ERROR, + "MUSICDEF: No music definition before field '%s'. (file %s, line %d)\n", + stoken, wadfiles[wadnum]->filename, line); + return false; } + if (version != MUSICDEF_220) + { + // Skip the equals sign. + value++; + + // Now skip funny whitespace. + value += strspn(value, "\t "); + } + + textline = value; i = atoi(value); + /* based ignored lumps */ if (!stricmp(stoken, "usage")) { #if 0 // Ignore for now - STRBUFCPY(def->usage, value); - for (value = def->usage; *value; value++) - if (*value == '_') *value = ' '; // turn _ into spaces. - //CONS_Printf("S_LoadMusicDefs: Set usage to '%s'\n", def->usage); + STRBUFCPY(def->usage, textline); #endif } else if (!stricmp(stoken, "source")) { #if 0 // Ignore for now - STRBUFCPY(def->source, value); - for (value = def->source; *value; value++) - if (*value == '_') *value = ' '; // turn _ into spaces. - //CONS_Printf("S_LoadMusicDefs: Set source to '%s'\n", def->usage); + STRBUFCPY(def->source, textline); #endif } else if (!stricmp(stoken, "title")) { - STRBUFCPY(def->title, value); - for (value = def->title; *value; value++) - if (*value == '_') *value = ' '; // turn _ into spaces. - //CONS_Printf("S_LoadMusicDefs: Set title to '%s'\n", def->source); + MusicDefStrcpy(def->title, textline, + sizeof def->title, version); } else if (!stricmp(stoken, "alttitle")) { - STRBUFCPY(def->alttitle, value); - for (value = def->alttitle; *value; value++) - if (*value == '_') *value = ' '; // turn _ into spaces. - //CONS_Printf("S_LoadMusicDefs: Set alttitle to '%s'\n", def->source); + MusicDefStrcpy(def->alttitle, textline, + sizeof def->alttitle, version); } else if (!stricmp(stoken, "authors")) { - STRBUFCPY(def->authors, value); - for (value = def->authors; *value; value++) - if (*value == '_') *value = ' '; // turn _ into spaces. - //CONS_Printf("S_LoadMusicDefs: Set authors to '%s'\n", def->source); + MusicDefStrcpy(def->authors, textline, + sizeof def->authors, version); } else if (!stricmp(stoken, "soundtestpage")) { def->soundtestpage = (UINT8)i; } else if (!stricmp(stoken, "soundtestcond")) { // Convert to map number - if (value[0] >= 'A' && value[0] <= 'Z' && value[2] == '\0') - i = M_MapNumber(value[0], value[1]); + if (textline[0] >= 'A' && textline[0] <= 'Z' && textline[2] == '\0') + i = M_MapNumber(textline[0], textline[1]); def->soundtestcond = (INT16)i; } else if (!stricmp(stoken, "stoppingtime")) { - double stoppingtime = atof(value)*TICRATE; + double stoppingtime = atof(textline)*TICRATE; def->stoppingtics = (tic_t)stoppingtime; } else if (!stricmp(stoken, "bpm")) { - double bpm = atof(value); + double bpm = atof(textline); fixed_t bpmf = FLOAT_TO_FIXED(bpm); if (bpmf > 0) def->bpm = FixedDiv((60*TICRATE)<filename, line); + CONS_Alert(CONS_WARNING, + "MUSICDEF: Invalid field '%s'. (file %s, line %d)\n", + stoken, wadfiles[wadnum]->filename, line); } - -skip_field: - stoken = strtok(NULL, "\r\n= "); - line++; } } - free(buf2); - return; + return true; +} + +void S_LoadMusicDefs(UINT16 wadnum) +{ + UINT16 lumpnum; + char *lump; + char *musdeftext; + size_t size; + + char *lf; + char *stoken; + + size_t nlf; + size_t ncr; + + musicdef_t *def = NULL; + int version = MUSICDEF_220; + int line = 1; // for better error msgs + boolean fields = false; + + lumpnum = W_CheckForMusicDefInPwad(wadnum); + if (lumpnum == INT16_MAX) + return; + + lump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + size = W_LumpLengthPwad(wadnum, lumpnum); + + // Null-terminated MUSICDEF lump. + musdeftext = malloc(size+1); + if (!musdeftext) + I_Error("S_LoadMusicDefs: No more free memory for the parser\n"); + M_Memcpy(musdeftext, lump, size); + musdeftext[size] = '\0'; + + // Find music def + stoken = musdeftext; + for (;;) + { + lf = strpbrk(stoken, "\r\n"); + if (lf) + { + if (*lf == '\n') + nlf = 1; + else + nlf = 0; + *lf++ = '\0';/* now we can delimit to here */ + } + + stoken = strtok(stoken, " "); + if (stoken) + { + if (! ReadMusicDefFields(wadnum, line, fields, stoken, + &def, &version)) + break; + fields = true; + } + + if (lf) + { + do + { + line += nlf; + ncr = strspn(lf, "\r");/* skip CR */ + lf += ncr; + nlf = strspn(lf, "\n"); + lf += nlf; + } + while (nlf || ncr) ; + + stoken = lf;/* now the next nonempty line */ + } + else + break;/* EOF */ + } + + free(musdeftext); } // diff --git a/src/s_sound.h b/src/s_sound.h index 18f2d3743..9a4cbe48b 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -35,6 +35,8 @@ extern consvar_t cv_numChannels; extern consvar_t cv_resetmusic; extern consvar_t cv_resetmusicbyheader; +extern consvar_t cv_1upsound; + #define RESETMUSIC (!modeattacking && \ (cv_resetmusicbyheader.value ? \ (mapheaderinfo[gamemap-1]->musforcereset != -1 ? mapheaderinfo[gamemap-1]->musforcereset : cv_resetmusic.value) \ diff --git a/src/screen.c b/src/screen.c index 5bb304c08..fcf6c6b0b 100644 --- a/src/screen.c +++ b/src/screen.c @@ -360,10 +360,13 @@ void SCR_Recalc(void) vid.fsmalldupy = vid.smalldupy*FRACUNIT; #endif - // toggle off automap because some screensize-dependent values will + // toggle off (then back on) the automap because some screensize-dependent values will // be calculated next time the automap is activated. if (automapactive) - AM_Stop(); + { + am_recalc = true; + AM_Start(); + } // set the screen[x] ptrs on the new vidbuffers V_Init(); diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 2b8633e5b..13e2423c4 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -110,7 +110,6 @@ static SDL_bool disable_fullscreen = SDL_FALSE; #define USE_FULLSCREEN (disable_fullscreen||!allow_fullscreen)?0:cv_fullscreen.value static SDL_bool disable_mouse = SDL_FALSE; #define USE_MOUSEINPUT (!disable_mouse && cv_usemouse.value && havefocus) -#define IGNORE_MOUSE (!cv_alwaysgrabmouse.value && (menuactive || paused || con_destlines || chat_on || gamestate != GS_LEVEL)) #define MOUSE_MENU false //(!disable_mouse && cv_usemouse.value && menuactive && !USE_FULLSCREEN) #define MOUSEBUTTONS_MAX MOUSEBUTTONS @@ -362,6 +361,17 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code) return 0; } +static boolean IgnoreMouse(void) +{ + if (cv_alwaysgrabmouse.value) + return false; + if (menuactive) + return !M_MouseNeeded(); + if (paused || con_destlines || chat_on || gamestate != GS_LEVEL) + return true; + return false; +} + static void SDLdoGrabMouse(void) { SDL_ShowCursor(SDL_DISABLE); @@ -388,7 +398,7 @@ void I_UpdateMouseGrab(void) { if (SDL_WasInit(SDL_INIT_VIDEO) == SDL_INIT_VIDEO && window != NULL && SDL_GetMouseFocus() == window && SDL_GetKeyboardFocus() == window - && USE_MOUSEINPUT && !IGNORE_MOUSE) + && USE_MOUSEINPUT && !IgnoreMouse()) SDLdoGrabMouse(); } @@ -596,7 +606,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) } //else firsttimeonmouse = SDL_FALSE; - if (USE_MOUSEINPUT && !IGNORE_MOUSE) + if (USE_MOUSEINPUT && !IgnoreMouse()) SDLdoGrabMouse(); } else if (!mousefocus && !kbfocus) @@ -647,7 +657,7 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt) if (USE_MOUSEINPUT) { - if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window) || (IGNORE_MOUSE && !firstmove)) + if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window) || (IgnoreMouse() && !firstmove)) { SDLdoUngrabMouse(); firstmove = false; @@ -700,7 +710,7 @@ static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type) // this apparently makes a mouse button down event but not a mouse button up event, // resulting in whatever key was pressed down getting "stuck" if we don't ignore it. // -- Monster Iestyn (28/05/18) - if (SDL_GetMouseFocus() != window || IGNORE_MOUSE) + if (SDL_GetMouseFocus() != window || IgnoreMouse()) return; /// \todo inputEvent.button.which @@ -1082,7 +1092,7 @@ void I_StartupMouse(void) } else firsttimeonmouse = SDL_FALSE; - if (cv_usemouse.value && !IGNORE_MOUSE) + if (cv_usemouse.value && !IgnoreMouse()) SDLdoGrabMouse(); else SDLdoUngrabMouse(); diff --git a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj index ab3157c44..2b03cb8d4 100644 --- a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj +++ b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj @@ -1219,7 +1219,7 @@ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CURRENT_PROJECT_VERSION = 2.2.0; + CURRENT_PROJECT_VERSION = 2.2.1; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", NORMALSRB2, @@ -1231,7 +1231,7 @@ C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CURRENT_PROJECT_VERSION = 2.2.0; + CURRENT_PROJECT_VERSION = 2.2.1; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_PREPROCESSOR_DEFINITIONS = ( diff --git a/src/st_stuff.c b/src/st_stuff.c index 4676506fc..d99d564c8 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1278,13 +1278,15 @@ void ST_preDrawTitleCard(void) // void ST_runTitleCard(void) { + boolean run = !(paused || P_AutoPause()); + if (!G_IsTitleCardAvailable()) return; if (lt_ticker >= (lt_endtime + TICRATE)) return; - if (!(paused || P_AutoPause())) + if (run || (lt_ticker < PRELEVELTIME)) { // tick lt_ticker++; diff --git a/src/v_video.c b/src/v_video.c index 4785a1541..81625ff9e 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -3244,7 +3244,6 @@ Unoptimized version #endif } -// Taken from my videos-in-SRB2 project // Generates a color look-up table // which has up to 64 colors at each channel // (see the defines in v_video.h) @@ -3261,7 +3260,7 @@ void InitColorLUT(RGBA_t *palette) for (r = 0; r < CLUTSIZE; r++) for (g = 0; g < CLUTSIZE; g++) for (b = 0; b < CLUTSIZE; b++) - colorlookup[r][g][b] = NearestColor(r << SHIFTCOLORBITS, g << SHIFTCOLORBITS, b << SHIFTCOLORBITS); + colorlookup[r][g][b] = NearestPaletteColor(r << SHIFTCOLORBITS, g << SHIFTCOLORBITS, b << SHIFTCOLORBITS, palette); clutinit = true; lastpalette = palette; } diff --git a/src/v_video.h b/src/v_video.h index eb75a414f..bc19f6e99 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -37,10 +37,7 @@ cv_allcaps; // Allocates buffer screens, call before R_Init. void V_Init(void); -// Taken from my videos-in-SRB2 project -// Generates a color look-up table -// which has up to 64 colors at each channel - +// Color look-up table #define COLORBITS 6 #define SHIFTCOLORBITS (8-COLORBITS) #define CLUTSIZE (1<real); - block->prev->next = block->next; - block->next->prev = block->prev; - free(block); #ifdef VALGRIND_DESTROY_MEMPOOL VALGRIND_DESTROY_MEMPOOL(block); #endif + block->prev->next = block->next; + block->next->prev = block->prev; + free(block); } /** malloc() that doesn't accept failure. @@ -317,13 +317,9 @@ void *Z_MallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits) // The mem header lives 'sizeof (memhdr_t)' bytes before given. hdr = (memhdr_t *)((UINT8 *)given - sizeof *hdr); -#ifdef VALGRIND_CREATE_MEMPOOL - VALGRIND_CREATE_MEMPOOL(block, padsize, Z_calloc); +#ifdef HAVE_VALGRIND Z_calloc = false; #endif -#ifdef VALGRIND_MEMPOOL_ALLOC - VALGRIND_MEMPOOL_ALLOC(block, hdr, size + sizeof *hdr); -#endif block->next = head.next; block->prev = &head; @@ -341,6 +337,13 @@ void *Z_MallocAlign(size_t size, INT32 tag, void *user, INT32 alignbits) block->size = blocksize; block->realsize = size; +#ifdef VALGRIND_CREATE_MEMPOOL + VALGRIND_CREATE_MEMPOOL(block, padsize, Z_calloc); +#endif +//#ifdef VALGRIND_MEMPOOL_ALLOC +// VALGRIND_MEMPOOL_ALLOC(block, hdr, size + sizeof *hdr); +//#endif + hdr->id = ZONEID; hdr->block = block; diff --git a/tools/musicdef-2.2.1/Makefile b/tools/musicdef-2.2.1/Makefile new file mode 100644 index 000000000..403d27b04 --- /dev/null +++ b/tools/musicdef-2.2.1/Makefile @@ -0,0 +1 @@ +musicdef-2.2.1: diff --git a/tools/musicdef-2.2.1/musicdef-2.2.1.c b/tools/musicdef-2.2.1/musicdef-2.2.1.c new file mode 100644 index 000000000..65d434c8a --- /dev/null +++ b/tools/musicdef-2.2.1/musicdef-2.2.1.c @@ -0,0 +1,77 @@ +/* +Copyright 2020 James R. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#ifdef _WIN32 +#define strcasecmp _stricmp +#else +#include +#endif + +int +main (int ac, char **av) +{ + char line[256]; + char buf[256]; + char *var; + char *val; + char *p; + int n; + (void)ac; + (void)av; + fputs( + "Copyright 2020 James R.\n" + "All rights reserved.\n" + "\n" + "Usage: musicdef-2.2.1 < old-MUSICDEF > new-MUSICDEF\n" + "\n" + ,stderr); + while (fgets(line, sizeof line, stdin)) + { + memcpy(buf, line, sizeof buf); + if (( var = strtok(buf, " =") )) + { + if (!( + strcasecmp(var, "TITLE") && + strcasecmp(var, "ALTTITLE") && + strcasecmp(var, "AUTHORS") + )){ + if (( val = strtok(0, "") )) + { + for (p = val; ( p = strchr(p, '_') ); ) + { + n = strspn(p, "_"); + memset(p, ' ', n); + p += n; + } + printf("%s %s", var, val); + continue; + } + } + } + fputs(line, stdout); + } + return 0; +}