diff --git a/src/command.c b/src/command.c index 8c72eeaa3..dbdaceab5 100644 --- a/src/command.c +++ b/src/command.c @@ -1458,7 +1458,7 @@ 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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } netid = READUINT16(*p); diff --git a/src/console.c b/src/console.c index 0b55e7e2c..ba5ba71df 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" @@ -806,6 +807,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) { @@ -958,23 +986,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; diff --git a/src/d_clisrv.c b/src/d_clisrv.c index acf1d1fb6..ee4e62b91 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,7 +393,7 @@ static void ExtraDataTicker(void) { if (server) { - SendKick(i, KICK_MSG_CON_FAIL); + 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]); @@ -437,6 +439,9 @@ 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); @@ -1058,7 +1063,7 @@ static void SV_SendResynch(INT32 node) if (resynch_score[node] > (unsigned)cv_resynchattempts.value*250) { - SendKick(nodetoplayer[node], KICK_MSG_CON_FAIL); + SendKick(nodetoplayer[node], KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); resynch_score[node] = 0; } } @@ -1627,6 +1632,7 @@ static void CL_LoadReceivedSavegame(void) paused = false; demoplayback = false; + titlemapinaction = TITLEMAP_OFF; titledemo = false; automapactive = false; @@ -2412,6 +2418,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)); } // @@ -2419,7 +2426,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. @@ -2432,9 +2439,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); @@ -2556,6 +2560,7 @@ void CL_Reset(void) multiplayer = false; servernode = 0; server = true; + resynch_local_inprogress = false; doomcom->numnodes = 1; doomcom->numslots = 1; SV_StopServer(); @@ -2829,9 +2834,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)) { @@ -2891,6 +2899,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]); @@ -2910,7 +2919,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: @@ -2959,7 +2969,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; @@ -3000,6 +3010,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); } @@ -3008,6 +3036,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 }; @@ -3077,6 +3108,7 @@ static void ResetNode(INT32 node) nodewaiting[node] = 0; playerpernode[node] = 0; sendingsavegame[node] = false; + SV_InitResynchVars(node); } void SV_ResetServer(void) @@ -3092,13 +3124,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 @@ -3106,6 +3133,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. } @@ -3196,6 +3224,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) { @@ -3212,13 +3271,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3227,15 +3289,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) @@ -3253,29 +3334,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 } @@ -3284,11 +3403,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++) { @@ -3297,68 +3412,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 @@ -3385,8 +3454,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++; } } @@ -3512,8 +3579,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 || @@ -3525,9 +3595,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.")); @@ -3542,7 +3612,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; @@ -3983,7 +4053,7 @@ static void HandlePacketFromPlayer(SINT8 node) } else { - SendKick(netconsole, KICK_MSG_CON_FAIL); + 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))); @@ -4102,6 +4172,7 @@ static void HandlePacketFromPlayer(SINT8 node) kickmsg = KICK_MSG_TIMEOUT; else kickmsg = KICK_MSG_PLAYER_QUIT; + kickmsg |= KICK_MSG_KEEP_BODY; SendKick(netconsole, kickmsg); nodetoplayer[node] = -1; @@ -4123,7 +4194,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHEND", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } resynch_local_inprogress = false; @@ -4141,7 +4212,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } @@ -4201,7 +4272,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHING", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } resynch_local_inprogress = true; @@ -4213,7 +4284,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } @@ -4237,7 +4308,7 @@ static void HandlePacketFromPlayer(SINT8 node) { CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL); + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); break; } if (client) @@ -4433,7 +4504,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) { G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); @@ -4609,6 +4680,11 @@ static void Local_Maketic(INT32 realtics) localcmds.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; @@ -4642,28 +4718,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++; @@ -4785,7 +4869,7 @@ static inline void PingUpdate(void) // ok your net has been bad for too long, you deserve to die. { pingtimeout[i] = 0; - SendKick(i, KICK_MSG_PING_HIGH); + SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); } } /* diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 408d0f8dd..df93fe31d 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -485,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 { @@ -512,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); diff --git a/src/d_main.c b/src/d_main.c index 8a7c446bb..15d3c8041 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1045,10 +1045,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(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 75f1296ab..6b3662710 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); @@ -1124,7 +1125,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } } @@ -1199,7 +1200,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; @@ -1482,7 +1483,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (kick) { CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s (team: %d), color: %d)\n"), player_names[playernum], p->ctfteam, p->skincolor); - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } } @@ -2022,7 +2023,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2133,7 +2134,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2208,7 +2209,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2271,7 +2272,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -2618,7 +2619,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } if (NetPacket.packet.verification) // Special marker that the server sent the request @@ -2627,7 +2628,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } playernum = NetPacket.packet.playernum; @@ -2660,7 +2661,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } return; } @@ -2719,7 +2720,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) if (server && ((NetPacket.packet.newteam < 0 || NetPacket.packet.newteam > 3) || error)) { CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); } //Safety first! @@ -3011,7 +3012,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3061,7 +3062,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3135,7 +3136,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); Z_Free(mymotd); return; } @@ -3191,7 +3192,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3349,7 +3350,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick) { CONS_Alert(CONS_WARNING, M_GetText("Illegal addfile command received from %s\n"), player_names[playernum]); - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -3398,7 +3399,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -4191,7 +4192,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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } diff --git a/src/d_player.h b/src/d_player.h index 62f38193f..db55a9913 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -510,6 +510,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 091371122..4c36639a2 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1629,6 +1629,11 @@ static void readlevelheader(MYFILE *f, INT32 num) mapheaderinfo[num-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")) @@ -6015,6 +6020,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", @@ -8841,7 +8847,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. @@ -9421,7 +9427,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}, @@ -9436,7 +9442,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) @@ -9603,7 +9609,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 @@ -9664,10 +9670,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. diff --git a/src/doomdef.h b/src/doomdef.h index 0a98c874a..3d02871e4 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -98,8 +98,8 @@ #ifdef GETTEXT #include -#include #endif +#include // locale should not be dependent on GETTEXT -- 11/01/20 Monster Iestyn #include #include @@ -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); @@ -546,6 +546,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..c7c12632a 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. diff --git a/src/f_finale.c b/src/f_finale.c index bbee48bdc..99ff586f4 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1165,6 +1165,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 +1234,7 @@ static const char *credits[] = { "Thomas \"Shadow Hog\" Igoe", "Alexander \"DrTapeworm\" Moench-Ford", "\"Kaito Sinclaire\"", + "\"QueenDelta\"", "Wessel \"sphere\" Smit", "\"Spazzo\"", "\"SSNTails\"", diff --git a/src/g_game.c b/src/g_game.c index f7778df8f..f5d7cd2fb 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]) @@ -2412,6 +2414,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 +2428,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 +2500,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 +2654,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 +2768,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 +2999,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 +3045,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 +3087,6 @@ void G_AddPlayer(INT32 playernum) } } - p->jointime = 0; p->playerstate = PST_REBORN; p->height = mobjinfo[MT_PLAYER].height; @@ -3101,6 +3109,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; @@ -4761,6 +4771,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/hardware/hw_main.c b/src/hardware/hw_main.c index 74be53db5..2a5eae9f1 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); @@ -5407,6 +5282,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 +5308,7 @@ static void HWR_DrawSprites(void) HWR_DrawSprite(spr); } } + } } } } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index a1f591847..c51499282 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,7 +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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } @@ -673,7 +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) - SendKick(playernum, KICK_MSG_CON_FAIL); + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } } @@ -1021,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) @@ -1082,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. @@ -1096,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; } } @@ -1116,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! @@ -1135,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); } @@ -1337,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 @@ -1648,12 +1647,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) @@ -1794,21 +1790,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')) @@ -1855,7 +1847,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. - } @@ -2378,7 +2369,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"); @@ -2577,7 +2568,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"); @@ -2701,7 +2692,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"); @@ -2732,7 +2723,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"); @@ -2840,7 +2831,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 bd672af12..de6ff475c 100644 --- a/src/info.c +++ b/src/info.c @@ -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 @@ -7850,7 +7851,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 324795d45..628335635 100644 --- a/src/info.h +++ b/src/info.h @@ -2021,6 +2021,7 @@ typedef enum state S_SIGNSTOP, S_SIGNBOARD, S_EGGMANSIGN, + S_CLEARSIGN, // Spike Ball S_SPIKEBALL1, 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 f950ff2fe..48f2e20a8 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -87,7 +87,7 @@ deny: CONS_Alert(CONS_WARNING, M_GetText("Illegal lua command received from %s\n"), player_names[playernum]); if (server) - SendKick(playernum, KICK_MSG_CON_FAIL); + 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_maplib.c b/src/lua_maplib.c index f74314c70..99ee1d145 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -2034,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..1dd4c45b5 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -362,6 +362,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); @@ -701,6 +703,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/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 3305519c3..62bea7ae0 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1446,8 +1446,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 @@ -1583,32 +1584,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 }; @@ -10631,8 +10633,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 { @@ -10640,10 +10642,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 diff --git a/src/m_misc.c b/src/m_misc.c index a5091c257..3026f6654 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,40 @@ 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; +} diff --git a/src/m_misc.h b/src/m_misc.h index e0a73e0b7..3fb9f031a 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -101,6 +101,14 @@ 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); + // 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 bd7b81d40..db297e684 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -746,6 +746,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 @@ -5177,6 +5180,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. @@ -5187,33 +5192,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 (skincount = 0; skincount < numskins; skincount++) + if (!skincheck(skincount)) + 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]; @@ -5221,21 +5218,33 @@ 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; + 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 + P_SetMobjState(ov, actor->info->missilestate); // S_CLEARSIGN + } } else // Eggman face { ov->color = SKINCOLOR_NONE; + ov->skin = NULL; 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 8334a2a4e..8090b7406 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1869,6 +1869,7 @@ 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; } /** Prints death messages relating to a dying or hit player. @@ -2256,9 +2257,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_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 4ed19a9ff..4649acf06 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3400,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 @@ -3906,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; } } @@ -10557,6 +10561,22 @@ 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 + if ( + type == MT_PLAYER || + type == MT_ROLLOUTROCK || + type == MT_EGGMOBILE4_MACE || + (type >= MT_SMALLMACE && type <= MT_REDSPRINGBALL) || + (mobj->flags & (MF_ENEMY|MF_BOSS)) + ) + mobj->shadowscale = FRACUNIT; + else if ( + type >= MT_RING && type <= MT_FLINGEMERALD && type != MT_EMERALDSPAWN + ) + mobj->shadowscale = 2*FRACUNIT/3; + else + mobj->shadowscale = 0; + #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! @@ -11098,7 +11118,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 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_saveg.c b/src/p_saveg.c index 2b6a474bf..853856880 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -255,6 +255,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); @@ -446,6 +447,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); diff --git a/src/p_setup.c b/src/p_setup.c index 09e78beaa..729ee00c2 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; @@ -3096,7 +3097,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++; @@ -3118,7 +3119,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) @@ -3296,7 +3297,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); } diff --git a/src/p_spec.c b/src/p_spec.c index 9defc33a0..84ccc29a5 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4426,10 +4426,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) @@ -6931,7 +6939,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 diff --git a/src/p_tick.c b/src/p_tick.c index 9b56ee1c2..0b30ff42d 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) + 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 fb59efab7..a5ccf467f 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); @@ -3153,7 +3160,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; @@ -9183,7 +9190,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! @@ -9201,7 +9208,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 @@ -10226,7 +10233,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; @@ -11747,6 +11754,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; @@ -12199,6 +12208,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 @@ -12242,12 +12256,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..871816672 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 diff --git a/src/r_defs.h b/src/r_defs.h index eade61db5..88d418fc9 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -144,7 +144,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_main.c b/src/r_main.c index 3c6aaf6a6..f4daac834 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -128,12 +128,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}; @@ -706,9 +701,9 @@ subsector_t *R_PointInSubsector(fixed_t x, fixed_t y) } // -// R_IsPointInSubsector, same as above but returns 0 if not in subsector +// R_PointInSubsectorOrNull, same as above but returns 0 if not in subsector // -subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y) +subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y) { node_t *node; INT32 side, i; @@ -1223,12 +1218,8 @@ void R_RegisterEngineStuff(void) 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 998bb50ef..d72e94973 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -64,7 +64,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,12 +76,8 @@ 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; diff --git a/src/r_things.c b/src/r_things.c index dd96e4f16..8fa0f2d0e 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -889,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); } @@ -934,7 +935,7 @@ static void R_DrawVisSprite(vissprite_t *vis) else { // Non-paper drawing loop - for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan) { #ifdef RANGECHECK texturecolumn = frac>>FRACBITS; @@ -1107,6 +1108,265 @@ static void R_SplitSprite(vissprite_t *sprite) } } +// +// R_GetShadowZ(thing, shadowslope) +// Get the first visible floor below the object for shadows +// shadowslope is filled with the floor's slope, if provided +// +fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) +{ + fixed_t z, floorz = INT32_MIN; + pslope_t *slope, *floorslope = NULL; + msecnode_t *node; + sector_t *sector; + ffloor_t *rover; + + for (node = thing->touching_sectorlist; node; node = node->m_sectorlist_next) + { + sector = node->m_sector; + + slope = (sector->heightsec != -1) ? NULL : sector->f_slope; + z = slope ? P_GetZAt(slope, thing->x, thing->y) : ( + (sector->heightsec != -1) ? sectors[sector->heightsec].floorheight : sector->floorheight + ); + + if (z < thing->z+thing->height/2 && z > floorz) + { + floorz = z; + floorslope = slope; + } + + if (sector->ffloors) + for (rover = sector->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES) || (rover->alpha < 90 && !(rover->flags & FF_SWIMMABLE))) + continue; + + z = *rover->t_slope ? P_GetZAt(*rover->t_slope, thing->x, thing->y) : *rover->topheight; + if (z < thing->z+thing->height/2 && z > floorz) + { + floorz = z; + floorslope = *rover->t_slope; + } + } + } + + if (thing->floorz > floorz + (!floorslope ? 0 : FixedMul(abs(floorslope->zdelta), thing->radius*3/2))) + { + floorz = thing->floorz; + floorslope = NULL; + } + +#if 0 // Unfortunately, this drops CEZ2 down to sub-17 FPS on my i7. +//#ifdef POLYOBJECTS + // Check polyobjects and see if floorz needs to be altered, for rings only because they don't update floorz + if (thing->type == MT_RING) + { + INT32 xl, xh, yl, yh, bx, by; + + xl = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + validcount++; + + for (by = yl; by <= yh; by++) + for (bx = xl; bx <= xh; bx++) + { + INT32 offset; + polymaplink_t *plink; // haleyjd 02/22/06 + + if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight) + continue; + + offset = by*bmapwidth + bx; + + // haleyjd 02/22/06: consider polyobject lines + plink = polyblocklinks[offset]; + + while (plink) + { + polyobj_t *po = plink->po; + + if (po->validcount != validcount) // if polyobj hasn't been checked + { + po->validcount = validcount; + + if (!P_MobjInsidePolyobj(po, thing) || !(po->flags & POF_RENDERPLANES)) + { + plink = (polymaplink_t *)(plink->link.next); + continue; + } + + // We're inside it! Yess... + z = po->lines[0]->backsector->ceilingheight; + + if (z < thing->z+thing->height/2 && z > floorz) + { + floorz = z; + floorslope = NULL; + } + } + plink = (polymaplink_t *)(plink->link.next); + } + } + } +#endif + + if (shadowslope != NULL) + *shadowslope = floorslope; + + return floorz; +} + +static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t tx, fixed_t tz) +{ + vissprite_t *shadow; + patch_t *patch; + fixed_t xscale, yscale, shadowxscale, shadowyscale, shadowskew, x1, x2; + INT32 light = 0; + fixed_t scalemul; UINT8 trans; + fixed_t floordiff; + fixed_t floorz; + pslope_t *floorslope; + + floorz = R_GetShadowZ(thing, &floorslope); + + if (abs(floorz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes + + floordiff = abs(thing->z - floorz); + + trans = floordiff / (100*FRACUNIT) + 3; + if (trans >= 9) return; + + scalemul = FixedMul(FRACUNIT - floordiff/640, scale); + + 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 < 0 ? 0 : x1; + shadow->x2 = x2 >= viewwidth ? viewwidth-1 : x2; + + // PORTAL SEMI-CLIPPING + if (portalrender) + { + if (shadow->x1 < portalclipstart) + shadow->x1 = portalclipstart; + if (shadow->x2 >= portalclipend) + shadow->x2 = portalclipend-1; + } + + shadow->xscale = FixedMul(xscale, shadowxscale); //SoM: 4/17/2000 + shadow->scale = FixedMul(yscale, shadowyscale); + shadow->sector = vis->sector; + shadow->szt = (INT16)((centeryfrac - FixedMul(shadow->gzt - viewz, yscale))>>FRACBITS); + shadow->sz = (INT16)((centeryfrac - FixedMul(shadow->gz - viewz, yscale))>>FRACBITS); + shadow->cut = SC_ISSCALED|SC_SHADOW; //check this + + shadow->startfrac = 0; + //shadow->xiscale = 0x7ffffff0 / (shadow->xscale/2); + shadow->xiscale = (patch->width<x1 > x1) + shadow->startfrac += shadow->xiscale*(shadow->x1-x1); + + // reusing x1 variable + x1 += (x2-x1)/2; + shadow->shear.offset = shadow->x1-x1; + + if (thing->subsector->sector->numlights) + { + INT32 lightnum; +#ifdef ESLOPE // R_GetPlaneLight won't work on sloped lights! + light = thing->subsector->sector->numlights - 1; + + for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) { + fixed_t h = thing->subsector->sector->lightlist[lightnum].slope ? P_GetZAt(thing->subsector->sector->lightlist[lightnum].slope, thing->x, thing->y) + : thing->subsector->sector->lightlist[lightnum].height; + if (h <= shadow->gzt) { + light = lightnum - 1; + break; + } + } +#else + light = R_GetPlaneLight(thing->subsector->sector, shadow->gzt, false); +#endif + } + + if (thing->subsector->sector->numlights) + shadow->extra_colormap = *thing->subsector->sector->lightlist[light].extra_colormap; + else + shadow->extra_colormap = thing->subsector->sector->extra_colormap; + + shadow->transmap = transtables + (trans<colormap = scalelight[0][0]; // full dark! + + objectsdrawn++; +} + // // R_ProjectSprite // Generates a vissprite for a thing @@ -1144,6 +1404,8 @@ static void R_ProjectSprite(mobj_t *thing) 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; @@ -1178,7 +1440,7 @@ static void R_ProjectSprite(mobj_t *thing) gxt = -FixedMul(tr_x, viewsin); gyt = FixedMul(tr_y, viewcos); - tx = -(gyt + gxt); + basetx = tx = -(gyt + gxt); // too far off the side? if (!papersprite && abs(tx) > tz<<2) // papersprite clipping is handled later @@ -1548,6 +1810,8 @@ static void R_ProjectSprite(mobj_t *thing) 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 @@ -1640,6 +1904,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; } @@ -1763,6 +2030,9 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis->pzt = vis->pz + vis->thingheight; vis->texturemid = vis->gzt - viewz; vis->scalestep = 0; + vis->paperdistance = 0; + vis->shear.tan = 0; + vis->shear.offset = 0; vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; @@ -1955,6 +2225,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) { @@ -1962,6 +2235,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; diff --git a/src/r_things.h b/src/r_things.h index d6af0fefa..c1933e662 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -51,6 +51,8 @@ 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); @@ -149,7 +151,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 @@ -182,6 +185,11 @@ typedef struct vissprite_s 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/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; +}