diff --git a/CMakeLists.txt b/CMakeLists.txt index cb93d22f0..31597f399 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0) project(SRB2 - VERSION 2.1.14 + VERSION 2.1.17 LANGUAGES C) if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) diff --git a/appveyor.yml b/appveyor.yml index e0ee99c61..25b95d292 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.1.16.{branch}-{build} +version: 2.1.17.{branch}-{build} os: MinGW environment: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e0031b87f..6a8b7e3f1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -393,18 +393,25 @@ if(${SRB2_CONFIG_HWRENDER} AND ${SRB2_CONFIG_STATIC_OPENGL}) endif() if(${SRB2_CONFIG_USEASM}) + #SRB2_ASM_FLAGS can be used to pass flags to either nasm or yasm. + if(${CMAKE_SYSTEM} MATCHES "Linux") + set(SRB2_ASM_FLAGS "-DLINUX ${SRB2_ASM_FLAGS}") + endif() + if(${SRB2_CONFIG_YASM}) set(CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS} nas) + set(CMAKE_ASM_YASM_FLAGS "${SRB2_ASM_FLAGS}" CACHE STRING "Flags used by the assembler during all build types.") enable_language(ASM_YASM) else() set(CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS} nas) + set(CMAKE_ASM_NASM_FLAGS "${SRB2_ASM_FLAGS}" CACHE STRING "Flags used by the assembler during all build types.") enable_language(ASM_NASM) endif() set(SRB2_USEASM ON) add_definitions(-DUSEASM) else() set(SRB2_USEASM OFF) - add_definitions(-DNOASM -DNONX86) + add_definitions(-DNONX86 -DNORUSEASM) endif() # Targets diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b5f36ad42..574cce7ab 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -72,14 +72,21 @@ #define MAX_REASONLENGTH 30 boolean server = true; // true or false but !server == client +#define client (!server) boolean nodownload = false; static boolean serverrunning = false; INT32 serverplayer = 0; char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support) -// server specific vars +// Server specific vars UINT8 playernode[MAXPLAYERS]; +// Minimum timeout for sending the savegame +// The actual timeout will be longer depending on the savegame length +tic_t jointimeout = (10*TICRATE); +static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame? +static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout? + #ifdef NEWPING UINT16 pingmeasurecount = 1; UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. @@ -108,7 +115,7 @@ static UINT8 resynch_local_inprogress = false; // WE are desynched and getting p static UINT8 player_joining = false; UINT8 hu_resynching = 0; -// client specific +// Client specific static ticcmd_t localcmds; static ticcmd_t localcmds2; static boolean cl_packetmissed; @@ -404,7 +411,7 @@ static void ExtraDataTicker(void) // If you are a client, you can safely forget the net commands for this tic // If you are the server, you need to remember them until every client has been aknowledged, // because if you need to resend a PT_SERVERTICS packet, you need to put the commands in it - if (!server) + if (client) D_FreeTextcmd(gametic); } @@ -912,7 +919,7 @@ static inline void resynch_read_others(resynchend_pak *p) for (i = 0; i < MAXPLAYERS; ++i) { // We don't care if they're in the game or not, just write all the data. - players[i].spectator = !(loc_ingame & i<ctfteam[i]); // no, 0 does not mean spectator, at least not in Match players[i].score = (UINT32)LONG(p->score[i]); players[i].numboxes = SHORT(p->numboxes[i]); @@ -1033,6 +1040,9 @@ static void SV_AcknowledgeResynchAck(INT32 node, UINT8 rsg) resynch_status[node] &= ~(1<>10)); - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, - va("%3.1fK/s ", ((double)getbps)/1024)); + if (lastfilenum != -1) + { + cltext = M_GetText("Downloading game state..."); + Net_GetNetStat(); + V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + va(" %4uK",fileneeded[lastfilenum].currentsize>>10)); + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + va("%3.1fK/s ", ((double)getbps)/1024)); + } + else + cltext = M_GetText("Waiting to download game state..."); break; #endif case CL_ASKJOIN: @@ -1146,25 +1161,31 @@ static inline void CL_DrawConnectionStatus(void) } else { - INT32 dldlength; - static char tempname[32]; + if (lastfilenum != -1) + { + INT32 dldlength; + static char tempname[32]; - Net_GetNetStat(); - dldlength = (INT32)((fileneeded[lastfilenum].currentsize/(double)fileneeded[lastfilenum].totalsize) * 256); - if (dldlength > 256) - dldlength = 256; - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 96); + Net_GetNetStat(); + dldlength = (INT32)((fileneeded[lastfilenum].currentsize/(double)fileneeded[lastfilenum].totalsize) * 256); + if (dldlength > 256) + dldlength = 256; + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 96); - memset(tempname, 0, sizeof(tempname)); - nameonly(strncpy(tempname, fileneeded[lastfilenum].filename, 31)); + memset(tempname, 0, sizeof(tempname)); + nameonly(strncpy(tempname, fileneeded[lastfilenum].filename, 31)); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, - va(M_GetText("Downloading \"%s\""), tempname)); - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, - va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,fileneeded[lastfilenum].totalsize>>10)); - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, - va("%3.1fK/s ", ((double)getbps)/1024)); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, + va(M_GetText("Downloading \"%s\""), tempname)); + V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,fileneeded[lastfilenum].totalsize>>10)); + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, + va("%3.1fK/s ", ((double)getbps)/1024)); + } + else + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, + M_GetText("Waiting to download files...")); } } #endif @@ -1453,6 +1474,10 @@ static void SV_SendSaveGame(INT32 node) SV_SendRam(node, buffertosend, length, SF_RAM, 0); save_p = NULL; + + // Remember when we started sending the savegame so we can handle timeouts + sendingsavegame[node] = true; + freezetimeout[node] = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte } #ifdef DUMPCONSISTENCY @@ -1745,7 +1770,7 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) return false; } - if (!server) + if (client) { D_ParseFileneeded(serverlist[i].info.fileneedednum, serverlist[i].info.fileneeded); @@ -1919,7 +1944,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic *oldtic = I_GetTime(); #ifdef CLIENT_LOADINGSCREEN - if (!server && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) + if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) { F_TitleScreenTicker(true); F_TitleScreenDrawer(); @@ -1961,7 +1986,7 @@ static void CL_ConnectToServer(boolean viams) cl_mode = CL_SEARCHING; #ifdef CLIENT_LOADINGSCREEN - lastfilenum = 0; + lastfilenum = -1; #endif #ifdef JOININGAME @@ -2032,7 +2057,7 @@ static void CL_ConnectToServer(boolean viams) pnumnodes++; } } - while (!(cl_mode == CL_CONNECTED && (!server || (server && nodewaited <= pnumnodes)))); + while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes)))); DEBFILE(va("Synchronisation Finished\n")); @@ -2569,6 +2594,14 @@ static void Command_Kick(void) WRITESINT8(p, pn); if (pn == -1 || pn == 0) return; + // Special case if we are trying to kick a player who is downloading the game state: + // trigger a timeout instead of kicking them, because a kick would only + // take effect after they have finished downloading + if (sendingsavegame[playernode[pn]]) + { + Net_ConnectionTimeout(playernode[pn]); + return; + } if (COM_Argc() == 2) { WRITEUINT8(p, KICK_MSG_GO_AWAY); @@ -2781,7 +2814,12 @@ consvar_t cv_blamecfail = {"blamecfail", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL // max file size to send to a player (in kilobytes) static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}}; -consvar_t cv_maxsend = {"maxsend", "1024", CV_SAVE, maxsend_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_maxsend = {"maxsend", "4096", CV_SAVE, maxsend_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_noticedownload = {"noticedownload", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; + +// Speed of file downloading (in packets per tic) +static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {32, "MAX"}, {0, NULL}}; +consvar_t cv_downloadspeed = {"downloadspeed", "16", CV_SAVE, downloadspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static void Got_AddPlayer(UINT8 **p, INT32 playernum); @@ -2804,6 +2842,9 @@ void D_ClientServerInit(void) COM_AddCommand("drop", Command_Drop); COM_AddCommand("droprate", Command_Droprate); #endif +#ifdef _DEBUG + COM_AddCommand("numnodes", Command_Numnodes); +#endif #endif RegisterNetXCmd(XD_KICK, Got_KickCmd); @@ -2839,6 +2880,7 @@ static void ResetNode(INT32 node) supposedtics[node] = gametic; nodewaiting[node] = 0; playerpernode[node] = 0; + sendingsavegame[node] = false; } void SV_ResetServer(void) @@ -3131,7 +3173,7 @@ void CL_RemoveSplitscreenPlayer(void) // is there a game running boolean Playing(void) { - return (server && serverrunning) || (!server && cl_mode == CL_CONNECTED); + return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); } boolean SV_SpawnServer(void) @@ -3394,12 +3436,19 @@ static void HandlePacketFromAwayNode(SINT8 node) } if (cl_mode == CL_WAITJOINRESPONSE) { + // Save the reason so it can be displayed after quitting the netgame + char *reason = strdup(netbuffer->u.serverrefuse.reason); + if (!reason) + I_Error("Out of memory!\n"); + D_QuitNetGame(); CL_Reset(); D_StartTitle(); M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), - netbuffer->u.serverrefuse.reason), NULL, MM_NOTHING); + reason), NULL, MM_NOTHING); + + free(reason); // Will be reset by caller. Signals refusal. cl_mode = CL_ABORTED; @@ -3420,7 +3469,7 @@ static void HandlePacketFromAwayNode(SINT8 node) if (cl_mode != CL_WAITJOINRESPONSE) break; - if (!server) + if (client) { maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); gametype = netbuffer->u.servercfg.gametype; @@ -3548,7 +3597,7 @@ FILESTAMP case PT_CLIENT2MIS: case PT_NODEKEEPALIVE: case PT_NODEKEEPALIVEMIS: - if (!server) + if (client) break; // Ignore tics from those not synched @@ -3581,6 +3630,13 @@ FILESTAMP || netbuffer->packettype == PT_NODEKEEPALIVEMIS) break; + // If a client sends a ticcmd it should mean they are done receiving the savegame + sendingsavegame[node] = false; + + // As long as clients send valid ticcmds, the server can keep running, so reset the timeout + /// \todo Use a separate cvar for that kind of timeout? + freezetimeout[node] = I_GetTime() + connectiontimeout; + // Copy ticcmd G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); @@ -3647,7 +3703,7 @@ FILESTAMP case PT_TEXTCMD2: // splitscreen special netconsole = nodetoplayer2[node]; case PT_TEXTCMD: - if (!server) + if (client) break; if (netconsole < 0 || netconsole >= MAXPLAYERS) @@ -3691,7 +3747,7 @@ FILESTAMP break; case PT_NODETIMEOUT: case PT_CLIENTQUIT: - if (!server) + if (client) break; // nodeingame will be put false in the execution of kick command @@ -3723,7 +3779,7 @@ FILESTAMP // Only accept PT_RESYNCHEND from the server. if (node != servernode) { - CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHEND", node); + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHEND", node); if (server) { @@ -3801,13 +3857,20 @@ FILESTAMP neededtic = realend; } else + { DEBFILE(va("frame not in bound: %u\n", neededtic)); + /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) + I_Error("Received an out of order PT_SERVERTICS packet!\n" + "Got tics %d-%d, needed tic %d\n\n" + "Please report this crash on the Master Board,\n" + "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ + } break; case PT_RESYNCHING: // Only accept PT_RESYNCHING from the server. if (node != servernode) { - CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHING", node); + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHING", node); if (server) { @@ -3827,7 +3890,7 @@ FILESTAMP // Only accept PT_PING from the server. if (node != servernode) { - CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_PING", node); + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); if (server) { @@ -3841,7 +3904,7 @@ FILESTAMP } //Update client ping table from the server. - if (!server) + if (client) { INT32 i; for (i = 0; i < MAXNETNODES; i++) @@ -3854,7 +3917,7 @@ FILESTAMP case PT_SERVERCFG: break; case PT_FILEFRAGMENT: - if (!server) + if (client) Got_Filetxpak(); break; default: @@ -3884,17 +3947,18 @@ FILESTAMP HandleConnect(node); continue; } - if (netbuffer->packettype == PT_SERVERSHUTDOWN && node == servernode - && !server && cl_mode != CL_SEARCHING) + if (node == servernode && client && cl_mode != CL_SEARCHING) { - HandleShutdown(node); - continue; - } - if (netbuffer->packettype == PT_NODETIMEOUT && node == servernode - && !server && cl_mode != CL_SEARCHING) - { - HandleTimeout(node); - continue; + if (netbuffer->packettype == PT_SERVERSHUTDOWN) + { + HandleShutdown(node); + continue; + } + if (netbuffer->packettype == PT_NODETIMEOUT) + { + HandleTimeout(node); + continue; + } } #ifndef NONET @@ -3911,7 +3975,7 @@ FILESTAMP // Packet received from someone already playing if (nodeingame[node]) HandlePacketFromPlayer(node); - // Packet received from someone trying to join + // Packet received from someone not playing else HandlePacketFromAwayNode(node); } @@ -4042,7 +4106,7 @@ static void CL_SendClientCmd(void) if (gamestate == GS_WAITINGPLAYERS) { - // send NODEKEEPALIVE packet + // Send PT_NODEKEEPALIVE packet netbuffer->packettype += 4; packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16); HSendPacket(servernode, false, 0, packetsize); @@ -4052,7 +4116,7 @@ static void CL_SendClientCmd(void) G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); - // send a special packet with 2 cmd for splitscreen + // Send a special packet with 2 cmd for splitscreen if (splitscreen || botingame) { netbuffer->packettype += 2; @@ -4067,23 +4131,23 @@ static void CL_SendClientCmd(void) if (cl_mode == CL_CONNECTED || dedicated) { - // send extra data if needed + // Send extra data if needed if (localtextcmd[0]) { netbuffer->packettype = PT_TEXTCMD; M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1); - // all extra data have been sended - if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // send can fail... + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail... localtextcmd[0] = 0; } - // send extra data if needed for player 2 (splitscreen) + // Send extra data if needed for player 2 (splitscreen) if (localtextcmd2[0]) { netbuffer->packettype = PT_TEXTCMD2; M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1); - // all extra data have been sended - if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // send can fail... + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail... localtextcmd2[0] = 0; } } @@ -4347,7 +4411,7 @@ static inline void PingUpdate(void) //check for ping limit breakage. if (cv_maxping.value) { - for (i = 1; i < MAXNETNODES; i++) + for (i = 1; i < MAXPLAYERS; i++) { if (playeringame[i] && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) { @@ -4361,7 +4425,7 @@ static inline void PingUpdate(void) //in that case, it is probably the server's fault. if (numlaggers < D_NumPlayers() - 1) { - for (i = 1; i < MAXNETNODES; i++) + for (i = 1; i < MAXPLAYERS; i++) { if (playeringame[i] && laggers[i]) { @@ -4376,7 +4440,7 @@ static inline void PingUpdate(void) } //make the ping packet and clear server data for next one - for (i = 0; i < MAXNETNODES; i++) + for (i = 0; i < MAXPLAYERS; i++) { netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount; //server takes a snapshot of the real ping for display. @@ -4386,7 +4450,7 @@ static inline void PingUpdate(void) } //send out our ping packets - for (i = 0; i < MAXNETNODES; i++) + for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) HSendPacket(i, true, 0, sizeof(INT32) * MAXPLAYERS); @@ -4435,7 +4499,7 @@ void NetUpdate(void) } #endif - if (!server) + if (client) maketic = neededtic; Local_Maketic(realtics); // make local tic, and call menu? @@ -4450,7 +4514,7 @@ FILESTAMP MasterClient_Ticker(); // Acking the Master Server - if (!server) + if (client) { if (!resynch_local_inprogress) CL_SendClientCmd(); // Send tic cmd @@ -4500,6 +4564,11 @@ FILESTAMP } } Net_AckTicker(); + // Handle timeouts to prevent definitive freezes from happenning + if (server) + for (i = 1; i < MAXNETNODES; i++) + if (nodeingame[i] && freezetimeout[i] < I_GetTime()) + Net_ConnectionTimeout(i); nowtime /= NEWTICRATERATIO; if (nowtime > resptime) { diff --git a/src/d_clisrv.h b/src/d_clisrv.h index c5c53c585..382329539 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -80,6 +80,9 @@ typedef enum void Command_Drop(void); void Command_Droprate(void); #endif +#ifdef _DEBUG +void Command_Numnodes(void); +#endif #if defined(_MSC_VER) #pragma pack(1) @@ -447,6 +450,7 @@ extern consvar_t cv_playbackspeed; #define KICK_MSG_CUSTOM_BAN 8 extern boolean server; +#define client (!server) extern boolean dedicated; // For dedicated server extern UINT16 software_MAXPACKETLENGTH; extern boolean acceptnewnode; @@ -454,13 +458,14 @@ extern SINT8 servernode; void Command_Ping_f(void); extern tic_t connectiontimeout; +extern tic_t jointimeout; #ifdef NEWPING extern UINT16 pingmeasurecount; extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS]; #endif -extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend; +extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed; // Used in d_net, the only dependence tic_t ExpandTics(INT32 low); diff --git a/src/d_net.c b/src/d_net.c index 6be1dbe5c..fae1ea311 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -42,7 +42,7 @@ // Normally maketic >= gametic > 0 #define FORCECLOSE 0x8000 -tic_t connectiontimeout = (15*TICRATE); +tic_t connectiontimeout = (10*TICRATE); /// \brief network packet doomcom_t *doomcom = NULL; @@ -62,7 +62,7 @@ INT32 net_bandwidth; /// \brief max length per packet INT16 hardware_MAXPACKETLENGTH; -void (*I_NetGet)(void) = NULL; +boolean (*I_NetGet)(void) = NULL; void (*I_NetSend)(void) = NULL; boolean (*I_NetCanSend)(void) = NULL; boolean (*I_NetCanGet)(void) = NULL; @@ -142,7 +142,7 @@ typedef struct UINT8 destinationnode; // The node to send the ack to tic_t senttime; // The time when the ack was sent UINT16 length; // The packet size - UINT16 resentnum; // The number of + UINT16 resentnum; // The number of times the ack has been resent union { SINT8 raw[MAXPACKETLENGTH]; doomdata_t data; @@ -152,11 +152,12 @@ typedef struct typedef enum { - CLOSE = 1, // flag is set when connection is closing + NF_CLOSE = 1, // Flag is set when connection is closing + NF_TIMEOUT = 2, // Flag is set when the node got a timeout } node_flags_t; #ifndef NONET -// table of packet that was not acknowleged can be resend (the sender window) +// Table of packets that were not acknowleged can be resent (the sender window) static ackpak_t ackpak[MAXACKPACKETS]; #endif @@ -274,6 +275,38 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) return false; } +/** Counts how many acks are free + * + * \param urgent True if the type of the packet meant to + * use an ack is lower than PT_CANFAIL + * If for some reason you don't want use it + * for any packet type in particular, + * just set to false + * \return The number of free acks + * + */ +INT32 Net_GetFreeAcks(boolean urgent) +{ + INT32 i, numfreeslot = 0; + INT32 n = 0; // Number of free acks found + + for (i = 0; i < MAXACKPACKETS; i++) + if (!ackpak[i].acknum) + { + // For low priority packets, make sure to let freeslots so urgent packets can be sent + if (!urgent) + { + numfreeslot++; + if (numfreeslot <= URGENTFREESLOTNUM) + continue; + } + + n++; + } + + return n; +} + // Get a ack to send in the queue of this node static UINT8 GetAcktosend(INT32 node) { @@ -298,7 +331,7 @@ static void RemoveAck(INT32 i) DEBFILE(va("Remove ack %d\n",ackpak[i].acknum)); #endif ackpak[i].acknum = 0; - if (nodes[node].flags & CLOSE) + if (nodes[node].flags & NF_CLOSE) Net_CloseConnection(node); } @@ -452,8 +485,13 @@ static void GotAcks(void) } #endif -static inline void Net_ConnectionTimeout(INT32 node) +void Net_ConnectionTimeout(INT32 node) { + // Don't timeout several times + if (nodes[node].flags & NF_TIMEOUT) + return; + nodes[node].flags |= NF_TIMEOUT; + // Send a very special packet to self (hack the reboundstore queue) // Main code will handle it reboundstore[rebound_head].packettype = PT_NODETIMEOUT; @@ -484,7 +522,7 @@ void Net_AckTicker(void) if (ackpak[i].acknum && ackpak[i].senttime + node->timeout < I_GetTime()) #endif { - if (ackpak[i].resentnum > 10 && (node->flags & CLOSE)) + if (ackpak[i].resentnum > 10 && (node->flags & NF_CLOSE)) { DEBFILE(va("ack %d sent 10 times so connection is supposed lost: node %d\n", i, nodei)); @@ -520,7 +558,7 @@ void Net_AckTicker(void) if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime()) Net_SendAcks(i); - if (!(nodes[i].flags & CLOSE) + if (!(nodes[i].flags & NF_CLOSE) && nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime()) { Net_ConnectionTimeout(i); @@ -678,7 +716,7 @@ void Net_CloseConnection(INT32 node) if (!node) return; - nodes[node].flags |= CLOSE; + nodes[node].flags |= NF_CLOSE; // try to Send ack back (two army problem) if (GetAcktosend(node)) @@ -813,18 +851,20 @@ static void DebugPrintpacket(const char *header) case PT_SERVERTICS: { servertics_pak *serverpak = &netbuffer->u.serverpak; - ticcmd_t *cmd = &serverpak->cmds[serverpak->numslots * serverpak->numtics]; - size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)cmd; + UINT8 *cmd = (UINT8 *)(&serverpak->cmds[serverpak->numslots * serverpak->numtics]); + size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - cmd; fprintf(debugfile, " firsttic %u ply %d tics %d ntxtcmd %s\n ", (UINT32)ExpandTics(serverpak->starttic), serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd)); - fprintfstring((char *)cmd, 3); + /// \todo Display more readable information about net commands + fprintfstringnewline((char *)cmd, ntxtcmd); + /*fprintfstring((char *)cmd, 3); if (ntxtcmd > 4) { - fprintf(debugfile, "[%s]", netxcmdnames[*(((UINT8 *)cmd) + 3) - 1]); + fprintf(debugfile, "[%s]", netxcmdnames[*((cmd) + 3) - 1]); fprintfstring(((char *)cmd) + 4, ntxtcmd - 4); } - fprintf(debugfile, "\n"); + fprintf(debugfile, "\n");*/ break; } case PT_CLIENTCMD: @@ -891,7 +931,7 @@ void Command_Drop(void) if (COM_Argc() < 2) { CONS_Printf("drop [quantity]: drop packets\n" - "drop reset: cancel all packet drops"); + "drop reset: cancel all packet drops\n"); return; } @@ -1067,6 +1107,8 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen // boolean HGetPacket(void) { + //boolean nodejustjoined; + // Get a packet from self if (rebound_tail != rebound_head) { @@ -1092,9 +1134,10 @@ boolean HGetPacket(void) while(true) { + //nodejustjoined = I_NetGet(); I_NetGet(); - if (doomcom->remotenode == -1) + if (doomcom->remotenode == -1) // No packet received return false; getbytes += packetheaderlength + doomcom->datalength; // For stat @@ -1110,6 +1153,7 @@ boolean HGetPacket(void) if (netbuffer->checksum != NetbufferChecksum()) { DEBFILE("Bad packet checksum\n"); + //Net_CloseConnection(nodejustjoined ? (doomcom->remotenode | FORCECLOSE) : doomcom->remotenode); Net_CloseConnection(doomcom->remotenode); continue; } @@ -1119,11 +1163,26 @@ boolean HGetPacket(void) DebugPrintpacket("GET"); #endif - // proceed the ack and ackreturn field + /*// If a new node sends an unexpected packet, just ignore it + if (nodejustjoined && server + && !(netbuffer->packettype == PT_ASKINFO + || netbuffer->packettype == PT_SERVERINFO + || netbuffer->packettype == PT_PLAYERINFO + || netbuffer->packettype == PT_REQUESTFILE + || netbuffer->packettype == PT_ASKINFOVIAMS + || netbuffer->packettype == PT_CLIENTJOIN)) + { + DEBFILE(va("New node sent an unexpected %s packet\n", packettypename[netbuffer->packettype])); + //CONS_Alert(CONS_NOTICE, "New node sent an unexpected %s packet\n", packettypename[netbuffer->packettype]); + Net_CloseConnection(doomcom->remotenode | FORCECLOSE); + continue; + }*/ + + // Proceed the ack and ackreturn field if (!Processackpak()) continue; // discarded (duplicated) - // a packet with just ackreturn + // A packet with just ackreturn if (netbuffer->packettype == PT_NOTHING) { GotAcks(); @@ -1136,9 +1195,10 @@ boolean HGetPacket(void) return true; } -static void Internal_Get(void) +static boolean Internal_Get(void) { doomcom->remotenode = -1; + return false; } FUNCNORETURN static ATTRNORETURN void Internal_Send(void) @@ -1223,7 +1283,7 @@ boolean D_CheckNetGame(void) if (netgame) ret = true; - if (!server && netgame) + if (client && netgame) netgame = false; server = true; // WTF? server always true??? // no! The deault mode is server. Client is set elsewhere diff --git a/src/d_net.h b/src/d_net.h index 190e07a60..84814ce39 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -39,6 +39,7 @@ extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if a extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game +INT32 Net_GetFreeAcks(boolean urgent); void Net_AckTicker(void); // If reliable return true if packet sent, 0 else @@ -53,6 +54,7 @@ boolean D_CheckNetGame(void); void D_CloseConnection(void); void Net_UnAcknowledgePacket(INT32 node); void Net_CloseConnection(INT32 node); +void Net_ConnectionTimeout(INT32 node); void Net_AbortPacketType(UINT8 packettype); void Net_SendAcks(INT32 node); void Net_WaitAllAckReceived(UINT32 timeout); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c54c96f75..55a3b30f9 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -82,6 +82,7 @@ static void AutoBalance_OnChange(void); static void TeamScramble_OnChange(void); static void NetTimeout_OnChange(void); +static void JoinTimeout_OnChange(void); static void Ringslinger_OnChange(void); static void Gravity_OnChange(void); @@ -340,7 +341,9 @@ consvar_t cv_killingdead = {"killingdead", "Off", CV_NETVAR, CV_OnOff, NULL, 0, consvar_t cv_netstat = {"netstat", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // show bandwidth statistics static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; -consvar_t cv_nettimeout = {"nettimeout", "525", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_nettimeout = {"nettimeout", "350", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; +consvar_t cv_jointimeout = {"jointimeout", "350", CV_CALL|CV_SAVE, jointimeout_cons_t, JoinTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL}; #ifdef NEWPING consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; #endif @@ -546,9 +549,12 @@ void D_RegisterServerCommands(void) // d_clisrv CV_RegisterVar(&cv_maxplayers); CV_RegisterVar(&cv_maxsend); + CV_RegisterVar(&cv_noticedownload); + CV_RegisterVar(&cv_downloadspeed); COM_AddCommand("ping", Command_Ping_f); CV_RegisterVar(&cv_nettimeout); + CV_RegisterVar(&cv_jointimeout); CV_RegisterVar(&cv_skipmapcheck); CV_RegisterVar(&cv_sleep); @@ -1005,7 +1011,7 @@ UINT8 CanChangeSkin(INT32 playernum) return true; // Force skin in effect. - if (!server && (cv_forceskin.value != -1) && !(adminplayer == playernum && serverplayer == -1)) + if (client && (cv_forceskin.value != -1) && !(adminplayer == playernum && serverplayer == -1)) return false; // Can change skin in intermission and whatnot. @@ -1616,7 +1622,7 @@ static void Command_Map_f(void) return; } - if (!server && !(adminplayer == consoleplayer)) + if (client && !(adminplayer == consoleplayer)) { CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); return; @@ -1943,7 +1949,7 @@ static void Got_Suicide(UINT8 **cp, INT32 playernum) // You can't suicide someone else. Nice try, there. if (suicideplayer != playernum || (!G_PlatformGametype())) { - CONS_Alert(CONS_WARNING, M_GetText("Illegal suicide command recieved from %s\n"), player_names[playernum]); + CONS_Alert(CONS_WARNING, M_GetText("Illegal suicide command received from %s\n"), player_names[playernum]); if (server) { XBOXSTATIC UINT8 buf[2]; @@ -2658,7 +2664,7 @@ static void Command_Changepassword_f(void) // If we have no MD5 support then completely disable XD_LOGIN responses for security. CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n"); #else - if (!server) // cannot change remotely + if (client) // cannot change remotely { CONS_Printf(M_GetText("Only the server can use this.\n")); return; @@ -2717,7 +2723,7 @@ static void Got_Login(UINT8 **cp, INT32 playernum) READMEM(*cp, sentmd5, 16); - if (!server) + if (client) return; // Do the final pass to compare with the sent md5 @@ -2739,7 +2745,7 @@ static void Command_Verify_f(void) char *temp; INT32 playernum; - if (!server) + if (client) { CONS_Printf(M_GetText("Only the server can use this.\n")); return; @@ -2823,7 +2829,7 @@ static void Command_MotD_f(void) return; } - if ((netgame || multiplayer) && !server) + if ((netgame || multiplayer) && client) SendNetXCmd(XD_SETMOTD, mymotd, sizeof(motd)); else { @@ -3080,7 +3086,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) READMEM(*cp, md5sum, 16); // Only the server processes this message. - if (!server) + if (client) return; // Disallow non-printing characters and semicolons. @@ -3347,6 +3353,11 @@ static void NetTimeout_OnChange(void) connectiontimeout = (tic_t)cv_nettimeout.value; } +static void JoinTimeout_OnChange(void) +{ + jointimeout = (tic_t)cv_jointimeout.value; +} + UINT32 timelimitintics = 0; /** Deals with a timelimit change by printing the change to the console. diff --git a/src/d_netfil.c b/src/d_netfil.c index 4e3d70fa9..bf4e59878 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -97,7 +97,7 @@ char downloaddir[256] = "DOWNLOAD"; #ifdef CLIENT_LOADINGSCREEN // for cl loading screen -INT32 lastfilenum = 0; +INT32 lastfilenum = -1; #endif /** Fills a serverinfo packet with information about wad files loaded. @@ -487,6 +487,9 @@ static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid) INT32 i; char wadfilename[MAX_WADPATH]; + if (cv_noticedownload.value) + CONS_Printf("Sending file \"%s\" to node %d\n", filename, node); + // Find the last file in the list and set a pointer to its "next" field q = &transfer[node].txlist; while (*q) @@ -609,6 +612,8 @@ static void SV_EndFileSend(INT32 node) switch (p->ram) { case SF_FILE: // It's a file, close it and free its filename + if (cv_noticedownload.value) + CONS_Printf("Ending file transfer for node %d\n", node); if (transfer[node].currentfile) fclose(transfer[node].currentfile); free(p->id.filename); @@ -636,6 +641,9 @@ static void SV_EndFileSend(INT32 node) /** Handles file transmission * + * \todo Use an acknowledging method more adapted to file transmission + * The current download speed suffers from lack of ack packets, + * especially when the one downloading has high latency * */ void SV_FileSendTicker(void) @@ -644,17 +652,38 @@ void SV_FileSendTicker(void) filetx_pak *p; size_t size; filetx_t *f; - INT32 packetsent = PACKETPERTIC, ram, i; + INT32 packetsent, ram, i, j; + INT32 maxpacketsent; - if (!filestosend) + if (!filestosend) // No file to send return; - if (!packetsent) - packetsent++; + + if (cv_downloadspeed.value) // New (and experimental) behavior + { + packetsent = cv_downloadspeed.value; + // Don't send more packets than we have free acks +#ifndef NONET + maxpacketsent = Net_GetFreeAcks(false) - 5; // Let 5 extra acks just in case +#else + maxpacketsent = 1; +#endif + if (packetsent > maxpacketsent && maxpacketsent > 0) // Send at least one packet + packetsent = maxpacketsent; + } + else // Old behavior + { + packetsent = PACKETPERTIC; + if (!packetsent) + packetsent = 1; + } + + netbuffer->packettype = PT_FILEFRAGMENT; + // (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth) while (packetsent-- && filestosend != 0) { - for (i = currentnode, ram = 0; ram < MAXNETNODES; - i = (i+1) % MAXNETNODES, ram++) + for (i = currentnode, j = 0; j < MAXNETNODES; + i = (i+1) % MAXNETNODES, j++) { if (transfer[i].txlist) goto found; @@ -713,7 +742,6 @@ void SV_FileSendTicker(void) p->position |= LONG(0x80000000); p->fileid = f->fileid; p->size = SHORT((UINT16)size); - netbuffer->packettype = PT_FILEFRAGMENT; // Send the packet if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND @@ -735,27 +763,40 @@ void SV_FileSendTicker(void) void Got_Filetxpak(void) { INT32 filenum = netbuffer->u.filetxpak.fileid; + fileneeded_t *file = &fileneeded[filenum]; + char *filename = file->filename; static INT32 filetime = 0; + if (!(strcmp(filename, "srb2.srb") + && strcmp(filename, "srb2.wad") + && strcmp(filename, "zones.dta") + && strcmp(filename, "player.dta") + && strcmp(filename, "rings.dta") + && strcmp(filename, "patch.dta") + && strcmp(filename, "music.dta") + )) + I_Error("Tried to download \"%s\"", filename); + if (filenum >= fileneedednum) { DEBFILE(va("fileframent not needed %d>%d\n", filenum, fileneedednum)); + //I_Error("Received an unneeded file fragment (file id received: %d, file id needed: %d)\n", filenum, fileneedednum); return; } - if (fileneeded[filenum].status == FS_REQUESTED) + if (file->status == FS_REQUESTED) { - if (fileneeded[filenum].file) + if (file->file) I_Error("Got_Filetxpak: already open file\n"); - fileneeded[filenum].file = fopen(fileneeded[filenum].filename, "wb"); - if (!fileneeded[filenum].file) - I_Error("Can't create file %s: %s", fileneeded[filenum].filename, strerror(errno)); - CONS_Printf("\r%s...\n",fileneeded[filenum].filename); - fileneeded[filenum].currentsize = 0; - fileneeded[filenum].status = FS_DOWNLOADING; + file->file = fopen(filename, "wb"); + if (!file->file) + I_Error("Can't create file %s: %s", filename, strerror(errno)); + CONS_Printf("\r%s...\n",filename); + file->currentsize = 0; + file->status = FS_DOWNLOADING; } - if (fileneeded[filenum].status == FS_DOWNLOADING) + if (file->status == FS_DOWNLOADING) { UINT32 pos = LONG(netbuffer->u.filetxpak.position); UINT16 size = SHORT(netbuffer->u.filetxpak.size); @@ -764,27 +805,47 @@ void Got_Filetxpak(void) if (pos & 0x80000000) { pos &= ~0x80000000; - fileneeded[filenum].totalsize = pos + size; + file->totalsize = pos + size; } // We can receive packet in the wrong order, anyway all os support gaped file - fseek(fileneeded[filenum].file, pos, SEEK_SET); - if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].file) != 1) - I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].file))); - fileneeded[filenum].currentsize += size; + fseek(file->file, pos, SEEK_SET); + if (fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1) + I_Error("Can't write to %s: %s\n",filename, strerror(ferror(file->file))); + file->currentsize += size; // Finished? - if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize) + if (file->currentsize == file->totalsize) { - fclose(fileneeded[filenum].file); - fileneeded[filenum].file = NULL; - fileneeded[filenum].status = FS_FOUND; + fclose(file->file); + file->file = NULL; + file->status = FS_FOUND; CONS_Printf(M_GetText("Downloading %s...(done)\n"), - fileneeded[filenum].filename); + filename); } } else - I_Error("Received a file not requested\n"); - + { + const char *s; + switch(file->status) + { + case FS_NOTFOUND: + s = "FS_NOTFOUND"; + break; + case FS_FOUND: + s = "FS_FOUND"; + break; + case FS_OPEN: + s = "FS_OPEN"; + break; + case FS_MD5SUMBAD: + s = "FS_MD5SUMBAD"; + break; + default: + s = "unknown"; + break; + } + I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s); + } // Send ack back quickly if (++filetime == 3) { @@ -797,12 +858,23 @@ void Got_Filetxpak(void) #endif } -/** Cancels all file requests for a node +/** \brief Checks if a node is downloading a file * - * \param node The destination - * \sa SV_EndFileSend + * \param node The node to check for + * \return True if the node is downloading a file * */ +boolean SV_SendingFile(INT32 node) +{ + return transfer[node].txlist != NULL; +} + +/** Cancels all file requests for a node + * + * \param node The destination + * \sa SV_EndFileSend + * + */ void SV_AbortSendFiles(INT32 node) { while (transfer[node].txlist) diff --git a/src/d_netfil.h b/src/d_netfil.h index e82b57d67..c9085a5b0 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -65,6 +65,7 @@ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, void SV_FileSendTicker(void); void Got_Filetxpak(void); +boolean SV_SendingFile(INT32 node); boolean CL_CheckDownloadable(void); boolean CL_SendRequestFile(void); diff --git a/src/dehacked.c b/src/dehacked.c index 56a211be8..c71c55ac1 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1961,6 +1961,7 @@ static actionpointer_t actionpointers[] = {{A_FlickyCheck}, "A_FLICKYCHECK"}, {{A_FlickyHeightCheck}, "A_FLICKYHEIGHTCHECK"}, {{A_FlickyFlutter}, "A_FLICKYFLUTTER"}, + {{A_FlameParticle}, "A_FLAMEPARTICLE"}, {{NULL}, "NONE"}, @@ -4398,6 +4399,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_GOOP1", "S_GOOP2", "S_GOOP3", + "S_GOOPTRAIL", // Boss 3 "S_EGGMOBILE3_STND", @@ -4974,7 +4976,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Starpost "S_STARPOST_IDLE", "S_STARPOST_FLASH", + "S_STARPOST_STARTSPIN", "S_STARPOST_SPIN", + "S_STARPOST_ENDSPIN", // Big floating mine "S_BIGMINE1", @@ -5156,21 +5160,15 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DEMONFIRE6", "S_GFZFLOWERA", - "S_GFZFLOWERA2", - - "S_GFZFLOWERB1", - "S_GFZFLOWERB2", - - "S_GFZFLOWERC1", + "S_GFZFLOWERB", + "S_GFZFLOWERC", "S_BERRYBUSH", "S_BUSH", // THZ Plant - "S_THZPLANT1", - "S_THZPLANT2", - "S_THZPLANT3", - "S_THZPLANT4", + "S_THZFLOWERA", + "S_THZFLOWERB", // THZ Alarm "S_ALARM1", @@ -5215,6 +5213,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FLAME2", "S_FLAME3", "S_FLAME4", + "S_FLAME5", + "S_FLAME6", + "S_FLAMEPARTICLE", + + "S_FLAMEREST", // Eggman Statue "S_EGGSTATUE1", @@ -5276,36 +5279,13 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Spinning flame jets "S_FJSPINAXISA1", // Counter-clockwise "S_FJSPINAXISA2", - "S_FJSPINAXISA3", - "S_FJSPINAXISA4", - "S_FJSPINAXISA5", - "S_FJSPINAXISA6", - "S_FJSPINAXISA7", - "S_FJSPINAXISA8", - "S_FJSPINAXISA9", - "S_FJSPINHELPERA1", - "S_FJSPINHELPERA2", - "S_FJSPINHELPERA3", "S_FJSPINAXISB1", // Clockwise "S_FJSPINAXISB2", - "S_FJSPINAXISB3", - "S_FJSPINAXISB4", - "S_FJSPINAXISB5", - "S_FJSPINAXISB6", - "S_FJSPINAXISB7", - "S_FJSPINAXISB8", - "S_FJSPINAXISB9", - "S_FJSPINHELPERB1", - "S_FJSPINHELPERB2", - "S_FJSPINHELPERB3", // Blade's flame "S_FLAMEJETFLAMEB1", "S_FLAMEJETFLAMEB2", "S_FLAMEJETFLAMEB3", - "S_FLAMEJETFLAMEB4", - "S_FLAMEJETFLAMEB5", - "S_FLAMEJETFLAMEB6", // Trapgoyles "S_TRAPGOYLE", @@ -5400,8 +5380,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BSZVINE_ORANGE", "S_BSZSHRUB", "S_BSZCLOVER", - "S_BSZFISH", - "S_BSZSUNFLOWER", + "S_BIG_PALMTREE_TRUNK", + "S_BIG_PALMTREE_TOP", + "S_PALMTREE_TRUNK", + "S_PALMTREE_TOP", "S_DBALL1", "S_DBALL2", @@ -6120,20 +6102,19 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FIREBALLEXP2", "S_FIREBALLEXP3", "S_SHELL", - "S_SHELL1", - "S_SHELL2", - "S_SHELL3", - "S_SHELL4", - "S_PUMA1", - "S_PUMA2", - "S_PUMA3", - "S_PUMA4", - "S_PUMA5", - "S_PUMA6", - "S_HAMMER1", - "S_HAMMER2", - "S_HAMMER3", - "S_HAMMER4", + "S_PUMA_START1", + "S_PUMA_START2", + "S_PUMA_UP1", + "S_PUMA_UP2", + "S_PUMA_UP3", + "S_PUMA_DOWN1", + "S_PUMA_DOWN2", + "S_PUMA_DOWN3", + "S_PUMATRAIL1", + "S_PUMATRAIL2", + "S_PUMATRAIL3", + "S_PUMATRAIL4", + "S_HAMMER", "S_KOOPA1", "S_KOOPA2", "S_KOOPAFLAME1", @@ -6386,6 +6367,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BOSSTANK2", "MT_BOSSSPIGOT", "MT_GOOP", + "MT_GOOPTRAIL", // Boss 3 "MT_EGGMOBILE3", @@ -6555,7 +6537,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BUSH", // Techno Hill Scenery - "MT_THZPLANT", // THZ Plant + "MT_THZFLOWER1", + "MT_THZFLOWER2", "MT_ALARM", // Deep Sea Scenery @@ -6571,6 +6554,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s // Castle Eggman Scenery "MT_CHAIN", // CEZ Chain "MT_FLAME", // Flame (has corona) + "MT_FLAMEPARTICLE", "MT_EGGSTATUE", // Eggman Statue "MT_MACEPOINT", // Mace rotation point "MT_SWINGMACEPOINT", // Mace swinging point @@ -6597,9 +6581,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_FLAMEJETFLAME", "MT_FJSPINAXISA", // Counter-clockwise - "MT_FJSPINHELPERA", "MT_FJSPINAXISB", // Clockwise - "MT_FJSPINHELPERB", "MT_FLAMEJETFLAMEB", // Blade's flame @@ -6676,8 +6658,10 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BSZVINE_ORANGE", "MT_BSZSHRUB", "MT_BSZCLOVER", - "MT_BSZFISH", - "MT_BSZSUNFLOWER", + "MT_BIG_PALMTREE_TRUNK", + "MT_BIG_PALMTREE_TOP", + "MT_PALMTREE_TRUNK", + "MT_PALMTREE_TOP", // Misc scenery "MT_DBALL", @@ -6783,6 +6767,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_FIREBALL", "MT_SHELL", "MT_PUMA", + "MT_PUMATRAIL", "MT_HAMMER", "MT_KOOPA", "MT_KOOPAFLAME", diff --git a/src/doomdef.h b/src/doomdef.h index 263219a9d..887b39b00 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -214,7 +214,7 @@ extern FILE *logstream; // it's only for detection of the version the player is using so the MS can alert them of an update. // Only set it higher, not lower, obviously. // Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1". -#define MODVERSION 21 +#define MODVERSION 22 // ========================================================================= diff --git a/src/f_finale.c b/src/f_finale.c index 78e9ada17..167fdd880 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -974,7 +974,7 @@ static const char *credits[] = { "Scott \"Graue\" Feeney", "Nathan \"Jazz\" Giroux", "Thomas \"Shadow Hog\" Igoe", - "\"Monster\" Iestyn Jealous", + "Iestyn \"Monster Iestyn\" Jealous", "Ronald \"Furyhunter\" Kinard", // The SDL2 port "John \"JTE\" Muniz", "Ehab \"Wolfy\" Saeed", @@ -986,6 +986,7 @@ static const char *credits[] = { "\"chi.miru\"", // Red's secret weapon, the REAL reason slopes exist (also helped port drawing code from ZDoom) "Andrew \"orospakr\" Clunis", "Gregor \"Oogaland\" Dick", + "Louis-Antoine \"LJSonic\" de Moulins", // for fixing 2.1's netcode (de Rochefort doesn't quite fit on the screen sorry lol) "Vivian \"toaster\" Grannell", "Julio \"Chaos Zero 64\" Guir", "\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog @@ -1021,7 +1022,7 @@ static const char *credits[] = { "Paul \"Boinciel\" Clempson", "Cyan Helkaraxe", "Kepa \"Nev3r\" Iceta", - "\"Monster\" Iestyn Jealous", + "Iestyn \"Monster Iestyn\" Jealous", "Jarel \"Arrow\" Jones", "Stefan \"Stuf\" Rimalia", "Shane Mychal Sexton", diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 35a07c527..cdd778caa 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -296,6 +296,7 @@ light_t *t_lspr[NUMSPRITES] = // Techno Hill Scenery &lspr[NOLIGHT], // SPR_THZP + &lspr[NOLIGHT], // SPR_FWR5 &lspr[REDBALL_L], // SPR_ALRM // Deep Sea Scenery diff --git a/src/hu_stuff.c b/src/hu_stuff.c index c8cd8df0d..01d4ed524 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -470,7 +470,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) boolean action = false; char *ptr; - CONS_Debug(DBG_NETPLAY,"Recieved SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]); + CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]); target = READSINT8(*p); flags = READUINT8(*p); @@ -1102,7 +1102,19 @@ void HU_Drawer(void) // draw desynch text if (hu_resynching) - V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_YELLOWMAP, "Resynching..."); + { + static UINT32 resynch_ticker = 0; + char resynch_text[14]; + UINT32 i; + + // Animate the dots + resynch_ticker++; + strcpy(resynch_text, "Resynching"); + for (i = 0; i < (resynch_ticker / 16) % 4; i++) + strcat(resynch_text, "."); + + V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_YELLOWMAP | V_ALLOWLOWERCASE, resynch_text); + } } //====================================================================== diff --git a/src/i_net.h b/src/i_net.h index e378f5723..2bfa5eac7 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -85,7 +85,7 @@ extern doomcom_t *doomcom; /** \brief return packet in doomcom struct */ -extern void (*I_NetGet)(void); +extern boolean (*I_NetGet)(void); /** \brief ask to driver if there is data waiting */ diff --git a/src/i_tcp.c b/src/i_tcp.c index f6212458b..c65a536a8 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -179,6 +179,7 @@ static UINT8 UPNP_support = TRUE; #include "i_system.h" #include "i_net.h" #include "d_net.h" +#include "d_netfil.h" #include "i_tcp.h" #include "m_argv.h" @@ -482,21 +483,12 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) return false; } -static SINT8 getfreenode(void) -{ - SINT8 j; - - for (j = 0; j < MAXNETNODES; j++) - if (!nodeconnected[j]) - { - nodeconnected[j] = true; - return j; - } - return -1; -} - // This is a hack. For some reason, nodes aren't being freed properly. // This goes through and cleans up what nodes were supposed to be freed. +/** \warning This function causes the file downloading to stop if someone joins. + * How? Because it removes nodes that are connected but not in game, + * which is exactly what clients downloading a file are. + */ static void cleanupnodes(void) { SINT8 j; @@ -506,13 +498,81 @@ static void cleanupnodes(void) // Why can't I start at zero? for (j = 1; j < MAXNETNODES; j++) + //if (!(nodeingame[j] || SV_SendingFile(j))) if (!nodeingame[j]) nodeconnected[j] = false; } + +static SINT8 getfreenode(void) +{ + SINT8 j; + + cleanupnodes(); + + for (j = 0; j < MAXNETNODES; j++) + if (!nodeconnected[j]) + { + nodeconnected[j] = true; + return j; + } + + /** \warning No free node? Just in case a node might not have been freed properly, + * look if there are connected nodes that aren't in game, and forget them. + * It's dirty, and might result in a poor guy having to restart + * downloading a needed wad, but it's better than not letting anyone join... + */ + /*I_Error("No more free nodes!!1!11!11!!1111\n"); + for (j = 1; j < MAXNETNODES; j++) + if (!nodeingame[j]) + return j;*/ + + return -1; +} + +#ifdef _DEBUG +void Command_Numnodes(void) +{ + INT32 connected = 0; + INT32 ingame = 0; + INT32 i; + + for (i = 1; i < MAXNETNODES; i++) + { + if (!(nodeconnected[i] || nodeingame[i])) + continue; + + if (nodeconnected[i]) + connected++; + if (nodeingame[i]) + ingame++; + + CONS_Printf("%2d - ", i); + if (nodetoplayer[i] != -1) + CONS_Printf("player %.2d", nodetoplayer[i]); + else + CONS_Printf(" "); + if (nodeconnected[i]) + CONS_Printf(" - connected"); + else + CONS_Printf(" - "); + if (nodeingame[i]) + CONS_Printf(" - ingame"); + else + CONS_Printf(" - "); + CONS_Printf(" - %s\n", I_GetNodeAddress(i)); + } + + CONS_Printf("\n" + "Connected: %d\n" + "Ingame: %d\n", + connected, ingame); +} +#endif #endif #ifndef NONET -static void SOCK_Get(void) +// Returns true if a packet was received from a new node, false in all other cases +static boolean SOCK_Get(void) { size_t i, n; int j; @@ -535,13 +595,12 @@ static void SOCK_Get(void) doomcom->remotenode = (INT16)j; // good packet from a game player doomcom->datalength = (INT16)c; nodesocket[j] = mysockets[n]; - return; + return false; } } // not found // find a free slot - cleanupnodes(); j = getfreenode(); if (j > 0) { @@ -564,14 +623,15 @@ static void SOCK_Get(void) } if (i == numbans) SOCK_bannednode[j] = false; - return; + return true; } else DEBFILE("New node detected: No more free slots\n"); - } } + doomcom->remotenode = -1; // no packet + return false; } #endif @@ -1256,7 +1316,6 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) gaie = I_getaddrinfo(address, port, &hints, &ai); if (gaie == 0) { - cleanupnodes(); newnode = getfreenode(); } if (newnode == -1) diff --git a/src/info.c b/src/info.c index 613320503..fdea617c1 100644 --- a/src/info.c +++ b/src/info.c @@ -184,6 +184,7 @@ char sprnames[NUMSPRITES + 1][5] = // Techno Hill Scenery "THZP", // Techno Hill Zone Plant + "FWR5", // Another one "ALRM", // THZ2 Alarm // Deep Sea Scenery @@ -990,9 +991,10 @@ state_t states[NUMSTATES] = {SPR_SPNK, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSPIGOT // Boss 2 Goop - {SPR_GOOP, 0, 2, {NULL}, 0, 0, S_GOOP2}, // S_GOOP1 - {SPR_GOOP, 1, 2, {NULL}, 0, 0, S_GOOP1}, // S_GOOP2 - {SPR_GOOP, 2,-1, {NULL}, 0, 0, S_NULL}, // S_GOOP3 + {SPR_GOOP, 0, 2, {A_SpawnObjectRelative}, 0, MT_GOOPTRAIL, S_GOOP2}, // S_GOOP1 + {SPR_GOOP, 1, 2, {A_SpawnObjectRelative}, 0, MT_GOOPTRAIL, S_GOOP1}, // S_GOOP2 + {SPR_GOOP, 2, -1, {NULL}, 0, 0, S_NULL}, // S_GOOP3 + {SPR_GOOP, FF_ANIMATE|3, 11, {NULL}, 2, 6, S_NULL}, // S_GOOPTRAIL // Boss 3 {SPR_EGGO, 0, 1, {NULL}, 0, 0, S_EGGMOBILE3_STND}, // S_EGGMOBILE3_STND @@ -1244,12 +1246,12 @@ state_t states[NUMSTATES] = {SPR_RCKT, 2 + FF_FULLBRIGHT, 6, {A_NapalmScatter}, MT_CYBRAKDEMON_NAPALM_FLAMES + (6<<16), 32 + (16<<16), S_CYBRAKDEMONMISSILE_EXPLODE3}, // S_CYBRAKDEMONMISSILE_EXPLODE2 {SPR_RCKT, 3 + FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_NULL}, // S_CYBRAKDEMONMISSILE_EXPLODE3 - {SPR_FLME, FF_TRANS50|FF_FULLBRIGHT , 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY2}, // S_CYBRAKDEMONFLAMESHOT_FLY1 - {SPR_FLME, FF_TRANS50|FF_FULLBRIGHT|1, 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY2 - {SPR_FLME, FF_TRANS50|FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY3 - {SPR_FLME, FF_TRANS50|FF_FULLBRIGHT|2, 0, {A_SpawnObjectRelative}, 0, MT_CYBRAKDEMON_FLAMEREST, S_NULL}, // S_CYBRAKDEMONFLAMESHOT_DIE + {SPR_FLME, FF_TRANS20|FF_FULLBRIGHT , 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY2}, // S_CYBRAKDEMONFLAMESHOT_FLY1 + {SPR_FLME, FF_TRANS20|FF_FULLBRIGHT|1, 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY2 + {SPR_FLME, FF_TRANS20|FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY3 + {SPR_FLME, FF_TRANS20|FF_FULLBRIGHT|2, 0, {A_SpawnObjectRelative}, 0, MT_CYBRAKDEMON_FLAMEREST, S_NULL}, // S_CYBRAKDEMONFLAMESHOT_DIE - {SPR_FLAM, FF_TRANS50|FF_FULLBRIGHT|3, 3, {A_SetFuse}, 10*TICRATE, 0, S_FLAME1}, // S_CYBRAKDEMONFLAMEREST + {SPR_FLAM, FF_TRANS20|FF_FULLBRIGHT|5, 3, {A_SetFuse}, 10*TICRATE, 0, S_FLAMEREST}, // S_CYBRAKDEMONFLAMEREST {SPR_ELEC, 0 + FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_CYBRAKDEMONELECTRICBARRIER_INIT2}, // S_CYBRAKDEMONELECTRICBARRIER_INIT1 {SPR_ELEC, 0 + FF_FULLBRIGHT, 0, {A_RemoteAction}, -1, S_CYBRAKDEMON_INVINCIBLERIZE, S_CYBRAKDEMONELECTRICBARRIER_PLAYSOUND}, // S_CYBRAKDEMONELECTRICBARRIER_INIT2 @@ -1570,9 +1572,11 @@ state_t states[NUMSTATES] = {SPR_USPK, 2,-1, {NULL}, 0, 0, S_NULL}, // S_SPIKED2 // Starpost - {SPR_STPT, 0 , -1, {NULL}, 0, 0, S_NULL}, // S_STARPOST_IDLE - {SPR_STPT, FF_ANIMATE , -1, {NULL}, 1, 2, S_NULL}, // S_STARPOST_FLASH - {SPR_STPT, FF_ANIMATE|2, 31, {NULL}, 15, 1, S_STARPOST_FLASH}, // S_STARPOST_SPIN + {SPR_STPT, 0 , -1, {NULL}, 0, 0, S_NULL}, // S_STARPOST_IDLE + {SPR_STPT, FF_ANIMATE|17, -1, {NULL}, 5, 1, S_NULL}, // S_STARPOST_FLASH + {SPR_STPT, FF_ANIMATE|13, 2, {NULL}, 1, 1, S_STARPOST_SPIN}, // S_STARPOST_STARTSPIN + {SPR_STPT, FF_ANIMATE|1 , 23, {NULL}, 11, 1, S_STARPOST_ENDSPIN}, // S_STARPOST_SPIN + {SPR_STPT, FF_ANIMATE|15, 2, {NULL}, 1, 1, S_STARPOST_FLASH}, // S_STARPOST_ENDSPIN // Big floating mine {SPR_BMNE, 0, 5, {NULL}, 0, 0, S_BIGMINE2}, // S_BIGMINE1 @@ -1753,21 +1757,15 @@ state_t states[NUMSTATES] = {SPR_CFIR, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_DEMONFIRE1}, // S_DEMONFIRE6 // GFZ Flower - {SPR_FWR1, 0, 14, {NULL}, 0, 0, S_GFZFLOWERA2}, // S_GFZFLOWERA - {SPR_FWR1, 1, 14, {NULL}, 0, 0, S_GFZFLOWERA}, // S_GFZFLOWERA2 - - {SPR_FWR2, 0, 7, {NULL}, 0, 0, S_GFZFLOWERB2}, // S_GFZFLOWERB1 - {SPR_FWR2, 1, 7, {NULL}, 0, 0, S_GFZFLOWERB1}, // S_GFZFLOWERB1 - - {SPR_FWR3, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GFZFLOWERC1 + {SPR_FWR1, FF_ANIMATE, -1, {NULL}, 7, 3, S_NULL}, // S_GFZFLOWERA + {SPR_FWR2, FF_ANIMATE, -1, {NULL}, 19, 3, S_NULL}, // S_GFZFLOWERB + {SPR_FWR3, FF_ANIMATE, -1, {NULL}, 11, 4, S_NULL}, // S_GFZFLOWERC {SPR_BUS1, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BERRYBUSH {SPR_BUS2, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BUSH - {SPR_THZP, 0, 4, {NULL}, 0, 0, S_THZPLANT2}, // S_THZPLANT1 - {SPR_THZP, 1, 4, {NULL}, 0, 0, S_THZPLANT3}, // S_THZPLANT1 - {SPR_THZP, 2, 4, {NULL}, 0, 0, S_THZPLANT4}, // S_THZPLANT1 - {SPR_THZP, 3, 4, {NULL}, 0, 0, S_THZPLANT1}, // S_THZPLANT1 + {SPR_THZP, FF_ANIMATE, -1, {NULL}, 7, 4, S_NULL}, // S_THZFLOWERA + {SPR_FWR5, FF_ANIMATE, -1, {NULL}, 19, 2, S_NULL}, // S_THZFLOWERB // THZ Alarm {SPR_ALRM, FF_FULLBRIGHT, 35, {A_Scream}, 0, 0, S_ALARM1}, // S_ALARM1 @@ -1808,10 +1806,15 @@ state_t states[NUMSTATES] = {SPR_CHAN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZCHAIN // Flame - {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_FLAME2}, // S_FLAME1 - {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS50|1, 3, {NULL}, 0, 0, S_FLAME3}, // S_FLAME2 - {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS50|2, 3, {NULL}, 0, 0, S_FLAME4}, // S_FLAME3 - {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS50|3, 3, {NULL}, 0, 0, S_FLAME1}, // S_FLAME4 + {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20, 3, {A_FlameParticle}, 3, 0, S_FLAME2}, // S_FLAME1 + {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|1, 3, {NULL}, 0, 0, S_FLAME3}, // S_FLAME2 + {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|2, 3, {A_FlameParticle}, 3, 0, S_FLAME4}, // S_FLAME3 + {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|3, 3, {NULL}, 0, 0, S_FLAME5}, // S_FLAME4 + {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|4, 3, {A_FlameParticle}, 3, 0, S_FLAME6}, // S_FLAME5 + {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|5, 3, {NULL}, 0, 0, S_FLAME1}, // S_FLAME6 + {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS10|6, 24, {NULL}, 0, 0, S_NULL}, // S_FLAMEPARTICLE + + {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|FF_ANIMATE, -1, {NULL}, 5, 3, S_FLAME2}, // S_FLAMEREST // Eggman statue {SPR_ESTA, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EGGSTATUE1 @@ -1867,48 +1870,23 @@ state_t states[NUMSTATES] = {SPR_NULL, 0, 2*TICRATE, {NULL}, 0, 0, S_FLAMEJETSTART}, // S_FLAMEJETSTND {SPR_NULL, 0, 3*TICRATE, {A_ToggleFlameJet}, 0, 0, S_FLAMEJETSTOP}, // S_FLAMEJETSTART {SPR_NULL, 0, 1, {A_ToggleFlameJet}, 0, 0, S_FLAMEJETSTND}, // S_FLAMEJETSTOP - {SPR_FLME, FF_TRANS50 , 4, {NULL}, 0, 0, S_FLAMEJETFLAME2}, // S_FLAMEJETFLAME1 - {SPR_FLME, FF_TRANS60|1, 5, {NULL}, 0, 0, S_FLAMEJETFLAME3}, // S_FLAMEJETFLAME2 - {SPR_FLME, FF_TRANS70|2, 11, {NULL}, 0, 0, S_NULL}, // S_FLAMEJETFLAME3 + {SPR_FLME, FF_FULLBRIGHT|FF_TRANS50 , 4, {NULL}, 0, 0, S_FLAMEJETFLAME2}, // S_FLAMEJETFLAME1 + {SPR_FLME, FF_FULLBRIGHT|FF_TRANS60|1, 5, {NULL}, 0, 0, S_FLAMEJETFLAME3}, // S_FLAMEJETFLAME2 + {SPR_FLME, FF_FULLBRIGHT|FF_TRANS70|2, 11, {NULL}, 0, 0, S_NULL}, // S_FLAMEJETFLAME3 // Spinning flame jets // A: Counter-clockwise - {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FJSPINAXISA2}, // S_FJSPINAXISA1 - {SPR_NULL, 0, 1, {A_Thrust}, 10, 1, S_FJSPINAXISA3}, // S_FJSPINAXISA2 - {SPR_NULL, 0, 0, {A_Thrust}, 0, 1, S_FJSPINAXISA4}, // S_FJSPINAXISA3 - {SPR_NULL, 0, 0, {A_SpawnObjectRelative}, 0, MT_FJSPINHELPERA, S_FJSPINAXISA5}, // S_FJSPINAXISA4 - {SPR_NULL, 0, 2, {A_FindTarget}, MT_FJSPINHELPERA, 0, S_FJSPINAXISA6}, // S_FJSPINAXISA5 - {SPR_NULL, 0, 1, {A_Thrust}, -10, 1, S_FJSPINAXISA7}, // S_FJSPINAXISA6 - {SPR_NULL, 0, 1, {A_Thrust}, 0, 1, S_FJSPINAXISA8}, // S_FJSPINAXISA7 - {SPR_NULL, 0, 0, {A_FireShot}, MT_FLAMEJETFLAMEB, -64, S_FJSPINAXISA9}, // S_FJSPINAXISA8 - {SPR_NULL, 0, 3, {A_ChangeAngleRelative}, 6, 6, S_FJSPINAXISA8}, // S_FJSPINAXISA9 - - {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FJSPINHELPERA2}, // S_FJSPINHELPERA1 - {SPR_NULL, 0, 0, {A_FindTarget}, MT_FJSPINAXISA, 0, S_FJSPINHELPERA3}, // S_FJSPINHELPERA2 - {SPR_NULL, 0, 1, {A_CapeChase}, 0, (64<<16), S_FJSPINHELPERA3}, // S_FJSPINHELPERA3 + {SPR_NULL, 0, 1, {A_TrapShot}, MT_FLAMEJETFLAMEB, -(16<<16)|(1<<15)|64, S_FJSPINAXISA2}, // S_FJSPINAXISA1 + {SPR_NULL, 0, 2, {A_ChangeAngleRelative}, 6, 6, S_FJSPINAXISA1}, // S_FJSPINAXISA2 // B: Clockwise - {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FJSPINAXISB2}, // S_FJSPINAXISB1 - {SPR_NULL, 0, 1, {A_Thrust}, 10, 1, S_FJSPINAXISB3}, // S_FJSPINAXISB2 - {SPR_NULL, 0, 0, {A_Thrust}, 0, 1, S_FJSPINAXISB4}, // S_FJSPINAXISB3 - {SPR_NULL, 0, 0, {A_SpawnObjectRelative}, 0, MT_FJSPINHELPERB, S_FJSPINAXISB5}, // S_FJSPINAXISB4 - {SPR_NULL, 0, 2, {A_FindTarget}, MT_FJSPINHELPERB, 0, S_FJSPINAXISB6}, // S_FJSPINAXISB5 - {SPR_NULL, 0, 1, {A_Thrust}, -10, 1, S_FJSPINAXISB7}, // S_FJSPINAXISB6 - {SPR_NULL, 0, 1, {A_Thrust}, 0, 1, S_FJSPINAXISB8}, // S_FJSPINAXISB7 - {SPR_NULL, 0, 0, {A_FireShot}, MT_FLAMEJETFLAMEB, -64, S_FJSPINAXISB9}, // S_FJSPINAXISB8 - {SPR_NULL, 0, 3, {A_ChangeAngleRelative}, -6, -6, S_FJSPINAXISB8}, // S_FJSPINAXISB9 - - {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FJSPINHELPERB2}, // S_FJSPINHELPERB1 - {SPR_NULL, 0, 0, {A_FindTarget}, MT_FJSPINAXISB, 0, S_FJSPINHELPERB3}, // S_FJSPINHELPERB2 - {SPR_NULL, 0, 1, {A_CapeChase}, 0, (64<<16), S_FJSPINHELPERB3}, // S_FJSPINHELPERB3 + {SPR_NULL, 0, 1, {A_TrapShot}, MT_FLAMEJETFLAMEB, -(16<<16)|(1<<15)|64, S_FJSPINAXISB2}, // S_FJSPINAXISB1 + {SPR_NULL, 0, 2, {A_ChangeAngleRelative}, -6, -6, S_FJSPINAXISB1}, // S_FJSPINAXISB2 // Blade's flame - {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|1, 1, {A_MoveRelative}, 0, 5, S_FLAMEJETFLAMEB2}, // S_FLAMEJETFLAMEB1 - {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|2, 1, {A_MoveRelative}, 0, 7, S_FLAMEJETFLAMEB3}, // S_FLAMEJETFLAMEB2 - {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|3,24, {NULL}, 0, 0, S_FLAMEJETFLAMEB4}, // S_FLAMEJETFLAMEB3 - {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|4,24, {NULL}, 0, 0, S_FLAMEJETFLAMEB5}, // S_FLAMEJETFLAMEB4 - {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|5,24, {NULL}, 0, 0, S_FLAMEJETFLAMEB6}, // S_FLAMEJETFLAMEB5 - {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|6,12, {NULL}, 0, 0, S_NULL}, // S_FLAMEJETFLAMEB6 + {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40, 1, {A_MoveRelative}, 0, 5, S_FLAMEJETFLAMEB2}, // S_FLAMEJETFLAMEB1 + {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40, 1, {A_MoveRelative}, 0, 7, S_FLAMEJETFLAMEB3}, // S_FLAMEJETFLAMEB2 + {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|FF_ANIMATE, (12*7), {NULL}, 7, 12, S_NULL}, // S_FLAMEJETFLAMEB3 // Trapgoyles {SPR_GARG, 0, 67, {NULL}, 0, 0, S_TRAPGOYLE_CHECK}, // S_TRAPGOYLE @@ -2006,8 +1984,10 @@ state_t states[NUMSTATES] = {SPR_BSZ7, 5, -1, {NULL}, 0, 0, S_NULL}, // S_BSZVINE_ORANGE {SPR_BSZ8, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BSZSHRUB {SPR_BSZ8, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BSZCLOVER - {SPR_BSZ8, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BSZFISH - {SPR_BSZ8, 3, -1, {NULL}, 0, 0, S_NULL}, // S_BSZSUNFLOWER + {SPR_BSZ8, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BIG_PALMTREE_TRUNK + {SPR_BSZ8, 3, -1, {NULL}, 0, 0, S_NULL}, // S_BIG_PALMTREE_TOP + {SPR_BSZ8, 4, -1, {NULL}, 0, 0, S_NULL}, // S_PALMTREE_TRUNK + {SPR_BSZ8, 5, -1, {NULL}, 0, 0, S_NULL}, // S_PALMTREE_TOP // Disco ball {SPR_DBAL, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_DBALL2}, // S_DBALL1 @@ -2762,25 +2742,25 @@ state_t states[NUMSTATES] = {SPR_FBLL, FF_FULLBRIGHT|6, 3, {NULL}, 0, 0, S_NULL}, // S_FIREBALLEXP3 // Turtle Shell - {SPR_SHLL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHELL - {SPR_SHLL, 0, 2, {NULL}, 0, 0, S_SHELL2}, // S_SHELL1 - {SPR_SHLL, 1, 2, {NULL}, 0, 0, S_SHELL3}, // S_SHELL2 - {SPR_SHLL, 2, 2, {NULL}, 0, 0, S_SHELL4}, // S_SHELL3 - {SPR_SHLL, 3, 2, {NULL}, 0, 0, S_SHELL1}, // S_SHELL4 + {SPR_SHLL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHELL // Puma (Mario fireball) - {SPR_PUMA, FF_FULLBRIGHT, 3, {A_FishJump}, 0, 0, S_PUMA2}, // S_PUMA1 - {SPR_PUMA, FF_FULLBRIGHT|1, 3, {A_FishJump}, 0, 0, S_PUMA3}, // S_PUMA2 - {SPR_PUMA, FF_FULLBRIGHT|2, 3, {A_FishJump}, 0, 0, S_PUMA1}, // S_PUMA3 - {SPR_PUMA, FF_FULLBRIGHT|3, 3, {A_FishJump}, 0, 0, S_PUMA5}, // S_PUMA4 - {SPR_PUMA, FF_FULLBRIGHT|4, 3, {A_FishJump}, 0, 0, S_PUMA6}, // S_PUMA5 - {SPR_PUMA, FF_FULLBRIGHT|5, 3, {A_FishJump}, 0, 0, S_PUMA4}, // S_PUMA6 + {SPR_PUMA, FF_FULLBRIGHT|2, 1, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_START2}, // S_PUMA_START1 + {SPR_PUMA, FF_FULLBRIGHT|2, 1, {A_PlaySound}, sfx_s3k70, 1, S_PUMA_UP1}, // S_PUMA_START2 + {SPR_PUMA, FF_FULLBRIGHT , 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_UP2}, // S_PUMA_UP1 + {SPR_PUMA, FF_FULLBRIGHT|1, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_UP3}, // S_PUMA_UP2 + {SPR_PUMA, FF_FULLBRIGHT|2, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_UP1}, // S_PUMA_UP3 + {SPR_PUMA, FF_FULLBRIGHT|3, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_DOWN2}, // S_PUMA_DOWN1 + {SPR_PUMA, FF_FULLBRIGHT|4, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_DOWN3}, // S_PUMA_DOWN2 + {SPR_PUMA, FF_FULLBRIGHT|5, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_DOWN1}, // S_PUMA_DOWN3 + + {SPR_PUMA, FF_FULLBRIGHT|FF_TRANS20|6, 4, {NULL}, 0, 0, S_PUMATRAIL2}, // S_PUMATRAIL1 + {SPR_PUMA, FF_FULLBRIGHT|FF_TRANS40|6, 5, {A_SetScale}, FRACUNIT, 1, S_PUMATRAIL3}, // S_PUMATRAIL2 + {SPR_PUMA, FF_FULLBRIGHT|FF_TRANS50|7, 4, {NULL}, 0, 0, S_PUMATRAIL4}, // S_PUMATRAIL3 + {SPR_PUMA, FF_FULLBRIGHT|FF_TRANS60|8, 3, {NULL}, 0, 0, S_NULL}, // S_PUMATRAIL4 // Hammer - {SPR_HAMM, 0, 3, {NULL}, 0, 0, S_HAMMER2}, // S_HAMMER1 - {SPR_HAMM, 1, 3, {NULL}, 0, 0, S_HAMMER3}, // S_HAMMER2 - {SPR_HAMM, 2, 3, {NULL}, 0, 0, S_HAMMER4}, // S_HAMMER3 - {SPR_HAMM, 3, 3, {NULL}, 0, 0, S_HAMMER1}, // S_HAMMER4 + {SPR_HAMM, FF_ANIMATE, -1, {NULL}, 4, 3, S_NULL}, // S_HAMMER // Koopa {SPR_KOOP, 0, -1, {NULL}, 0, 0, S_NULL}, // S_KOOPA1 @@ -3195,8 +3175,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_pop, // deathsound 4*FRACUNIT, // speed - 20*FRACUNIT, // radius - 24*FRACUNIT, // height + 28*FRACUNIT, // radius + 40*FRACUNIT, // height 0, // display offset 100, // mass 0, // damage @@ -3222,8 +3202,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_pop, // deathsound 8*FRACUNIT, // speed - 20*FRACUNIT, // radius - 24*FRACUNIT, // height + 28*FRACUNIT, // radius + 40*FRACUNIT, // height 0, // display offset 100, // mass 0, // damage @@ -4339,6 +4319,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_GOOPTRAIL + -1, // doomednum + S_GOOPTRAIL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 3, // speed + 4*FRACUNIT, // radius + 4*FRACUNIT, // height + 0, // display offset + 4, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_EGGMOBILE3 202, // doomednum S_EGGMOBILE3_STND, // spawnstate @@ -4726,7 +4733,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance + MT_NULL, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate @@ -5989,7 +5996,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound - S_STARPOST_SPIN, // painstate + S_STARPOST_STARTSPIN, // painstate 0, // painchance sfx_strpst, // painsound S_NULL, // meleestate @@ -5999,7 +6006,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 8, // speed 64*FRACUNIT, // radius - 80*FRACUNIT, // height + 128*FRACUNIT, // height 0, // display offset 4, // mass 0, // damage @@ -7927,7 +7934,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_GFZFLOWER2 801, // doomednum - S_GFZFLOWERB1, // spawnstate + S_GFZFLOWERB, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -7954,7 +7961,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_GFZFLOWER3 802, // doomednum - S_GFZFLOWERC1, // spawnstate + S_GFZFLOWERC, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -7975,7 +7982,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags S_NULL // raisestate }, @@ -8033,9 +8040,36 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_THZPLANT + { // MT_THZFLOWER1 900, // doomednum - S_THZPLANT1, // spawnstate + S_THZFLOWERA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 8, // speed + 8*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 16, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_THZFLOWER2 + 902, // doomednum + S_THZFLOWERB, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -8339,7 +8373,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance + MT_FLAMEPARTICLE, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate @@ -8357,6 +8391,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_FLAMEPARTICLE + -1, // doomednum + S_FLAMEPARTICLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + FRACUNIT, // radius + FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY, // flags + S_NULL // raisestate + }, + { // MT_EGGSTATUE 1102, // doomednum S_EGGSTATUE1, // spawnstate @@ -8924,33 +8985,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_FJSPINHELPERA - -1, // doomednum - S_FJSPINHELPERA1,// spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 10*FRACUNIT, // speed - 16*FRACUNIT, // radius - 1*FRACUNIT, // height - 0, // display offset - 100, // mass - 1, // damage - sfx_None, // activesound - MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOSECTOR, // flags - S_NULL // raisestate - }, - { // MT_FJSPINAXISB 3576, // doomednum S_FJSPINAXISB1, // spawnstate @@ -8978,33 +9012,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_FJSPINHELPERB - -1, // doomednum - S_FJSPINHELPERB1,// spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 8, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_NULL, // deathstate - S_NULL, // xdeathstate - sfx_None, // deathsound - 10*FRACUNIT, // speed - 16*FRACUNIT, // radius - 1*FRACUNIT, // height - 0, // display offset - 100, // mass - 1, // damage - sfx_None, // activesound - MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOSECTOR, // flags - S_NULL // raisestate - }, - { // MT_FLAMEJETFLAMEB -1, // doomednum S_FLAMEJETFLAMEB1, // spawnstate @@ -10709,9 +10716,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_BSZFISH + { // MT_BIG_PALMTREE_TRUNK 1472, // doomednum - S_BSZFISH, // spawnstate + S_BIG_PALMTREE_TRUNK, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -10736,9 +10743,63 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_BSZSUNFLOWER + { // MT_BIG_PALMTREE_TOP 1473, // doomednum - S_BSZSUNFLOWER, // spawnstate + S_BIG_PALMTREE_TOP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_PALMTREE_TRUNK + 1474, // doomednum + S_PALMTREE_TRUNK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_PALMTREE_TOP + 1475, // doomednum + S_PALMTREE_TOP, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -13085,9 +13146,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound - 20*FRACUNIT, // speed - 8*FRACUNIT, // radius - 16*FRACUNIT, // height + 16, // speed + 16*FRACUNIT, // radius + 20*FRACUNIT, // height 0, // display offset 100, // mass 1, // damage @@ -13098,19 +13159,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_PUMA 1805, // doomednum - S_PUMA1, // spawnstate + S_PUMA_START1, // spawnstate 1000, // spawnhealth - S_PUMA1, // seestate + S_PUMA_START1, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound - S_PUMA4, // meleestate + S_PUMA_DOWN1, // meleestate S_NULL, // missilestate S_NULL, // deathstate - S_PUMA6, // xdeathstate + S_PUMA_DOWN3, // xdeathstate sfx_None, // deathsound 0, // speed 8*FRACUNIT, // radius @@ -13119,12 +13180,40 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_PAIN|MF_FIRE, // flags + MF_PAIN|MF_FIRE, // flags S_NULL // raisestate }, + + { // MT_PUMATRAIL + -1, // doomednum + S_PUMATRAIL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 2*FRACUNIT, // radius + 4*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_HAMMER -1, // doomednum - S_HAMMER1, // spawnstate + S_HAMMER, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -14335,7 +14424,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14362,7 +14451,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14389,7 +14478,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14416,7 +14505,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14443,7 +14532,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14470,7 +14559,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14497,7 +14586,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14524,7 +14613,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14551,7 +14640,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14578,7 +14667,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14605,7 +14694,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14632,7 +14721,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14659,7 +14748,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14686,7 +14775,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14713,7 +14802,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, @@ -14740,7 +14829,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // display offset 1000, // mass 0, // damage - sfx_None, // activesound + sfx_crumbl, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, diff --git a/src/info.h b/src/info.h index edd8541d4..1a6c14a70 100644 --- a/src/info.h +++ b/src/info.h @@ -224,6 +224,7 @@ void A_FlickyFlounder(); void A_FlickyCheck(); void A_FlickyHeightCheck(); void A_FlickyFlutter(); +void A_FlameParticle(); // ratio of states to sprites to mobj types is roughly 6 : 1 : 1 #define NUMMOBJFREESLOTS 256 @@ -388,7 +389,8 @@ typedef enum sprite SPR_BUS2, // GFZ Bush w/o berries // Techno Hill Scenery - SPR_THZP, // Techno Hill Zone Plant + SPR_THZP, // THZ1 Flower + SPR_FWR5, // Another flower SPR_ALRM, // THZ2 Alarm // Deep Sea Scenery @@ -1200,6 +1202,7 @@ typedef enum state S_GOOP1, S_GOOP2, S_GOOP3, + S_GOOPTRAIL, // Boss 3 S_EGGMOBILE3_STND, @@ -1776,7 +1779,9 @@ typedef enum state // Starpost S_STARPOST_IDLE, S_STARPOST_FLASH, + S_STARPOST_STARTSPIN, S_STARPOST_SPIN, + S_STARPOST_ENDSPIN, // Big floating mine S_BIGMINE1, @@ -1960,21 +1965,15 @@ typedef enum state S_DEMONFIRE6, S_GFZFLOWERA, - S_GFZFLOWERA2, - - S_GFZFLOWERB1, - S_GFZFLOWERB2, - - S_GFZFLOWERC1, + S_GFZFLOWERB, + S_GFZFLOWERC, S_BERRYBUSH, S_BUSH, // THZ Plant - S_THZPLANT1, - S_THZPLANT2, - S_THZPLANT3, - S_THZPLANT4, + S_THZFLOWERA, + S_THZFLOWERB, // THZ Alarm S_ALARM1, @@ -2019,6 +2018,11 @@ typedef enum state S_FLAME2, S_FLAME3, S_FLAME4, + S_FLAME5, + S_FLAME6, + S_FLAMEPARTICLE, + + S_FLAMEREST, // Eggman Statue S_EGGSTATUE1, @@ -2080,36 +2084,13 @@ typedef enum state // Spinning flame jets S_FJSPINAXISA1, // Counter-clockwise S_FJSPINAXISA2, - S_FJSPINAXISA3, - S_FJSPINAXISA4, - S_FJSPINAXISA5, - S_FJSPINAXISA6, - S_FJSPINAXISA7, - S_FJSPINAXISA8, - S_FJSPINAXISA9, - S_FJSPINHELPERA1, - S_FJSPINHELPERA2, - S_FJSPINHELPERA3, S_FJSPINAXISB1, // Clockwise S_FJSPINAXISB2, - S_FJSPINAXISB3, - S_FJSPINAXISB4, - S_FJSPINAXISB5, - S_FJSPINAXISB6, - S_FJSPINAXISB7, - S_FJSPINAXISB8, - S_FJSPINAXISB9, - S_FJSPINHELPERB1, - S_FJSPINHELPERB2, - S_FJSPINHELPERB3, // Blade's flame S_FLAMEJETFLAMEB1, S_FLAMEJETFLAMEB2, S_FLAMEJETFLAMEB3, - S_FLAMEJETFLAMEB4, - S_FLAMEJETFLAMEB5, - S_FLAMEJETFLAMEB6, // Trapgoyles S_TRAPGOYLE, @@ -2204,8 +2185,10 @@ typedef enum state S_BSZVINE_ORANGE, S_BSZSHRUB, S_BSZCLOVER, - S_BSZFISH, - S_BSZSUNFLOWER, + S_BIG_PALMTREE_TRUNK, + S_BIG_PALMTREE_TOP, + S_PALMTREE_TRUNK, + S_PALMTREE_TOP, S_DBALL1, S_DBALL2, @@ -2924,20 +2907,19 @@ typedef enum state S_FIREBALLEXP2, S_FIREBALLEXP3, S_SHELL, - S_SHELL1, - S_SHELL2, - S_SHELL3, - S_SHELL4, - S_PUMA1, - S_PUMA2, - S_PUMA3, - S_PUMA4, - S_PUMA5, - S_PUMA6, - S_HAMMER1, - S_HAMMER2, - S_HAMMER3, - S_HAMMER4, + S_PUMA_START1, + S_PUMA_START2, + S_PUMA_UP1, + S_PUMA_UP2, + S_PUMA_UP3, + S_PUMA_DOWN1, + S_PUMA_DOWN2, + S_PUMA_DOWN3, + S_PUMATRAIL1, + S_PUMATRAIL2, + S_PUMATRAIL3, + S_PUMATRAIL4, + S_HAMMER, S_KOOPA1, S_KOOPA2, S_KOOPAFLAME1, @@ -3209,6 +3191,7 @@ typedef enum mobj_type MT_BOSSTANK2, MT_BOSSSPIGOT, MT_GOOP, + MT_GOOPTRAIL, // Boss 3 MT_EGGMOBILE3, @@ -3378,7 +3361,8 @@ typedef enum mobj_type MT_BUSH, // Techno Hill Scenery - MT_THZPLANT, // THZ Plant + MT_THZFLOWER1, + MT_THZFLOWER2, MT_ALARM, // Deep Sea Scenery @@ -3394,6 +3378,7 @@ typedef enum mobj_type // Castle Eggman Scenery MT_CHAIN, // CEZ Chain MT_FLAME, // Flame (has corona) + MT_FLAMEPARTICLE, MT_EGGSTATUE, // Eggman Statue MT_MACEPOINT, // Mace rotation point MT_SWINGMACEPOINT, // Mace swinging point @@ -3420,9 +3405,7 @@ typedef enum mobj_type MT_FLAMEJETFLAME, MT_FJSPINAXISA, // Counter-clockwise - MT_FJSPINHELPERA, MT_FJSPINAXISB, // Clockwise - MT_FJSPINHELPERB, MT_FLAMEJETFLAMEB, // Blade's flame @@ -3499,8 +3482,10 @@ typedef enum mobj_type MT_BSZVINE_ORANGE, MT_BSZSHRUB, MT_BSZCLOVER, - MT_BSZFISH, - MT_BSZSUNFLOWER, + MT_BIG_PALMTREE_TRUNK, + MT_BIG_PALMTREE_TOP, + MT_PALMTREE_TRUNK, + MT_PALMTREE_TOP, // Misc scenery MT_DBALL, @@ -3606,6 +3591,7 @@ typedef enum mobj_type MT_FIREBALL, MT_SHELL, MT_PUMA, + MT_PUMATRAIL, MT_HAMMER, MT_KOOPA, MT_KOOPAFLAME, diff --git a/src/p_enemy.c b/src/p_enemy.c index eace8ccc9..08a79b8cf 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -252,6 +252,7 @@ void A_FlickyFlounder(mobj_t *actor); void A_FlickyCheck(mobj_t *actor); void A_FlickyHeightCheck(mobj_t *actor); void A_FlickyFlutter(mobj_t *actor); +void A_FlameParticle(mobj_t *actor); // // ENEMY THINKING @@ -2134,13 +2135,15 @@ void A_Boss1Laser(mobj_t *actor) if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) { point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET); + point->angle = actor->angle; point->fuse = actor->tics+1; P_SetTarget(&point->target, actor->target); P_SetTarget(&actor->target, point); } } + /* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)) - actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y); + actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/ if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH) angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT); @@ -2190,11 +2193,16 @@ void A_Boss1Laser(mobj_t *actor) // var1: // 0 - accelerative focus with friction // 1 - steady focus with fixed movement speed -// var2 = unused +// anything else - don't move +// var2: +// 0 - don't trace target, just move forwards +// & 1 - change horizontal angle +// & 2 - change vertical angle // void A_FocusTarget(mobj_t *actor) { INT32 locvar1 = var1; + INT32 locvar2 = var2; #ifdef HAVE_BLUA if (LUA_CallAction("A_FocusTarget", actor)) return; @@ -2203,9 +2211,9 @@ void A_FocusTarget(mobj_t *actor) if (actor->target) { fixed_t speed = FixedMul(actor->info->speed, actor->scale); - fixed_t dist = R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y); - angle_t vangle = R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist); - angle_t hangle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1); + angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle); + angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90); switch(locvar1) { case 0: @@ -3534,41 +3542,49 @@ void A_ScoreRise(mobj_t *actor) // Function: A_ParticleSpawn // -// Description: Spawns a particle at a specified interval +// Description: Hyper-specialised function for spawning a particle for MT_PARTICLEGEN. // -// var1 = type (if 0, defaults to MT_PARTICLE) +// var1 = unused // var2 = unused // void A_ParticleSpawn(mobj_t *actor) { - INT32 locvar1 = var1; - fixed_t speed; - mobjtype_t type; + INT32 i = 0; mobj_t *spawn; #ifdef HAVE_BLUA if (LUA_CallAction("A_ParticleSpawn", actor)) return; #endif - if (!actor->spawnpoint) - { - P_RemoveMobj(actor); + if (!actor->health) return; + + if (!actor->lastlook) + return; + + if (!actor->threshold) + return; + + for (i = 0; i < actor->lastlook; i++) + { + spawn = P_SpawnMobj( + actor->x + FixedMul(FixedMul(actor->friction, actor->scale), FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), + actor->y + FixedMul(FixedMul(actor->friction, actor->scale), FINESINE(actor->angle>>ANGLETOFINESHIFT)), + actor->z, + (mobjtype_t)actor->threshold); + P_SetScale(spawn, actor->scale); + spawn->momz = FixedMul(actor->movefactor, spawn->scale); + spawn->destscale = spawn->scale/100; + spawn->scalespeed = spawn->scale/actor->health; + spawn->tics = (tic_t)actor->health; + spawn->flags2 |= (actor->flags2 & MF2_OBJECTFLIP); + spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones + if (spawn->frame & FF_ANIMATE) + spawn->frame += P_RandomKey(spawn->state->var1); + + actor->angle += actor->movedir; } - - if (locvar1) - type = (mobjtype_t)locvar1; - else - type = MT_PARTICLE; - - speed = FixedMul((actor->spawnpoint->angle >> 12)<scale); - - spawn = P_SpawnMobj(actor->x, actor->y, actor->z, type); - P_SetScale(spawn, actor->scale); - spawn->momz = speed; - spawn->destscale = FixedDiv(spawn->scale<scalespeed = FixedDiv(((actor->spawnpoint->angle >> 8) & 63) << FRACBITS, 100<tics = actor->spawnpoint->extrainfo + 1; + actor->angle += (angle_t)actor->movecount; } // Function: A_BunnyHop @@ -3882,11 +3898,18 @@ void A_DropMine(mobj_t *actor) void A_FishJump(mobj_t *actor) { INT32 locvar1 = var1; + INT32 locvar2 = var2; #ifdef HAVE_BLUA if (LUA_CallAction("A_FishJump", actor)) return; #endif + if (locvar2) + { + fixed_t rad = actor->radius>>FRACBITS; + P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale))) { fixed_t jumpval; @@ -9623,14 +9646,19 @@ void A_HomingChase(mobj_t *actor) // lower 16 bits = object # to fire // upper 16 bits = front offset // var2: -// lower 16 bits = vertical angle +// lower 15 bits = vertical angle variable +// 16th bit: +// - 0: use vertical angle variable as vertical angle in degrees +// - 1: mimic P_SpawnXYZMissile +// use z of actor minus z of missile as vertical distance to cover during momz calculation +// use vertical angle variable as horizontal distance to cover during momz calculation // upper 16 bits = height offset // void A_TrapShot(mobj_t *actor) { INT32 locvar1 = var1; INT32 locvar2 = var2; - angle_t vertang = FixedAngle(((INT16)(locvar2 & 65535))*FRACUNIT); + boolean oldstyle = (locvar2 & 32768) ? true : false; mobjtype_t type = (mobjtype_t)(locvar1 & 65535); mobj_t *missile; INT16 frontoff = (INT16)(locvar1 >> 16); @@ -9646,10 +9674,7 @@ void A_TrapShot(mobj_t *actor) y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale)); if (actor->eflags & MFE_VERTICALFLIP) - { z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale); - vertang = InvAngle(vertang); // flip firing angle - } else z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale); @@ -9660,20 +9685,35 @@ void A_TrapShot(mobj_t *actor) if (actor->eflags & MFE_VERTICALFLIP) missile->flags2 |= MF2_OBJECTFLIP; - missile->destscale = actor->destscale; - P_SetScale(missile, missile->destscale); + + missile->destscale = actor->scale; + P_SetScale(missile, actor->scale); if (missile->info->seesound) - S_StartSound(actor, missile->info->seesound); + S_StartSound(missile, missile->info->seesound); P_SetTarget(&missile->target, actor); missile->angle = actor->angle; speed = FixedMul(missile->info->speed, missile->scale); - missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed)); - missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed)); - missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed); + if (oldstyle) + { + missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed); + missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed); + // The below line basically mimics P_SpawnXYZMissile's momz calculation. + missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed); + P_CheckMissileSpawn(missile); + } + else + { + angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT); + if (actor->eflags & MFE_VERTICALFLIP) + vertang = InvAngle(vertang); // flip firing angle + missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed)); + missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed)); + missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed); + } } // Function: A_VileTarget @@ -10735,3 +10775,32 @@ void A_FlickyFlutter(mobj_t *actor) } #undef FLICKYHITWALL + +// Function: A_FlameParticle +// +// Description: Creates the mobj's painchance at a random position around the object's radius. +// +// var1 = momz of particle. +// +void A_FlameParticle(mobj_t *actor) +{ + mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance); + INT32 locvar1 = var1; + +#ifdef HAVE_BLUA + if (LUA_CallAction("A_FlameParticle", actor)) + return; +#endif + + if (type) + { + fixed_t rad = 2*actor->radius>>FRACBITS; + fixed_t hei = actor->height>>FRACBITS; + mobj_t *particle = P_SpawnMobjFromMobj(actor, + P_RandomRange(rad, -rad)<sector->ceilingdata = NULL; block->sector->floorspeed = 0; block->sector->ceilspeed = 0; + block->direction = 0; } for (i = -1; (i = P_FindSectorFromTag((INT16)block->vars[0], i)) >= 0 ;) @@ -1800,10 +1805,24 @@ static mobj_t *SearchMarioNode(msecnode_t *node) void T_MarioBlockChecker(levelspecthink_t *block) { line_t *masterline = block->sourceline; + if (block->vars[2] == 1) // Don't update the textures when the block's being bumped upwards. + return; if (SearchMarioNode(block->sector->touching_thinglist)) - sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].bottomtexture; + { + sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].bottomtexture; // Update textures + if (masterline->backsector) + { + block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->ceilingpic; // Update flats to be backside's ceiling + } + } else + { sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].toptexture; + if (masterline->backsector) + { + block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->floorpic; // Update flats to be backside's floor + } + } } // This is the Thwomp's 'brain'. It looks around for players nearby, and if @@ -2523,6 +2542,29 @@ void T_CameraScanner(elevator_t *elevator) } } +void T_PlaneDisplace(planedisplace_t *pd) +{ + sector_t *control, *target; + INT32 direction; + fixed_t diff; + + control = §ors[pd->control]; + target = §ors[pd->affectee]; + + if (control->floorheight == pd->last_height) + return; // no change, no movement + + direction = (control->floorheight > pd->last_height) ? 1 : -1; + diff = FixedMul(control->floorheight-pd->last_height, pd->speed); + + if (pd->type == pd_floor || pd->type == pd_both) + T_MovePlane(target, INT32_MAX/2, target->floorheight+diff, 0, 0, direction); // move floor + if (pd->type == pd_ceiling || pd->type == pd_both) + T_MovePlane(target, INT32_MAX/2, target->ceilingheight+diff, 0, 1, direction); // move ceiling + + pd->last_height = control->floorheight; +} + // // EV_DoFloor // @@ -2880,18 +2922,41 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover) size_t topmostvertex = 0, bottommostvertex = 0; fixed_t leftx, rightx; fixed_t topy, bottomy; - fixed_t topz; + fixed_t topz, bottomz; + fixed_t widthfactor, heightfactor; fixed_t a, b, c; mobjtype_t type = MT_ROCKCRUMBLE1; + fixed_t spacing = (32<master->frontsector->special, 3) >= 8) - type = MT_ROCKCRUMBLE1+(GETSECSPECIAL(rover->master->frontsector->special, 3)-7); +#define controlsec rover->master->frontsector + + if (controlsec->tag != 0) + { + INT32 tagline = P_FindSpecialLineFromTag(14, controlsec->tag, -1); + if (tagline != -1) + { + if (sides[lines[tagline].sidenum[0]].toptexture) + type = (mobjtype_t)sides[lines[tagline].sidenum[0]].toptexture; // Set as object type in p_setup.c... + if (sides[lines[tagline].sidenum[0]].textureoffset) + spacing = sides[lines[tagline].sidenum[0]].textureoffset; + if (sides[lines[tagline].sidenum[0]].rowoffset) + { + if (sides[lines[tagline].sidenum[0]].rowoffset>>FRACBITS != -1) + lifetime = (sides[lines[tagline].sidenum[0]].rowoffset>>FRACBITS); + else + lifetime = 0; + } + flags = lines[tagline].flags; + } + } + +#undef controlsec // soundorg z height never gets set normally, so MEH. sec->soundorg.z = sec->floorheight; - S_StartSound(&sec->soundorg, sfx_crumbl); + S_StartSound(&sec->soundorg, mobjinfo[type].activesound); // Find the outermost vertexes in the subsector for (i = 0; i < sec->linecount; i++) @@ -2910,23 +2975,46 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover) bottommostvertex = i; } - leftx = sec->lines[leftmostvertex]->v1->x+(16<lines[leftmostvertex]->v1->x+(spacing>>1); rightx = sec->lines[rightmostvertex]->v1->x; - topy = sec->lines[topmostvertex]->v1->y-(16<lines[topmostvertex]->v1->y-(spacing>>1); bottomy = sec->lines[bottommostvertex]->v1->y; - topz = *rover->topheight-(16<topheight-(spacing>>1); + bottomz = *rover->bottomheight; + + if (flags & ML_EFFECT1) { - for (b = topy; b > bottomy; b -= (32<>3; + heightfactor = (topz - *rover->bottomheight)>>2; + } + + for (a = leftx; a < rightx; a += spacing) + { + for (b = topy; b > bottomy; b -= spacing) { if (R_PointInSubsector(a, b)->sector == sec) { mobj_t *spawned = NULL; - for (c = topz; c > *rover->bottomheight; c -= (32<t_slope) + topz = P_GetZAt(*rover->t_slope, a, b) - (spacing>>1); + if (*rover->b_slope) + bottomz = P_GetZAt(*rover->b_slope, a, b); +#endif + + for (c = topz; c > bottomz; c -= spacing) { spawned = P_SpawnMobj(a, b, c, type); - spawned->fuse = 3*TICRATE; + spawned->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones + + if (flags & ML_EFFECT1) + { + P_InstaThrust(spawned, R_PointToAngle2(sec->soundorg.x, sec->soundorg.y, a, b), FixedDiv(P_AproxDistance(a - sec->soundorg.x, b - sec->soundorg.y), widthfactor)); + P_SetObjectMomZ(spawned, FixedDiv((c - bottomz), heightfactor), false); + } + + spawned->fuse = lifetime; } } } @@ -3086,8 +3174,10 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating, return 1; } -INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mobj_t *puncher) +INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) { + sector_t *roversec = rover->master->frontsector; + fixed_t topheight = *rover->topheight; levelspecthink_t *block; mobj_t *thing; fixed_t oldx = 0, oldy = 0, oldz = 0; @@ -3095,11 +3185,14 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob I_Assert(puncher != NULL); I_Assert(puncher->player != NULL); - if (sec->floordata || sec->ceilingdata) + if (roversec->floordata || roversec->ceilingdata) return 0; + if (!(rover->flags & FF_SOLID)) + rover->flags |= (FF_SOLID|FF_RENDERALL|FF_CUTLEVEL); + // Find an item to pop out! - thing = SearchMarioNode(sec->touching_thinglist); + thing = SearchMarioNode(roversec->touching_thinglist); // Found something! if (thing) @@ -3109,13 +3202,13 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL); P_AddThinker(&block->thinker); - sec->floordata = block; - sec->ceilingdata = block; + roversec->floordata = block; + roversec->ceilingdata = block; block->thinker.function.acp1 = (actionf_p1)T_MarioBlock; // Set up the fields - block->sector = sec; - block->vars[0] = roversector->tag; // actionsector + block->sector = roversec; + block->vars[0] = sector->tag; // actionsector block->vars[1] = 4*FRACUNIT; // speed block->vars[2] = 1; // Up // direction block->vars[3] = block->sector->floorheight; // floorwasheight @@ -3131,8 +3224,8 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob } P_UnsetThingPosition(thing); - thing->x = roversector->soundorg.x; - thing->y = roversector->soundorg.y; + thing->x = sector->soundorg.x; + thing->y = sector->soundorg.y; thing->z = topheight; thing->momz = FixedMul(6*FRACUNIT, thing->scale); P_SetThingPosition(thing); @@ -3149,7 +3242,7 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob { if (thing->type == MT_EMMY && thing->spawnpoint && (thing->spawnpoint->options & MTF_OBJECTSPECIAL)) { - mobj_t *tokenobj = P_SpawnMobj(roversector->soundorg.x, roversector->soundorg.y, topheight, MT_TOKEN); + mobj_t *tokenobj = P_SpawnMobj(sector->soundorg.x, sector->soundorg.y, topheight, MT_TOKEN); P_SetTarget(&thing->tracer, tokenobj); P_SetTarget(&tokenobj->target, thing); P_SetMobjState(tokenobj, mobjinfo[MT_TOKEN].seestate); diff --git a/src/p_inter.c b/src/p_inter.c index 54badc299..c7a15275a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1174,15 +1174,33 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Mario // // ***** // case MT_SHELL: - if (special->state == &states[S_SHELL]) // Resting anim { - // Kick that sucker around! - special->angle = toucher->angle; - P_InstaThrust(special, special->angle, FixedMul(special->info->speed, special->scale)); - S_StartSound(toucher, sfx_mario2); - P_SetMobjState(special, S_SHELL1); - P_SetTarget(&special->target, toucher); - special->threshold = (3*TICRATE)/2; + boolean bounceon = ((P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0)); + if (special->threshold == TICRATE) // it's moving + { + if (bounceon) + { + // Stop it! + special->momx = special->momy = 0; + S_StartSound(toucher, sfx_mario2); + P_SetTarget(&special->target, NULL); + special->threshold = TICRATE - 1; + toucher->momz = -toucher->momz; + } + else // can't handle in PIT_CheckThing because of landing-on causing it to stop + P_DamageMobj(toucher, special, special->target, 1, 0); + } + else if (special->threshold == 0) + { + // Kick that sucker around! + special->movedir = ((special->movedir == 1) ? -1 : 1); + P_InstaThrust(special, toucher->angle, (special->info->speed*special->scale)); + S_StartSound(toucher, sfx_mario2); + P_SetTarget(&special->target, toucher); + special->threshold = (3*TICRATE)/2; + if (bounceon) + toucher->momz = -toucher->momz; + } } return; case MT_AXE: @@ -1792,7 +1810,7 @@ void P_CheckTimeLimit(void) return; //Tagmode round end but only on the tic before the - //XD_EXITLEVEL packet is recieved by all players. + //XD_EXITLEVEL packet is received by all players. if (G_TagGametype()) { if (leveltime == (timelimitintics + 1)) @@ -1803,7 +1821,7 @@ void P_CheckTimeLimit(void) || (players[i].pflags & PF_TAGGED) || (players[i].pflags & PF_TAGIT)) continue; - CONS_Printf(M_GetText("%s recieved double points for surviving the round.\n"), player_names[i]); + CONS_Printf(M_GetText("%s received double points for surviving the round.\n"), player_names[i]); P_AddPlayerScore(&players[i], players[i].score); } } diff --git a/src/p_map.c b/src/p_map.c index 7afd266fd..d362caf80 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -768,8 +768,6 @@ static boolean PIT_CheckThing(mobj_t *thing) } } - if (tmthing->type == MT_SHELL && tmthing->threshold > TICRATE) - return true; // damage / explode if (tmthing->flags & MF_ENEMY) // An actual ENEMY! (Like the deton, for example) P_DamageMobj(thing, tmthing, tmthing, 1, 0); @@ -810,7 +808,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmthing->y = thing->y; P_SetThingPosition(tmthing); } - else + else if (!(tmthing->type == MT_SHELL && thing->player)) // player collision handled in touchspecial P_DamageMobj(thing, tmthing, tmthing->target, 1, 0); // don't traverse any more @@ -1145,7 +1143,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack... return false; - tmfloorz = tmceilingz = INT32_MIN; // block while in air + tmfloorz = tmceilingz = topz; // block while in air #ifdef ESLOPE tmceilingslope = NULL; #endif @@ -1191,7 +1189,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack... return false; - tmfloorz = tmceilingz = INT32_MAX; // block while in air + tmfloorz = tmceilingz = topz; // block while in air #ifdef ESLOPE tmfloorslope = NULL; #endif diff --git a/src/p_mobj.c b/src/p_mobj.c index f1dfa94f5..39682a7ef 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -564,7 +564,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) // Adjust the player's animation speed to match their velocity. if (!(disableSpeedAdjust || player->charflags & SF_NOSPEEDADJUST)) { - fixed_t speed;// = FixedDiv(player->speed, mobj->scale); + fixed_t speed;// = FixedDiv(player->speed, FixedMul(mobj->scale, player->mo->movefactor)); if (player->panim == PA_FALL) { speed = FixedDiv(abs(mobj->momz), mobj->scale); @@ -590,7 +590,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) } else { - speed = FixedDiv(player->speed, mobj->scale); + speed = FixedDiv(player->speed, FixedMul(mobj->scale, player->mo->movefactor)); if (player->panim == PA_ROLL || player->panim == PA_JUMP) { if (speed > 16<momx -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale); explodemo->momy -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale); S_StartSound(explodemo, sfx_cybdth); - - // Hack: Release an animal. - P_DamageMobj(mo, NULL, NULL, 1, DMG_INSTAKILL); } mo->flags &= ~MF_MISSILE; @@ -1845,7 +1842,6 @@ void P_CheckGravity(mobj_t *mo, boolean affect) } #define STOPSPEED (FRACUNIT) -#define FRICTION (ORIG_FRICTION) // 0.90625 // // P_SceneryXYFriction @@ -1878,7 +1874,6 @@ static void P_SceneryXYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) { // Stolen from P_SpawnFriction mo->friction = FRACUNIT - 0x100; - mo->movefactor = ((0x10092 - mo->friction)*(0x70))/0x158; } else mo->friction = ORIG_FRICTION; @@ -1903,7 +1898,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) // spinning friction if (player->pflags & PF_SPINNING && (player->rmomx || player->rmomy) && !(player->pflags & PF_STARTDASH)) { - const fixed_t ns = FixedDiv(549*FRICTION,500*FRACUNIT); + const fixed_t ns = FixedDiv(549*ORIG_FRICTION,500*FRACUNIT); mo->momx = FixedMul(mo->momx, ns); mo->momy = FixedMul(mo->momy, ns); } @@ -2181,8 +2176,39 @@ void P_XYMovement(mobj_t *mo) } else if (player || mo->flags & (MF_SLIDEME|MF_PUSHABLE)) { // try to slide along it +#ifdef ESLOPE + // Wall transfer part 1. + pslope_t *transferslope = NULL; + fixed_t transfermomz = 0; + if (oldslope && (P_MobjFlip(mo)*(predictedz - mo->z) > 0)) // Only for moving up (relative to gravity), otherwise there's a failed launch when going down slopes and hitting walls + { + transferslope = ((mo->standingslope) ? mo->standingslope : oldslope); + if (((transferslope->zangle < ANGLE_180) ? transferslope->zangle : InvAngle(transferslope->zangle)) >= ANGLE_45) // Prevent some weird stuff going on on shallow slopes. + transfermomz = P_GetWallTransferMomZ(mo, transferslope); + } +#endif + P_SlideMove(mo); xmove = ymove = 0; + +#ifdef ESLOPE + // Wall transfer part 2. + if (transfermomz && transferslope) // Are we "transferring onto the wall" (really just a disguised vertical launch)? + { + angle_t relation; // Scale transfer momentum based on how head-on it is to the slope. + if (mo->momx || mo->momy) // "Guess" the angle of the wall you hit using new momentum + relation = transferslope->xydirection - R_PointToAngle2(0, 0, mo->momx, mo->momy); + else // Give it for free, I guess. + relation = ANGLE_90; + transfermomz = FixedMul(transfermomz, + abs(FINESINE((relation >> ANGLETOFINESHIFT) & FINEMASK))); + if (P_MobjFlip(mo)*(transfermomz - mo->momz) > 2*FRACUNIT) // Do the actual launch! + { + mo->momz = transfermomz; + mo->standingslope = NULL; + } + } +#endif } else if (mo->type == MT_SPINFIRE) { @@ -2412,14 +2438,16 @@ static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motyp topheight = P_GetFOFTopZ(mo, sector, rover, mo->x, mo->y, NULL); bottomheight = P_GetFOFBottomZ(mo, sector, rover, mo->x, mo->y, NULL); - if (mo->player && (P_CheckSolidLava(mo, rover) || P_CanRunOnWater(mo->player, rover))) // only the player should be affected + if (mo->player && (P_CheckSolidLava(mo, rover) || P_CanRunOnWater(mo->player, rover))) // only the player should stand on lava or run on water ; else if (motype != 0 && rover->flags & FF_SWIMMABLE) // "scenery" only continue; else if (rover->flags & FF_QUICKSAND) // quicksand ; - else if (!((rover->flags & FF_BLOCKPLAYER && mo->player) // solid to players? - || (rover->flags & FF_BLOCKOTHERS && !mo->player))) // solid to others? + else if (!( // if it's not either of the following... + (rover->flags & (FF_BLOCKPLAYER|FF_MARIO) && mo->player) // ...solid to players? (mario blocks are always solid from beneath to players) + || (rover->flags & FF_BLOCKOTHERS && !mo->player) // ...solid to others? + )) // ...don't take it into account. continue; if (rover->flags & FF_QUICKSAND) { @@ -2444,7 +2472,9 @@ static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motyp delta1 = mo->z - (bottomheight + ((topheight - bottomheight)/2)); delta2 = thingtop - (bottomheight + ((topheight - bottomheight)/2)); + if (topheight > mo->floorz && abs(delta1) < abs(delta2) + && (rover->flags & FF_SOLID) // Non-FF_SOLID Mario blocks are only solid from bottom && !(rover->flags & FF_REVERSEPLATFORM) && ((P_MobjFlip(mo)*mo->momz >= 0) || (!(rover->flags & FF_PLATFORM)))) // In reverse gravity, only clip for FOFs that are intangible from their bottom (the "top" you're falling through) if you're coming from above ("below" in your frame of reference) { @@ -2452,7 +2482,7 @@ static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motyp } if (bottomheight < mo->ceilingz && abs(delta1) >= abs(delta2) && !(rover->flags & FF_PLATFORM) - && ((P_MobjFlip(mo)*mo->momz >= 0) || (!(rover->flags & FF_REVERSEPLATFORM)))) // In normal gravity, only clip for FOFs that are intangible from the top if you're coming from below + && ((P_MobjFlip(mo)*mo->momz >= 0) || ((rover->flags & FF_SOLID) && !(rover->flags & FF_REVERSEPLATFORM)))) // In normal gravity, only clip for FOFs that are intangible from the top if you're coming from below { mo->ceilingz = bottomheight; } @@ -2937,7 +2967,6 @@ static boolean P_ZMovement(mobj_t *mo) // Stolen from P_SpawnFriction mo->friction = FRACUNIT - 0x100; - mo->movefactor = ((0x10092 - mo->friction)*(0x70))/0x158; } else if (mo->type == MT_FALLINGROCK) { @@ -3392,8 +3421,13 @@ nightsdone: if (rover->flags & FF_MARIO && !(mo->eflags & MFE_VERTICALFLIP) // if you were flipped, your head isn't actually hitting your ceilingz is it? && *rover->bottomheight == mo->ceilingz) // The player's head hit the bottom! + { // DO THE MARIO! - EV_MarioBlock(rover->master->frontsector, node->m_sector, *rover->topheight, mo); + if (rover->flags & FF_SHATTERBOTTOM) // Brick block! + EV_CrumbleChain(node->m_sector, rover); + else // Question block! + EV_MarioBlock(rover, node->m_sector, mo); + } } } // Ugly ugly billions of braces! Argh! } @@ -4554,7 +4588,15 @@ static void P_Boss1Thinker(mobj_t *mobj) return; } - if (mobj->state != &states[mobj->info->spawnstate] && mobj->health > 0 && mobj->flags & MF_FLOAT && !(mobj->flags2 & MF2_SKULLFLY)) + if (mobj->flags2 & MF2_SKULLFLY) + { + fixed_t dist = (mobj->eflags & MFE_VERTICALFLIP) + ? ((mobj->ceilingz-(2*mobj->height)) - (mobj->z+mobj->height)) + : (mobj->z - (mobj->floorz+(2*mobj->height))); + if (dist > 0 && P_MobjFlip(mobj)*mobj->momz > 0) + mobj->momz = FixedMul(mobj->momz, FRACUNIT - (dist>>12)); + } + else if (mobj->state != &states[mobj->info->spawnstate] && mobj->health > 0 && mobj->flags & MF_FLOAT) mobj->momz = FixedMul(mobj->momz,7*FRACUNIT/8); if (mobj->state == &states[mobj->info->meleestate] @@ -6723,7 +6765,7 @@ void P_MobjThinker(mobj_t *mobj) } } - if (mobj->type == MT_GHOST && mobj->fuse > 0 // Not guaranteed to be MF_SCENERY or not MF_SCENERY! + if ((mobj->type == MT_GHOST || mobj->type == MT_THOK) && mobj->fuse > 0 // Not guaranteed to be MF_SCENERY or not MF_SCENERY! && (signed)(mobj->frame >> FF_TRANSSHIFT) < (NUMTRANSMAPS-1) - mobj->fuse / 2) // fade out when nearing the end of fuse... mobj->frame = (mobj->frame & ~FF_TRANSMASK) | (((NUMTRANSMAPS-1) - mobj->fuse / 2) << FF_TRANSSHIFT); @@ -7680,13 +7722,13 @@ void P_MobjThinker(mobj_t *mobj) P_NightsItemChase(mobj); break; case MT_SHELL: - if (mobj->threshold > TICRATE) + if (mobj->threshold && mobj->threshold != TICRATE) mobj->threshold--; - if (mobj->state != &states[S_SHELL]) + if (mobj->threshold >= TICRATE) { - mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); - P_InstaThrust(mobj, mobj->angle, FixedMul(mobj->info->speed, mobj->scale)); + mobj->angle += ((mobj->movedir == 1) ? ANGLE_22h : ANGLE_337h); + P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), (mobj->info->speed*mobj->scale)); } break; case MT_TURRET: @@ -8258,7 +8300,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->friction = ORIG_FRICTION; - mobj->movefactor = ORIG_FRICTION_FACTOR; + mobj->movefactor = FRACUNIT; // All mobjs are created at 100% scale. mobj->scale = FRACUNIT; @@ -9769,6 +9811,85 @@ ML_NOCLIMB : Direction not controllable } break; } + case MT_PARTICLEGEN: + { + fixed_t radius, speed, bottomheight, topheight; + INT32 type, numdivisions, time, anglespeed; + angle_t angledivision; + size_t line; + const size_t mthingi = (size_t)(mthing - mapthings); + + for (line = 0; line < numlines; line++) + { + if (lines[line].special == 15 && lines[line].tag == mthing->angle) + break; + } + + if (line == numlines) + { + CONS_Debug(DBG_GAMELOGIC, "Particle generator (mapthing #%s) needs tagged to a #15 parameter line (trying to find tag %d).\n", sizeu1(mthingi), mthing->angle); + return; + } + + if (sides[lines[line].sidenum[0]].toptexture) + type = sides[lines[line].sidenum[0]].toptexture; // Set as object type in p_setup.c... + else + type = (INT32)MT_PARTICLE; + + speed = abs(sides[lines[line].sidenum[0]].textureoffset); + bottomheight = lines[line].frontsector->floorheight; + topheight = lines[line].frontsector->ceilingheight - mobjinfo[(mobjtype_t)type].height; + + numdivisions = (mthing->options >> ZSHIFT); + + if (numdivisions) + { + radius = R_PointToDist2(lines[line].v1->x, lines[line].v1->y, lines[line].v2->x, lines[line].v2->y); + anglespeed = (sides[lines[line].sidenum[0]].rowoffset >> FRACBITS) % 360; + angledivision = 360/numdivisions; + } + else + { + numdivisions = 1; // Simple trick to make A_ParticleSpawn simpler. + radius = 0; + anglespeed = 0; + angledivision = 0; + } + + if ((speed) && (topheight > bottomheight)) + time = (INT32)(FixedDiv((topheight - bottomheight), speed) >> FRACBITS); + else + time = 1; // There's no reasonable way to set it, so just show the object for one tic and move on. + + if (mthing->options & MTF_OBJECTFLIP) + { + mobj->z = topheight; + speed *= -1; + } + else + mobj->z = bottomheight; + + CONS_Debug(DBG_GAMELOGIC, "Particle Generator (mapthing #%s):\n" + "Radius is %d\n" + "Speed is %d\n" + "Anglespeed is %d\n" + "Numdivisions is %d\n" + "Angledivision is %d\n" + "Time is %d\n" + "Type is %d\n", + sizeu1(mthingi), radius, speed, anglespeed, numdivisions, angledivision, time, type); + + mobj->angle = 0; + mobj->movefactor = speed; + mobj->lastlook = numdivisions; + mobj->movedir = angledivision*ANG1; + mobj->movecount = anglespeed*ANG1; + mobj->health = time; + mobj->friction = radius; + mobj->threshold = type; + + break; + } case MT_ROCKSPAWNER: mobj->threshold = mthing->angle; mobj->movecount = mthing->extrainfo; diff --git a/src/p_saveg.c b/src/p_saveg.c index bdeb6ff97..6abb4d14c 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -974,6 +974,7 @@ typedef enum tc_noenemies, tc_eachtime, tc_disappear, + tc_planedisplace, #ifdef POLYOBJECTS tc_polyrotate, // haleyjd 03/26/06: polyobjects tc_polymove, @@ -1097,7 +1098,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff |= MD_TRACER; if (mobj->friction != ORIG_FRICTION) diff |= MD_FRICTION; - if (mobj->movefactor != ORIG_FRICTION_FACTOR) + if (mobj->movefactor != FRACUNIT) diff |= MD_MOVEFACTOR; if (mobj->fuse) diff |= MD_FUSE; @@ -1537,6 +1538,21 @@ static void SaveDisappearThinker(const thinker_t *th, const UINT8 type) WRITEINT32(save_p, ht->exists); } +// +// SavePlaneDisplaceThinker +// +// Saves a planedisplace_t thinker +// +static void SavePlaneDisplaceThinker(const thinker_t *th, const UINT8 type) +{ + const planedisplace_t *ht = (const void *)th; + WRITEUINT8(save_p, type); + WRITEINT32(save_p, ht->affectee); + WRITEINT32(save_p, ht->control); + WRITEFIXED(save_p, ht->last_height); + WRITEFIXED(save_p, ht->speed); + WRITEUINT8(save_p, ht->type); +} #ifdef POLYOBJECTS // @@ -1818,6 +1834,12 @@ static void P_NetArchiveThinkers(void) SaveDisappearThinker(th, tc_disappear); continue; } + + else if (th->function.acp1 == (actionf_p1)T_PlaneDisplace) + { + SavePlaneDisplaceThinker(th, tc_planedisplace); + continue; + } #ifdef POLYOBJECTS else if (th->function.acp1 == (actionf_p1)T_PolyObjRotate) { @@ -2083,7 +2105,7 @@ static void LoadMobjThinker(actionf_p1 thinker) if (diff & MD_MOVEFACTOR) mobj->movefactor = READFIXED(save_p); else - mobj->movefactor = ORIG_FRICTION_FACTOR; + mobj->movefactor = FRACUNIT; if (diff & MD_FUSE) mobj->fuse = READINT32(save_p); if (diff & MD_WATERTOP) @@ -2486,6 +2508,23 @@ static inline void LoadDisappearThinker(actionf_p1 thinker) P_AddThinker(&ht->thinker); } +// +// LoadPlaneDisplaceThinker +// +// Loads a planedisplace_t thinker +// +static inline void LoadPlaneDisplaceThinker(actionf_p1 thinker) +{ + planedisplace_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL); + ht->thinker.function.acp1 = thinker; + ht->affectee = READINT32(save_p); + ht->control = READINT32(save_p); + ht->last_height = READFIXED(save_p); + ht->speed = READFIXED(save_p); + ht->type = READUINT8(save_p); + P_AddThinker(&ht->thinker); +} + #ifdef POLYOBJECTS // @@ -2769,6 +2808,10 @@ static void P_NetUnArchiveThinkers(void) case tc_disappear: LoadDisappearThinker((actionf_p1)T_Disappear); break; + + case tc_planedisplace: + LoadPlaneDisplaceThinker((actionf_p1)T_PlaneDisplace); + break; #ifdef POLYOBJECTS case tc_polyrotate: LoadPolyrotatetThinker((actionf_p1)T_PolyObjRotate); diff --git a/src/p_setup.c b/src/p_setup.c index fa35d7e2b..a7e430c3c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1562,6 +1562,8 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum) sd->text[6] = 0; break; } + + case 4: // Speed pad parameters case 414: // Play SFX { sd->toptexture = sd->midtexture = sd->bottomtexture = 0; @@ -1575,6 +1577,8 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum) break; } + case 14: // Bustable block parameters + case 15: // Fan particle spawner parameters case 425: // Calls P_SetMobjState on calling mobj case 434: // Custom Power case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors diff --git a/src/p_slopes.c b/src/p_slopes.c index d939fee98..f480b6e4f 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -804,6 +804,39 @@ void P_SlopeLaunch(mobj_t *mo) mo->standingslope = NULL; } +// +// P_GetWallTransferMomZ +// +// It would be nice to have a single function that does everything necessary for slope-to-wall transfer. +// However, it needs to be seperated out in P_XYMovement to take into account momentum before and after hitting the wall. +// This just performs the necessary calculations for getting the base vertical momentum; the horizontal is already reasonably calculated by P_SlideMove. +fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope) +{ + vector3_t slopemom, axis; + angle_t ang; + + if (mo->standingslope->flags & SL_NOPHYSICS) + return 0; + + // If there's physics, time for launching. + // Doesn't kill the vertical momentum as much as P_SlopeLaunch does. + ang = slope->zangle + ANG15*((slope->zangle > 0) ? 1 : -1); + if (ang > ANGLE_90 && ang < ANGLE_180) + ang = ((slope->zangle > 0) ? ANGLE_90 : InvAngle(ANGLE_90)); // hard cap of directly upwards + + slopemom.x = mo->momx; + slopemom.y = mo->momy; + slopemom.z = 3*(mo->momz/2); + + axis.x = -slope->d.y; + axis.y = slope->d.x; + axis.z = 0; + + FV3_Rotate(&slopemom, &axis, ang >> ANGLETOFINESHIFT); + + return 2*(slopemom.z/3); +} + // Function to help handle landing on slopes void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) { diff --git a/src/p_slopes.h b/src/p_slopes.h index de38f1d9e..f59c5b767 100644 --- a/src/p_slopes.h +++ b/src/p_slopes.h @@ -37,6 +37,7 @@ fixed_t P_GetZAt(pslope_t *slope, fixed_t x, fixed_t y); void P_QuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope); void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope); void P_SlopeLaunch(mobj_t *mo); +fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope); void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope); void P_ButteredSlope(mobj_t *mo); diff --git a/src/p_spec.c b/src/p_spec.c index 38185745e..b4380bb4b 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -51,6 +51,9 @@ mobj_t *skyboxmo[2]; // Amount (dx, dy) vector linedef is shifted right to get scroll amount #define SCROLL_SHIFT 5 +// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize. +#define MAXFLATSIZE (2048<sidenum[0]].toptexture; //P_AproxDistance(line->dx, line->dy)>>FRACBITS; + sfxnum = sides[line->sidenum[0]].toptexture; if (line->tag != 0 && line->flags & ML_EFFECT5) { @@ -3683,14 +3687,13 @@ DoneSection2: // Process Section 3 switch (special) { - case 1: // Ice/Sludge + case 1: // Unused case 2: // Wind/Current - case 3: // Ice/Sludge and Wind/Current + case 3: // Unused case 4: // Conveyor Belt break; - case 5: // Speed pad w/o spin - case 6: // Speed pad w/ spin + case 5: // Speed pad if (player->powers[pw_flashing] != 0 && player->powers[pw_flashing] < TICRATE/2) break; @@ -3700,9 +3703,16 @@ DoneSection2: { angle_t lineangle; fixed_t linespeed; + fixed_t sfxnum; lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y); - linespeed = P_AproxDistance(lines[i].v2->x-lines[i].v1->x, lines[i].v2->y-lines[i].v1->y); + linespeed = sides[lines[i].sidenum[0]].textureoffset; + + if (linespeed == 0) + { + CONS_Debug(DBG_GAMELOGIC, "ERROR: Speed pad (tag %d) at zero speed.\n", sector->tag); + break; + } player->mo->angle = lineangle; @@ -3732,7 +3742,7 @@ DoneSection2: P_InstaThrust(player->mo, player->mo->angle, linespeed); - if (GETSECSPECIAL(sector->special, 3) == 6 && (player->charability2 == CA2_SPINDASH)) + if ((lines[i].flags & ML_EFFECT5) && (player->charability2 == CA2_SPINDASH)) // Roll! { if (!(player->pflags & PF_SPINNING)) player->pflags |= PF_SPINNING; @@ -3741,19 +3751,26 @@ DoneSection2: } player->powers[pw_flashing] = TICRATE/3; - S_StartSound(player->mo, sfx_spdpad); + + sfxnum = sides[lines[i].sidenum[0]].toptexture; + + if (!sfxnum) + sfxnum = sfx_spdpad; + + S_StartSound(player->mo, sfxnum); } break; - case 7: // Bustable block sprite parameter - case 8: - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: + case 6: // Unused + case 7: // Unused + case 8: // Unused + case 9: // Unused + case 10: // Unused + case 11: // Unused + case 12: // Unused + case 13: // Unused + case 14: // Unused + case 15: // Unused break; } @@ -3923,8 +3940,14 @@ DoneSection2: } // Grab speed and sequence values - speed = abs(lines[lineindex].dx)/8; - sequence = abs(lines[lineindex].dy)>>FRACBITS; + speed = abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8; + sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS; + + if (speed == 0) + { + CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence); + break; + } // scan the thinkers // to find the first waypoint @@ -3996,8 +4019,14 @@ DoneSection2: } // Grab speed and sequence values - speed = -(abs(lines[lineindex].dx)/8); // Negative means reverse - sequence = abs(lines[lineindex].dy)>>FRACBITS; + speed = -abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8; // Negative means reverse + sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS; + + if (speed == 0) + { + CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence); + break; + } // scan the thinkers // to find the last waypoint @@ -4037,6 +4066,7 @@ DoneSection2: player->speed = speed; player->pflags |= PF_SPINNING; player->pflags &= ~(PF_JUMPED|PF_GLIDING|PF_SLIDING|PF_CANCARRY); + player->climbing = 0; if (player->mo->state-states != S_PLAY_SPIN) { @@ -4135,8 +4165,14 @@ DoneSection2: } // Grab speed and sequence values - speed = abs(lines[lineindex].dx)/8; - sequence = abs(lines[lineindex].dy)>>FRACBITS; + speed = abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8; + sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS; + + if (speed == 0) + { + CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence); + break; + } // Find the closest waypoint // Find the preceding waypoint @@ -4983,7 +5019,8 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f if ((flags & FF_MARIO)) { - P_AddBlockThinker(sec2, master); + if (!(flags & FF_SHATTERBOTTOM)) // Don't change the textures of a brick block, just a question block + P_AddBlockThinker(sec2, master); CheckForMarioBlocks = true; } @@ -5083,6 +5120,33 @@ static inline void P_AddBridgeThinker(line_t *sourceline, sector_t *sec) } */ +/** + * Adds a plane displacement thinker. + * Whenever the "control" sector moves, + * the "affectee" sector's floor or ceiling plane moves too! + * + * \param speed Rate of movement relative to control sector + * \param control Control sector. + * \param affectee Target sector. + * \sa P_SpawnSpecials, T_PlaneDisplace + * \author Monster Iestyn + */ +static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control, INT32 affectee) +{ + planedisplace_t *displace; + + // create and initialize new displacement thinker + displace = Z_Calloc(sizeof (*displace), PU_LEVSPEC, NULL); + P_AddThinker(&displace->thinker); + + displace->thinker.function.acp1 = (actionf_p1)T_PlaneDisplace; + displace->affectee = affectee; + displace->control = control; + displace->last_height = sectors[control].floorheight; + displace->speed = speed; + displace->type = type; +} + /** Adds a Mario block thinker, which changes the block's texture between blank * and ? depending on whether it has contents. * Needed in case objects respawn inside. @@ -5321,7 +5385,7 @@ static inline void P_AddCameraScanner(sector_t *sourcesec, sector_t *actionsecto elevator->distance = FixedInt(AngleFixed(angle)); } -static const ffloortype_e laserflags = FF_EXISTS|FF_RENDERALL|FF_NOSHADE|FF_EXTRA|FF_CUTEXTRA; +static const ffloortype_e laserflags = FF_EXISTS|FF_RENDERALL|FF_NOSHADE|FF_EXTRA|FF_CUTEXTRA|FF_TRANSLUCENT; /** Flashes a laser block. * @@ -5341,10 +5405,12 @@ void T_LaserFlash(laserthink_t *flash) if (!ffloor || !(ffloor->flags & FF_EXISTS)) return; - if (leveltime & 1) - ffloor->flags |= FF_RENDERALL; + if (leveltime & 2) + //ffloor->flags |= FF_RENDERALL; + ffloor->alpha = 0xB0; else - ffloor->flags &= ~FF_RENDERALL; + //ffloor->flags &= ~FF_RENDERALL; + ffloor->alpha = 0x90; sourcesec = ffloor->master->frontsector; // Less to type! @@ -5572,32 +5638,27 @@ void P_SpawnSpecials(INT32 fromnetsave) // Init line EFFECTs for (i = 0; i < numlines; i++) { - // set line specials to 0 here too, same reason as above - if (netgame || multiplayer) + if (lines[i].special != 7) // This is a hack. I can at least hope nobody wants to prevent flat alignment with arbitrary skin setups... { - // future: nonet flag? - } - else if ((lines[i].flags & ML_NETONLY) == ML_NETONLY) - { - lines[i].special = 0; - continue; - } - else - { - if (players[consoleplayer].charability == CA_THOK && (lines[i].flags & ML_NOSONIC)) + // set line specials to 0 here too, same reason as above + if (netgame || multiplayer) + { + // future: nonet flag? + } + else if ((lines[i].flags & ML_NETONLY) == ML_NETONLY) { lines[i].special = 0; continue; } - if (players[consoleplayer].charability == CA_FLY && (lines[i].flags & ML_NOTAILS)) + else { - lines[i].special = 0; - continue; - } - if (players[consoleplayer].charability == CA_GLIDEANDCLIMB && (lines[i].flags & ML_NOKNUX)) - { - lines[i].special = 0; - continue; + if ((players[consoleplayer].charability == CA_THOK && (lines[i].flags & ML_NOSONIC)) + || (players[consoleplayer].charability == CA_FLY && (lines[i].flags & ML_NOTAILS)) + || (players[consoleplayer].charability == CA_GLIDEANDCLIMB && (lines[i].flags & ML_NOKNUX))) + { + lines[i].special = 0; + continue; + } } } @@ -5643,47 +5704,53 @@ void P_SpawnSpecials(INT32 fromnetsave) break; #endif - case 7: // Flat alignment - if (lines[i].flags & ML_EFFECT4) // Align angle + case 7: // Flat alignment - redone by toast + if ((lines[i].flags & (ML_NOSONIC|ML_NOTAILS)) != (ML_NOSONIC|ML_NOTAILS)) // If you can do something... { - if (!(lines[i].flags & ML_EFFECT5)) // Align floor unless ALLTRIGGER flag is set + angle_t flatangle = InvAngle(R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y)); + fixed_t xoffs; + fixed_t yoffs; + + if (lines[i].flags & ML_NOKNUX) // Set offset through x and y texture offsets if NOKNUX flag is set { - for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) - sectors[s].spawn_flrpic_angle = sectors[s].floorpic_angle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y); + xoffs = sides[lines[i].sidenum[0]].textureoffset; + yoffs = sides[lines[i].sidenum[0]].rowoffset; + } + else // Otherwise, set calculated offsets such that line's v1 is the apparent origin + { + fixed_t cosinecomponent = FINECOSINE(flatangle>>ANGLETOFINESHIFT); + fixed_t sinecomponent = FINESINE(flatangle>>ANGLETOFINESHIFT); + xoffs = (-FixedMul(lines[i].v1->x, cosinecomponent) % MAXFLATSIZE) + (FixedMul(lines[i].v1->y, sinecomponent) % MAXFLATSIZE); // No danger of overflow thanks to the strategically placed modulo operations. + yoffs = (FixedMul(lines[i].v1->x, sinecomponent) % MAXFLATSIZE) + (FixedMul(lines[i].v1->y, cosinecomponent) % MAXFLATSIZE); // Ditto. } - if (!(lines[i].flags & ML_BOUNCY)) // Align ceiling unless BOUNCY flag is set + for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) { - for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) - sectors[s].spawn_ceilpic_angle = sectors[s].ceilingpic_angle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y); - } - } - else // Do offsets - { - if (!(lines[i].flags & ML_BLOCKMONSTERS)) // Align floor unless BLOCKMONSTERS flag is set - { - for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) + if (!(lines[i].flags & ML_NOSONIC)) // Modify floor flat alignment unless NOSONIC flag is set { - sectors[s].floor_xoffs += lines[i].dx; - sectors[s].floor_yoffs += lines[i].dy; + sectors[s].spawn_flrpic_angle = sectors[s].floorpic_angle = flatangle; + sectors[s].floor_xoffs += xoffs; + sectors[s].floor_yoffs += yoffs; // saved for netgames sectors[s].spawn_flr_xoffs = sectors[s].floor_xoffs; sectors[s].spawn_flr_yoffs = sectors[s].floor_yoffs; } - } - if (!(lines[i].flags & ML_NOCLIMB)) // Align ceiling unless NOCLIMB flag is set - { - for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) + if (!(lines[i].flags & ML_NOTAILS)) // Modify ceiling flat alignment unless NOTAILS flag is set { - sectors[s].ceiling_xoffs += lines[i].dx; - sectors[s].ceiling_yoffs += lines[i].dy; + sectors[s].spawn_ceilpic_angle = sectors[s].ceilingpic_angle = flatangle; + sectors[s].ceiling_xoffs += xoffs; + sectors[s].ceiling_yoffs += yoffs; // saved for netgames sectors[s].spawn_ceil_xoffs = sectors[s].ceiling_xoffs; sectors[s].spawn_ceil_yoffs = sectors[s].ceiling_yoffs; } } } + else // Otherwise, print a helpful warning. Can I do no less? + CONS_Alert(CONS_WARNING, + M_GetText("Flat alignment linedef (tag %d) doesn't have anything to do.\nConsider changing the linedef's flag configuration or removing it entirely.\n"), + lines[i].tag); break; case 8: // Sector Parameters @@ -5795,6 +5862,19 @@ void P_SpawnSpecials(INT32 fromnetsave) P_AddBridgeThinker(&lines[i], §ors[s]);*/ break; + case 66: // Displace floor by front sector + for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) + P_AddPlaneDisplaceThinker(pd_floor, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s); + break; + case 67: // Displace ceiling by front sector + for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) + P_AddPlaneDisplaceThinker(pd_ceiling, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s); + break; + case 68: // Displace both floor AND ceiling by front sector + for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;) + P_AddPlaneDisplaceThinker(pd_both, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s); + break; + case 100: // FOF (solid, opaque, shadows) P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers); break; @@ -5809,11 +5889,8 @@ void P_SpawnSpecials(INT32 fromnetsave) // Draw the 'insides' of the block too if (lines[i].flags & ML_NOCLIMB) { - ffloorflags |= FF_CUTLEVEL; - ffloorflags |= FF_BOTHPLANES; - ffloorflags |= FF_ALLSIDES; - ffloorflags &= ~FF_EXTRA; - ffloorflags &= ~FF_CUTEXTRA; + ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES; + ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA); } P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); @@ -5920,11 +5997,8 @@ void P_SpawnSpecials(INT32 fromnetsave) // Draw the 'insides' of the block too if (lines[i].flags & ML_EFFECT2) { - ffloorflags |= FF_CUTLEVEL; - ffloorflags |= FF_BOTHPLANES; - ffloorflags |= FF_ALLSIDES; - ffloorflags &= ~FF_EXTRA; - ffloorflags &= ~FF_CUTEXTRA; + ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES; + ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA); } P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); @@ -5938,11 +6012,8 @@ void P_SpawnSpecials(INT32 fromnetsave) // Draw the 'insides' of the block too if (lines[i].flags & ML_EFFECT2) { - ffloorflags |= FF_CUTLEVEL; - ffloorflags |= FF_BOTHPLANES; - ffloorflags |= FF_ALLSIDES; - ffloorflags &= ~FF_EXTRA; - ffloorflags &= ~FF_CUTEXTRA; + ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES; + ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA); } P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); @@ -5966,11 +6037,8 @@ void P_SpawnSpecials(INT32 fromnetsave) // Draw the 'insides' of the block too if (lines[i].flags & ML_EFFECT2) { - ffloorflags |= FF_CUTLEVEL; - ffloorflags |= FF_BOTHPLANES; - ffloorflags |= FF_ALLSIDES; - ffloorflags &= ~FF_EXTRA; - ffloorflags &= ~FF_CUTEXTRA; + ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES; + ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA); } P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); @@ -5984,11 +6052,8 @@ void P_SpawnSpecials(INT32 fromnetsave) // Draw the 'insides' of the block too if (lines[i].flags & ML_EFFECT2) { - ffloorflags |= FF_CUTLEVEL; - ffloorflags |= FF_BOTHPLANES; - ffloorflags |= FF_ALLSIDES; - ffloorflags &= ~FF_EXTRA; - ffloorflags &= ~FF_CUTEXTRA; + ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES; + ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA); } P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); @@ -6166,7 +6231,13 @@ void P_SpawnSpecials(INT32 fromnetsave) break; case 250: // Mario Block - P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL|FF_MARIO, secthinkers); + ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL|FF_MARIO; + if (lines[i].flags & ML_NOCLIMB) + ffloorflags |= FF_SHATTERBOTTOM; + if (lines[i].flags & ML_EFFECT1) + ffloorflags &= ~(FF_SOLID|FF_RENDERALL|FF_CUTLEVEL); + + P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); break; case 251: // A THWOMP! @@ -6180,10 +6251,11 @@ void P_SpawnSpecials(INT32 fromnetsave) break; case 252: // Shatter block (breaks when touched) + ffloorflags = FF_EXISTS|FF_RENDERALL|FF_BUSTUP|FF_SHATTER; if (lines[i].flags & ML_NOCLIMB) - P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP|FF_SHATTER|FF_SHATTERBOTTOM, secthinkers); - else - P_AddFakeFloorsByLine(i, FF_EXISTS|FF_RENDERALL|FF_BUSTUP|FF_SHATTER, secthinkers); + ffloorflags |= FF_SOLID|FF_SHATTERBOTTOM; + + P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); break; case 253: // Translucent shatter block (see 76) @@ -6191,10 +6263,11 @@ void P_SpawnSpecials(INT32 fromnetsave) break; case 254: // Bustable block + ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP; if (lines[i].flags & ML_NOCLIMB) - P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP|FF_ONLYKNUX, secthinkers); - else - P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP, secthinkers); + ffloorflags |= FF_ONLYKNUX; + + P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); break; case 255: // Spin bust block (breaks when jumped or spun downwards onto) @@ -6206,10 +6279,11 @@ void P_SpawnSpecials(INT32 fromnetsave) break; case 257: // Quicksand + ffloorflags = FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES; if (lines[i].flags & ML_EFFECT5) - P_AddFakeFloorsByLine(i, FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES|FF_RIPPLE, secthinkers); - else - P_AddFakeFloorsByLine(i, FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES, secthinkers); + ffloorflags |= FF_RIPPLE; + + P_AddFakeFloorsByLine(i, ffloorflags, secthinkers); break; case 258: // Laser block @@ -6970,7 +7044,6 @@ void T_Disappear(disappear_t *d) /** Adds friction thinker. * * \param friction Friction value, 0xe800 is normal. - * \param movefactor Inertia factor. * \param affectee Target sector. * \param roverfriction FOF or not * \sa T_Friction, P_SpawnFriction @@ -7008,22 +7081,10 @@ void T_Friction(friction_t *f) sec = sectors + f->affectee; - // Make sure the sector type hasn't changed + // Get FOF control sector if (f->roverfriction) - { referrer = sectors + f->referrer; - if (!(GETSECSPECIAL(referrer->special, 3) == 1 - || GETSECSPECIAL(referrer->special, 3) == 3)) - return; - } - else - { - if (!(GETSECSPECIAL(sec->special, 3) == 1 - || GETSECSPECIAL(sec->special, 3) == 3)) - return; - } - // Assign the friction value to players on the floor, non-floating, // and clipped. Normally the object's friction value is kept at // ORIG_FRICTION and this thinker changes it for icy or muddy floors. @@ -7053,14 +7114,16 @@ void T_Friction(friction_t *f) || (f->friction < thing->friction)) { thing->friction = f->friction; - thing->movefactor = f->movefactor; + if (thing->player) + thing->movefactor = f->movefactor; } } else if (P_GetSpecialBottomZ(thing, sec, sec) == thing->floorz && (thing->friction == ORIG_FRICTION // normal friction? || f->friction < thing->friction)) { thing->friction = f->friction; - thing->movefactor = f->movefactor; + if (thing->player) + thing->movefactor = f->movefactor; } } node = node->m_thinglist_next; @@ -7076,33 +7139,32 @@ static void P_SpawnFriction(void) size_t i; line_t *l = lines; register INT32 s; - fixed_t length; // line length controls magnitude + fixed_t strength; // frontside texture offset controls magnitude fixed_t friction; // friction value to be applied during movement INT32 movefactor; // applied to each player move to simulate inertia for (i = 0; i < numlines; i++, l++) if (l->special == 540) { - length = P_AproxDistance(l->dx, l->dy)>>FRACBITS; - friction = (0x1EB8*length)/0x80 + 0xD000; + strength = sides[l->sidenum[0]].textureoffset>>FRACBITS; + if (strength > 0) // sludge + strength = strength*2; // otherwise, the maximum sludginess value is +967... + + // The following might seem odd. At the time of movement, + // the move distance is multiplied by 'friction/0x10000', so a + // higher friction value actually means 'less friction'. + friction = ORIG_FRICTION - (0x1EB8*strength)/0x80; // ORIG_FRICTION is 0xE800 if (friction > FRACUNIT) friction = FRACUNIT; if (friction < 0) friction = 0; - // The following check might seem odd. At the time of movement, - // the move distance is multiplied by 'friction/0x10000', so a - // higher friction value actually means 'less friction'. - - if (friction > ORIG_FRICTION) // ice - movefactor = ((0x10092 - friction)*(0x70))/0x158; + movefactor = FixedDiv(ORIG_FRICTION, friction); + if (movefactor < FRACUNIT) + movefactor = 8*movefactor - 7*FRACUNIT; else - movefactor = ((friction - 0xDB34)*(0xA))/0x80; - - // killough 8/28/98: prevent odd situations - if (movefactor < 32) - movefactor = 32; + movefactor = FRACUNIT; for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;) Add_Friction(friction, movefactor, s, -1); @@ -7352,12 +7414,10 @@ void T_Pusher(pusher_t *p) { referrer = §ors[p->referrer]; - if (!(GETSECSPECIAL(referrer->special, 3) == 2 - || GETSECSPECIAL(referrer->special, 3) == 3)) + if (GETSECSPECIAL(referrer->special, 3) != 2) return; } - else if (!(GETSECSPECIAL(sec->special, 3) == 2 - || GETSECSPECIAL(sec->special, 3) == 3)) + else if (GETSECSPECIAL(sec->special, 3) != 2) return; // For constant pushers (wind/current) there are 3 situations: diff --git a/src/p_spec.h b/src/p_spec.h index 23e4cfdf9..0c77eb19f 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -325,7 +325,7 @@ INT32 EV_StartCrumble(sector_t *sector, ffloor_t *rover, INT32 EV_DoContinuousFall(sector_t *sec, sector_t *pbacksector, fixed_t spd, boolean backwards); -INT32 EV_MarioBlock(sector_t *sector, sector_t *roversector, fixed_t topheight, mobj_t *puncher); +INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher); void T_MoveFloor(floormove_t *movefloor); @@ -388,7 +388,7 @@ typedef struct { thinker_t thinker; ///< Thinker structure for friction. INT32 friction; ///< Friction value, 0xe800 = normal. - INT32 movefactor; ///< Inertia factor when adding to momentum. + INT32 movefactor; ///< Inertia factor when adding to momentum, FRACUNIT = normal. INT32 affectee; ///< Number of affected sector. INT32 referrer; ///< If roverfriction == true, then this will contain the sector # of the control sector where the effect was applied. UINT8 roverfriction; ///< flag for whether friction originated from a FOF or not @@ -450,6 +450,26 @@ void T_Disappear(disappear_t *d); void T_Pusher(pusher_t *p); mobj_t *P_GetPushThing(UINT32 s); +// Plane displacement +typedef struct +{ + thinker_t thinker; ///< Thinker structure for plane displacement effect. + INT32 affectee; ///< Number of affected sector. + INT32 control; ///< Control sector used to control plane positions. + fixed_t last_height; ///< Last known height of control sector. + fixed_t speed; ///< Plane movement speed. + /** Types of plane displacement effects. + */ + enum + { + pd_floor, ///< Displace floor. + pd_ceiling, ///< Displace ceiling. + pd_both, ///< Displace both floor AND ceiling. + } type; +} planedisplace_t; + +void T_PlaneDisplace(planedisplace_t *pd); + void P_CalcHeight(player_t *player); sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo); diff --git a/src/p_user.c b/src/p_user.c index 09fec73c0..905c3be62 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1624,6 +1624,12 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type) // scale P_SetScale(mobj, player->mo->scale); mobj->destscale = player->mo->scale; + + if (type == MT_THOK) // spintrail-specific modification for MT_THOK + { + mobj->frame = FF_TRANS70; + mobj->fuse = mobj->tics; + } } P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do @@ -4791,6 +4797,9 @@ static void P_3dMovement(player_t *player) acceleration = player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration; } + if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration... + acceleration = FixedMul(acceleration<mo->movefactor)>>FRACBITS; + // Forward movement if (player->climbing) { @@ -6447,7 +6456,7 @@ static void P_SkidStuff(player_t *player) // If your push angle is more than this close to a full 180 degrees, trigger a skid. if (dang > ANGLE_157h) { - player->skidtime = TICRATE/2; + player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS; S_StartSound(player->mo, sfx_skid); if (player->panim != PA_WALK) P_SetPlayerMobjState(player->mo, S_PLAY_WALK); @@ -6484,6 +6493,9 @@ static void P_MovePlayer(player_t *player) cmd = &player->cmd; runspd = FixedMul(player->runspeed, player->mo->scale); + // Let's have some movement speed fun on low-friction surfaces, JUST for players... (high friction surfaces shouldn't have any adjustment, since the acceleration in this game is super high and that ends up cheesing high-friction surfaces.) + runspd = FixedMul(runspd, player->mo->movefactor); + // Control relinquishing stuff! if (player->powers[pw_ingoop]) player->pflags |= PF_FULLSTASIS; @@ -6674,6 +6686,7 @@ static void P_MovePlayer(player_t *player) if (!player->mo->momx && !player->mo->momy && !player->mo->momz && player->panim == PA_WALK) P_SetPlayerMobjState(player->mo, S_PLAY_STND); + player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame. ////////////////// //GAMEPLAY STUFF// @@ -6895,7 +6908,7 @@ static void P_MovePlayer(player_t *player) P_ResetScore(player); // Show the "THOK!" graphic when spinning quickly across the ground. (even applies to non-spinners, in the case of zoom tubes) - if (player->pflags & PF_SPINNING && player->speed > FixedMul(15<mo->scale) && !(player->pflags & PF_JUMPED)) + if (player->pflags & PF_SPINNING && P_AproxDistance(player->speed, player->mo->momz) > FixedMul(15<mo->scale) && !(player->pflags & PF_JUMPED)) { P_SpawnSpinMobj(player, player->spinitem); if (demorecording) diff --git a/src/screen.c b/src/screen.c index 97c865a97..7fdef4b19 100644 --- a/src/screen.c +++ b/src/screen.c @@ -30,7 +30,7 @@ #include "f_finale.h" -#if defined (USEASM) //&& (!defined (_MSC_VER) || (_MSC_VER <= 1200)) +#if defined (USEASM) && !defined (NORUSEASM)//&& (!defined (_MSC_VER) || (_MSC_VER <= 1200)) #define RUSEASM //MSC.NET can't patch itself #endif diff --git a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj index c3f0d3b38..32ae88c02 100644 --- a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj +++ b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj @@ -1214,7 +1214,7 @@ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CURRENT_PROJECT_VERSION = 2.1.14; + CURRENT_PROJECT_VERSION = 2.1.17; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", NORMALSRB2, @@ -1226,7 +1226,7 @@ C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CURRENT_PROJECT_VERSION = 2.1.14; + CURRENT_PROJECT_VERSION = 2.1.17; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_PREPROCESSOR_DEFINITIONS = ( diff --git a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj index 98599fb60..13e78f314 100644 --- a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj +++ b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj @@ -1214,7 +1214,7 @@ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CURRENT_PROJECT_VERSION = 2.1.14; + CURRENT_PROJECT_VERSION = 2.1.17; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", NORMALSRB2, @@ -1226,7 +1226,7 @@ C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CURRENT_PROJECT_VERSION = 2.1.14; + CURRENT_PROJECT_VERSION = 2.1.17; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_PREPROCESSOR_DEFINITIONS = ( diff --git a/src/v_video.c b/src/v_video.c index 83dd8fb9b..205ba34c0 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -267,7 +267,7 @@ static void CV_Gammaxxx_ONChange(void) #endif -#if defined (__GNUC__) && defined (__i386__) && !defined (NOASM) && !defined (__APPLE__) +#if defined (__GNUC__) && defined (__i386__) && !defined (NOASM) && !defined (__APPLE__) && !defined (NORUSEASM) void VID_BlitLinearScreen_ASM(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT32 height, size_t srcrowbytes, size_t destrowbytes); #define HAVE_VIDCOPY diff --git a/src/w_wad.c b/src/w_wad.c index aeaad3ced..e4cb93050 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1223,6 +1223,7 @@ int W_VerifyNMUSlumps(const char *filename) {"COLORMAP", 8}, {"PAL", 3}, {"CLM", 3}, + {"TRANS", 5}, {NULL, 0}, }; return W_VerifyFile(filename, NMUSlist, false); diff --git a/src/win32/win_main.c b/src/win32/win_main.c index 663eddbd4..d84c86232 100644 --- a/src/win32/win_main.c +++ b/src/win32/win_main.c @@ -69,7 +69,7 @@ static HCURSOR windowCursor = NULL; // main window cursor static LPCSTR wClassName = "SRB2WC"; -boolean appActive = false; // app window is active +INT appActive = false; // app window is active #ifdef LOGMESSAGES FILE *logstream; diff --git a/src/win32/win_main.h b/src/win32/win_main.h index ed55246ab..326a813dd 100644 --- a/src/win32/win_main.h +++ b/src/win32/win_main.h @@ -23,7 +23,7 @@ extern HWND hWndMain; -extern boolean appActive; +extern INT appActive; VOID I_GetSysMouseEvents(INT mouse_state); extern UINT MSHWheelMessage;