diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index db71b8c39..1d461a820 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -453,8 +453,9 @@ if(${SRB2_CONFIG_HAVE_DISCORDRPC}) if(${DISCORDRPC_FOUND}) set(SRB2_HAVE_DISCORDRPC ON) add_definitions(-DHAVE_DISCORDRPC) - set(SRB2_DISCORDRPC_SOURCES discord.c) - set(SRB2_DISCORDRPC_HEADERS discord.h) + add_definitions(-DUSE_STUN) + set(SRB2_DISCORDRPC_SOURCES discord.c stun.c) + set(SRB2_DISCORDRPC_HEADERS discord.h stun.h) prepend_sources(SRB2_DISCORDRPC_SOURCES) prepend_sources(SRB2_DISCORDRPC_HEADERS) source_group("Discord Rich Presence" FILES ${SRB2_DISCORDRPC_SOURCES} ${SRB2_DISCORDRPC_HEADERS}) diff --git a/src/Makefile b/src/Makefile index b9a653eef..de857fd1d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -393,8 +393,8 @@ endif ifdef HAVE_DISCORDRPC LIBS+=-ldiscord-rpc -CFLAGS+=-DHAVE_DISCORDRPC -OBJS+=$(OBJDIR)/discord.o +CFLAGS+=-DHAVE_DISCORDRPC -DUSE_STUN +OBJS+=$(OBJDIR)/discord.o $(OBJDIR)/stun.o endif include blua/Makefile.cfg diff --git a/src/command.h b/src/command.h index 851688b6c..44d5e50b3 100644 --- a/src/command.h +++ b/src/command.h @@ -35,7 +35,7 @@ enum /* Command buffer flags. */ enum { - COM_SAFE = 1, + COM_SAFE = 0x01, }; typedef void (*com_func_t)(void); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 37fbc1ff5..3c4c087df 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -546,6 +546,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) // Score is resynched in the rspfirm resync packet rsp->rings = SHORT(players[i].rings); + rsp->spheres = SHORT(players[i].spheres); rsp->lives = players[i].lives; rsp->lostlife = players[i].lostlife; rsp->continues = players[i].continues; @@ -616,7 +617,14 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->airtime = (tic_t)LONG(players[i].airtime); rsp->trickpanel = (UINT8)players[i].trickpanel; - rsp->trickdelay = (tic_t)LONG(players[i].trickdelay); + rsp->trickdelay = (boolean)players[i].trickdelay; + rsp->trickmomx = (fixed_t)LONG(players[i].trickmomx); + rsp->trickmomy = (fixed_t)LONG(players[i].trickmomy); + rsp->trickmomz = (fixed_t)LONG(players[i].trickmomz); + + rsp->bumpers = players[i].bumpers; + rsp->karmadelay = SHORT(players[i].karmadelay); + rsp->eliminated = players[i].eliminated; // respawnvars_t rsp->respawn_state = players[i].respawn.state; @@ -692,6 +700,7 @@ static void resynch_read_player(resynch_pak *rsp) // Score is resynched in the rspfirm resync packet players[i].rings = SHORT(rsp->rings); + players[i].spheres = SHORT(rsp->spheres); players[i].lives = rsp->lives; players[i].lostlife = rsp->lostlife; players[i].continues = rsp->continues; @@ -761,7 +770,14 @@ static void resynch_read_player(resynch_pak *rsp) players[i].airtime = (tic_t)LONG(rsp->airtime); players[i].trickpanel = (UINT8)rsp->trickpanel; - players[i].trickdelay = (tic_t)LONG(rsp->trickdelay); + players[i].trickdelay = (boolean)rsp->trickdelay; + players[i].trickmomx = (fixed_t)LONG(rsp->trickmomx); + players[i].trickmomy = (fixed_t)LONG(rsp->trickmomy); + players[i].trickmomz = (fixed_t)LONG(rsp->trickmomz); + + players[i].bumpers = rsp->bumpers; + players[i].karmadelay = SHORT(rsp->karmadelay); + players[i].eliminated = rsp->eliminated; // respawnvars_t players[i].respawn.state = rsp->respawn_state; @@ -1250,7 +1266,7 @@ static inline void CL_DrawConnectionStatus(void) cltext = M_GetText("Server full, waiting for a slot..."); else cltext = M_GetText("Requesting to join..."); - + break; #ifdef HAVE_CURL case CL_PREPAREHTTPFILES: @@ -1994,6 +2010,9 @@ static void SendAskInfo(INT32 node) // now allowed traffic from the host to us in, so once the MS relays // our address to the host, it'll be able to speak to us. HSendPacket(node, false, 0, sizeof (askinfo_pak)); + + if (node != 0 && node != BROADCASTADDR) + I_NetRequestHolePunch(); } serverelem_t serverlist[MAXSERVERLIST]; @@ -2116,7 +2135,7 @@ void CL_UpdateServerList (void) static void M_ConfirmConnect(event_t *ev) { -#ifndef NONET +#ifndef NONET if (ev->type == ev_keydown) { if (ev->data1 == ' ' || ev->data1 == 'y' || ev->data1 == KEY_ENTER || ev->data1 == gamecontrol[0][gc_accelerate][0] || ev->data1 == gamecontrol[0][gc_accelerate][1]) @@ -2139,7 +2158,7 @@ static void M_ConfirmConnect(event_t *ev) } else cl_mode = CL_LOADFILES; - + M_ClearMenus(true); } else if (ev->data1 == 'n' || ev->data1 == KEY_ESCAPE|| ev->data1 == gamecontrol[0][gc_brake][0] || ev->data1 == gamecontrol[0][gc_brake][1]) @@ -2350,7 +2369,10 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) cl_mode = CL_CHECKFILES; } else + { cl_mode = CL_ASKJOIN; // files need not be checked for the server. + *asksent = 0; + } return true; } @@ -2384,7 +2406,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic { boolean waitmore; INT32 i; - + #ifdef NONET (void)tmpsave; #endif @@ -2421,7 +2443,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic { curl_transfers++; } - + cl_mode = CL_DOWNLOADHTTPFILES; } break; @@ -2986,9 +3008,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) } } - if (K_IsPlayerWanted(&players[playernum])) - K_CalculateBattleWanted(); - + K_CalculateBattleWanted(); LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting // don't look through someone's view who isn't there @@ -3579,6 +3599,76 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) } } +#ifdef HAVE_CURL +/** Add a login for HTTP downloads. If the + * user/password is missing, remove it. + * + * \sa Command_list_http_logins + */ +static void Command_set_http_login (void) +{ + HTTP_login *login; + HTTP_login **prev_next; + + if (COM_Argc() < 2) + { + CONS_Printf( + "set_http_login [user:password]: Set or remove a login to " + "authenticate HTTP downloads.\n" + ); + return; + } + + login = CURLGetLogin(COM_Argv(1), &prev_next); + + if (COM_Argc() == 2) + { + if (login) + { + (*prev_next) = login->next; + CONS_Printf("Login for '%s' removed.\n", login->url); + Z_Free(login); + } + } + else + { + if (login) + Z_Free(login->auth); + else + { + login = ZZ_Alloc(sizeof *login); + login->url = Z_StrDup(COM_Argv(1)); + } + + login->auth = Z_StrDup(COM_Argv(2)); + + login->next = curl_logins; + curl_logins = login; + } +} + +/** List logins for HTTP downloads. + * + * \sa Command_set_http_login + */ +static void Command_list_http_logins (void) +{ + HTTP_login *login; + + for ( + login = curl_logins; + login; + login = login->next + ){ + CONS_Printf( + "'%s' -> '%s'\n", + login->url, + login->auth + ); + } +} +#endif/*HAVE_CURL*/ + static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); @@ -3654,6 +3744,10 @@ void D_ClientServerInit(void) COM_AddCommand("reloadbans", Command_ReloadBan); COM_AddCommand("connect", Command_connect); COM_AddCommand("nodes", Command_Nodes); +#ifdef HAVE_CURL + COM_AddCommand("set_http_login", Command_set_http_login); + COM_AddCommand("list_http_logins", Command_list_http_logins); +#endif #ifdef PACKETDROP COM_AddCommand("drop", Command_Drop); COM_AddCommand("droprate", Command_Droprate); @@ -5491,7 +5585,10 @@ static void CL_SendClientCmd(void) boolean mis = false; if (lowest_lag && ( gametic % lowest_lag )) + { + cl_packetmissed = true; return; + } netbuffer->packettype = PT_CLIENTCMD; @@ -6016,6 +6113,19 @@ static void UpdatePingTable(void) } } +static void RenewHolePunch(void) +{ + static time_t past; + + const time_t now = time(NULL); + + if ((now - past) > 20) + { + I_NetRegisterHolePunch(); + past = now; + } +} + // Handle timeouts to prevent definitive freezes from happenning static void HandleNodeTimeouts(void) { @@ -6054,6 +6164,11 @@ void NetKeepAlive(void) MasterClient_Ticker(); #endif + if (netgame && serverrunning) + { + RenewHolePunch(); + } + if (client) { // send keep alive @@ -6113,6 +6228,11 @@ void NetUpdate(void) MasterClient_Ticker(); // Acking the Master Server #endif + if (netgame && serverrunning) + { + RenewHolePunch(); + } + if (client) { if (!resynch_local_inprogress) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index fed85df52..ae9e59e7a 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -37,7 +37,7 @@ applications may follow different packet versions. // be transmitted. // Networking and tick handling related. -#define BACKUPTICS 32 +#define BACKUPTICS 1024 #define TICQUEUE 512 // more than enough for most timeouts.... #define MAXTEXTCMD 256 // @@ -214,6 +214,7 @@ typedef struct // Score is resynched in the confirm resync packet INT16 rings; + INT16 spheres; SINT8 lives; boolean lostlife; SINT8 continues; @@ -281,7 +282,14 @@ typedef struct INT32 kartstuff[NUMKARTSTUFF]; tic_t airtime; UINT8 trickpanel; - tic_t trickdelay; + boolean trickdelay; + fixed_t trickmomx; + fixed_t trickmomy; + fixed_t trickmomz; + + UINT8 bumpers; + INT16 karmadelay; + boolean eliminated; // respawnvars_t UINT8 respawn_state; @@ -569,6 +577,7 @@ extern INT32 mapchangepending; // Points inside doomcom extern doomdata_t *netbuffer; +extern consvar_t cv_stunserver; extern consvar_t cv_httpsource; extern consvar_t cv_showjoinaddress; diff --git a/src/d_main.c b/src/d_main.c index db0f74fb2..045d558ed 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1494,6 +1494,11 @@ void D_SRB2Main(void) CON_Init(); + memset(timelimits, 0, sizeof(timelimits)); + memset(pointlimits, 0, sizeof(pointlimits)); + + timelimits[GT_BATTLE] = 2; + D_RegisterServerCommands(); D_RegisterClientCommands(); // be sure that this is called before D_CheckNetGame R_RegisterEngineStuff(); diff --git a/src/d_net.c b/src/d_net.c index d4e1f4d03..8af1889bc 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -49,6 +49,8 @@ tic_t connectiontimeout = (10*TICRATE); doomcom_t *doomcom = NULL; /// \brief network packet data, points inside doomcom doomdata_t *netbuffer = NULL; +/// \brief hole punching packet, also points inside doomcom +holepunch_t *holepunchpacket = NULL; #ifdef DEBUGFILE FILE *debugfile = NULL; // put some net info in a file during the game @@ -72,6 +74,8 @@ boolean (*I_NetCanGet)(void) = NULL; void (*I_NetCloseSocket)(void) = NULL; void (*I_NetFreeNodenum)(INT32 nodenum) = NULL; SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL; +void (*I_NetRequestHolePunch)(void) = NULL; +void (*I_NetRegisterHolePunch)(void) = NULL; boolean (*I_NetOpenSocket)(void) = NULL; boolean (*I_Ban) (INT32 node) = NULL; void (*I_ClearBans)(void) = NULL; @@ -1347,6 +1351,7 @@ boolean D_CheckNetGame(void) I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES); netbuffer = (doomdata_t *)(void *)&doomcom->data; + holepunchpacket = (holepunch_t *)(void *)&doomcom->data; #ifdef DEBUGFILE if (M_CheckParm("-debugfile")) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 6ff12ce3b..a0e798578 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -289,10 +289,10 @@ consvar_t cv_skin[MAXSPLITSCREENPLAYERS] = { // player's followers. Also saved. consvar_t cv_follower[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("follower", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower_OnChange), - CVAR_INIT ("follower2", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower2_OnChange), - CVAR_INIT ("follower3", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower3_OnChange), - CVAR_INIT ("follower4", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower4_OnChange) + CVAR_INIT ("follower", "None", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower_OnChange), + CVAR_INIT ("follower2", "None", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower2_OnChange), + CVAR_INIT ("follower3", "None", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower3_OnChange), + CVAR_INIT ("follower4", "None", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower4_OnChange) }; // player's follower colors... Also saved... @@ -453,8 +453,7 @@ consvar_t cv_scrambleonchange = CVAR_INIT ("scrambleonchange", "Off", CV_SAVE|CV consvar_t cv_itemfinder = CVAR_INIT ("itemfinder", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, ItemFinder_OnChange); // Scoring type options -static CV_PossibleValue_t overtime_cons_t[] = {{0, "No"}, {1, "Yes"}, {2, "Super"}, {0, NULL}}; -consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_NETVAR|CV_CHEAT, overtime_cons_t, NULL); +consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL); consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL); @@ -462,9 +461,9 @@ static CV_PossibleValue_t pointlimit_cons_t[] = {{1, "MIN"}, {MAXSCORE, "MAX"}, consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange); static CV_PossibleValue_t timelimit_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "None"}, {0, NULL}}; consvar_t cv_timelimit = CVAR_INIT ("timelimit", "None", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange); -static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, NULL}}; -consvar_t cv_numlaps = CVAR_INIT ("numlaps", "4", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t, NumLaps_OnChange); -static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, "Map default"}, {0, NULL}}; +static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}}; +consvar_t cv_numlaps = CVAR_INIT ("numlaps", "3", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_cons_t, NumLaps_OnChange); +static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, "Map default"}, {0, NULL}}; consvar_t cv_basenumlaps = CVAR_INIT ("basenumlaps", "Map default", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange); // Point and time limits for every gametype @@ -749,6 +748,10 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_dummyconsvar); +#ifdef USE_STUN + CV_RegisterVar(&cv_stunserver); +#endif + CV_RegisterVar(&cv_discordinvites); RegisterNetXCmd(XD_DISCORD, Got_DiscordInfo); } @@ -2854,13 +2857,6 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) memset(&luabanks, 0, sizeof(luabanks)); } - if (modeattacking) - { - SetPlayerSkinByNum(0, cv_chooseskin.value-1); - players[0].skincolor = skins[players[0].skin].prefcolor; - CV_StealthSetValue(&cv_playercolor[0], players[0].skincolor); - } - mapnumber = M_MapNumber(mapname[3], mapname[4]); LUAh_MapChange(mapnumber); @@ -3470,8 +3466,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) if (gametyperules & GTR_BUMPERS) // SRB2kart { players[playernum].marescore = 0; - if (K_IsPlayerWanted(&players[playernum])) - K_CalculateBattleWanted(); + K_CalculateBattleWanted(); } K_PlayerForfeit(playernum, true); @@ -4330,15 +4325,13 @@ static void TimeLimit_OnChange(void) if (cv_timelimit.value != 0) { - CONS_Printf(M_GetText("Levels will end after %d second%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003 - timelimitintics = cv_timelimit.value * TICRATE; + CONS_Printf(M_GetText("Rounds will end after %d minute%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003 + timelimitintics = cv_timelimit.value * (60*TICRATE); // Note the deliberate absence of any code preventing // pointlimit and timelimit from being set simultaneously. // Some people might like to use them together. It works. } - else if (netgame || multiplayer) - CONS_Printf(M_GetText("Time limit disabled\n")); #ifdef HAVE_DISCORDRPC DRPC_UpdatePresence(); diff --git a/src/d_netfil.c b/src/d_netfil.c index 5ca99508a..3307d00e9 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -138,6 +138,7 @@ static UINT32 curl_origfilesize; static UINT32 curl_origtotalfilesize; static char *curl_realname = NULL; fileneeded_t *curl_curfile = NULL; +HTTP_login *curl_logins; #endif luafiletransfer_t *luafiletransfers = NULL; @@ -476,10 +477,10 @@ INT32 CL_CheckFiles(void) for (i = 0; i < fileneedednum; i++) { - if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_FALLBACK) + if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK) downloadrequired = true; - if (fileneeded[i].status == FS_FOUND || fileneeded[i].status == FS_NOTFOUND) + if (fileneeded[i].status != FS_OPEN) filestoload++; if (fileneeded[i].status != FS_NOTCHECKED) //since we're running this over multiple tics now, its possible for us to come across files checked in previous tics @@ -1646,6 +1647,8 @@ int curlprogress_callback(void *clientp, double dltotal, double dlnow, double ul void CURLPrepareFile(const char* url, int dfilenum) { + HTTP_login *login; + #ifdef PARANOIA if (M_CheckParm("-nodownload")) I_Error("Attempted to download files in -nodownload mode"); @@ -1674,6 +1677,14 @@ void CURLPrepareFile(const char* url, int dfilenum) curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("SRB2Kart/v%d.%d", VERSION, SUBVERSION)); // Set user agent as some servers won't accept invalid user agents. + // Authenticate if the user so wishes + login = CURLGetLogin(url, NULL); + + if (login) + { + curl_easy_setopt(http_handle, CURLOPT_USERPWD, login->auth); + } + // Follow a redirect request, if sent by the server. curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1L); @@ -1775,4 +1786,27 @@ void CURLGetFile(void) curl_global_cleanup(); } } + +HTTP_login * +CURLGetLogin (const char *url, HTTP_login ***return_prev_next) +{ + HTTP_login * login; + HTTP_login ** prev_next; + + for ( + prev_next = &curl_logins; + ( login = (*prev_next)); + prev_next = &login->next + ){ + if (strcmp(login->url, url) == 0) + { + if (return_prev_next) + (*return_prev_next) = prev_next; + + return login; + } + } + + return NULL; +} #endif diff --git a/src/d_netfil.h b/src/d_netfil.h index 31f9bc507..20a49f498 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -72,6 +72,16 @@ extern UINT32 totalfilesrequestedsize; extern boolean curl_failedwebdownload; extern boolean curl_running; extern INT32 curl_transfers; + +typedef struct HTTP_login HTTP_login; + +extern struct HTTP_login +{ + char * url; + char * auth; + HTTP_login * next; +} +*curl_logins; #endif UINT8 *PutFileNeeded(UINT16 firstfile); @@ -151,6 +161,7 @@ size_t nameonlylength(const char *s); #ifdef HAVE_CURL void CURLPrepareFile(const char* url, int dfilenum); void CURLGetFile(void); +HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next); #endif #endif // __D_NETFIL__ diff --git a/src/d_player.h b/src/d_player.h index 52b0fac64..89679e9f1 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -449,6 +449,10 @@ typedef enum // QUICKLY GET RING TOTAL, INCLUDING RINGS CURRENTLY IN THE PICKUP ANIMATION #define RINGTOTAL(p) (p->rings + p->kartstuff[k_pickuprings]) +// CONSTANTS FOR TRICK PANELS +#define TRICKMOMZRAMP (30) +#define TRICKLAG (9) + //} // player_t struct for all respawn variables @@ -512,6 +516,7 @@ typedef struct player_s // player's ring count INT16 rings; + INT16 spheres; // Power ups. invinc and invis are tic counters. UINT16 powers[NUMPOWERS]; @@ -522,9 +527,18 @@ typedef struct player_s UINT32 distancetofinish; waypoint_t *nextwaypoint; respawnvars_t respawn; // Respawn info - tic_t airtime; // Keep track of how long you've been in the air - UINT8 trickpanel; // Trick panel state - tic_t trickdelay; + tic_t airtime; // Keep track of how long you've been in the air + + UINT8 trickpanel; // Trick panel state + boolean trickdelay; // Prevent tricks until control stick is neutral + fixed_t trickmomx; + fixed_t trickmomy; + fixed_t trickmomz; // Instead of stupid auxiliary variables let's... just make some ourselves. + + UINT8 bumpers; + INT16 karmadelay; + boolean eliminated; + // Bit flags. // See pflags_t, above. diff --git a/src/dehacked.c b/src/dehacked.c index b4fae59a3..b6bec105b 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1271,8 +1271,6 @@ static void readspriteframe(MYFILE *f, spriteinfo_t *sprinfo, UINT8 frame) sprinfo->pivot[frame].x = value; else if (fastcmp(word, "YPIVOT")) sprinfo->pivot[frame].y = value; - else if (fastcmp(word, "ROTAXIS")) - sprinfo->pivot[frame].rotaxis = value; else { f->curpos = lastline; @@ -6174,10 +6172,43 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FASTRING11", "S_FASTRING12", - // Blue Sphere for special stages + // Blue Sphere "S_BLUESPHERE", - "S_BLUESPHEREBONUS", - "S_BLUESPHERESPARK", + "S_BLUESPHERE_SPAWN", + + "S_BLUESPHERE_BOUNCE1", + "S_BLUESPHERE_BOUNCE2", + + "S_BLUESPHERE_BOUNCE3", + "S_BLUESPHERE_BOUNCE4", + + "S_BLUESPHERE_BOUNCE5", + "S_BLUESPHERE_BOUNCE6", + "S_BLUESPHERE_BOUNCE7", + "S_BLUESPHERE_BOUNCE8", + + "S_BLUESPHERE_BOUNCE9", + "S_BLUESPHERE_BOUNCE10", + "S_BLUESPHERE_BOUNCE11", + "S_BLUESPHERE_BOUNCE12", + + "S_BLUESPHERE_BOUNCE13", + "S_BLUESPHERE_BOUNCE14", + "S_BLUESPHERE_BOUNCE15", + "S_BLUESPHERE_BOUNCE16", + "S_BLUESPHERE_BOUNCE17", + "S_BLUESPHERE_BOUNCE18", + "S_BLUESPHERE_BOUNCE19", + "S_BLUESPHERE_BOUNCE20", + + "S_BLUESPHERE_BOUNCE21", + "S_BLUESPHERE_BOUNCE22", + "S_BLUESPHERE_BOUNCE23", + "S_BLUESPHERE_BOUNCE24", + "S_BLUESPHERE_BOUNCE25", + "S_BLUESPHERE_BOUNCE26", + "S_BLUESPHERE_BOUNCE27", + "S_BLUESPHERE_BOUNCE28", // Bomb Sphere "S_BOMBSPHERE1", @@ -6236,13 +6267,17 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_EMBLEM26", // Chaos Emeralds - "S_CEMG1", - "S_CEMG2", - "S_CEMG3", - "S_CEMG4", - "S_CEMG5", - "S_CEMG6", - "S_CEMG7", + "S_CHAOSEMERALD1", + "S_CHAOSEMERALD2", + "S_CHAOSEMERALD_UNDER", + + "S_EMERALDSPARK1", + "S_EMERALDSPARK2", + "S_EMERALDSPARK3", + "S_EMERALDSPARK4", + "S_EMERALDSPARK5", + "S_EMERALDSPARK6", + "S_EMERALDSPARK7", // Emerald hunt shards "S_SHRD1", @@ -8687,6 +8722,44 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_BATTLEBUMPER2", "S_BATTLEBUMPER3", + "S_BATTLEBUMPER_EXCRYSTALA1", + "S_BATTLEBUMPER_EXCRYSTALA2", + "S_BATTLEBUMPER_EXCRYSTALA3", + "S_BATTLEBUMPER_EXCRYSTALA4", + + "S_BATTLEBUMPER_EXCRYSTALB1", + "S_BATTLEBUMPER_EXCRYSTALB2", + "S_BATTLEBUMPER_EXCRYSTALB3", + "S_BATTLEBUMPER_EXCRYSTALB4", + + "S_BATTLEBUMPER_EXCRYSTALC1", + "S_BATTLEBUMPER_EXCRYSTALC2", + "S_BATTLEBUMPER_EXCRYSTALC3", + "S_BATTLEBUMPER_EXCRYSTALC4", + + "S_BATTLEBUMPER_EXSHELLA1", + "S_BATTLEBUMPER_EXSHELLA2", + + "S_BATTLEBUMPER_EXSHELLB1", + "S_BATTLEBUMPER_EXSHELLB2", + + "S_BATTLEBUMPER_EXSHELLC1", + "S_BATTLEBUMPER_EXSHELLC2", + + "S_BATTLEBUMPER_EXDEBRIS1", + "S_BATTLEBUMPER_EXDEBRIS2", + + "S_BATTLEBUMPER_EXBLAST1", + "S_BATTLEBUMPER_EXBLAST2", + "S_BATTLEBUMPER_EXBLAST3", + "S_BATTLEBUMPER_EXBLAST4", + "S_BATTLEBUMPER_EXBLAST5", + "S_BATTLEBUMPER_EXBLAST6", + "S_BATTLEBUMPER_EXBLAST7", + "S_BATTLEBUMPER_EXBLAST8", + "S_BATTLEBUMPER_EXBLAST9", + "S_BATTLEBUMPER_EXBLAST10", + // DEZ respawn laser "S_DEZLASER", "S_DEZLASER_TRAIL1", @@ -9287,9 +9360,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_TIREGREASE", - "S_OVERTIMEFOG", - "S_OVERTIMEORB", - "S_OVERTIMEBEAM", + "S_OVERTIME_BULB1", + "S_OVERTIME_BULB2", + "S_OVERTIME_LASER", + "S_OVERTIME_CENTER", "S_BATTLECAPSULE_SIDE1", "S_BATTLECAPSULE_SIDE2", @@ -9317,6 +9391,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_WATERTRAILUNDERLAY7", "S_WATERTRAILUNDERLAY8", + "S_SPINDASHDUST", + "S_SPINDASHWIND", + #ifdef SEENAMES "S_NAMECHECK", #endif @@ -9465,16 +9542,10 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_REDFLAG", // Red CTF Flag "MT_BLUEFLAG", // Blue CTF Flag "MT_EMBLEM", - "MT_EMERALD1", - "MT_EMERALD2", - "MT_EMERALD3", - "MT_EMERALD4", - "MT_EMERALD5", - "MT_EMERALD6", - "MT_EMERALD7", + "MT_EMERALD", + "MT_EMERALDSPARK", "MT_EMERHUNT", // Emerald Hunt "MT_EMERALDSPAWN", // Emerald spawner w/ delay - "MT_FLINGEMERALD", // Lost emerald // Springs and others "MT_FAN", @@ -10169,6 +10240,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_SINKTRAIL", "MT_BATTLEBUMPER", // Battle Mode bumper + "MT_BATTLEBUMPER_DEBRIS", + "MT_BATTLEBUMPER_BLAST", "MT_DEZLASER", @@ -10383,9 +10456,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_SPBDUST", "MT_TIREGREASE", - "MT_OVERTIMEFOG", - "MT_OVERTIMEORB", - "MT_OVERTIMEBEAM", + "MT_OVERTIME_PARTICLE", + "MT_OVERTIME_CENTER", "MT_BATTLECAPSULE", "MT_BATTLECAPSULE_PIECE", @@ -10397,6 +10469,11 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_WATERTRAIL", "MT_WATERTRAILUNDERLAY", + "MT_SPINDASHDUST", + "MT_SPINDASHWIND", + + "MT_PAPERITEMSPOT", + #ifdef SEENAMES "MT_NAMECHECK", #endif @@ -10717,67 +10794,67 @@ static const char *COLOR_ENUMS[] = { // Rejigged for Kart. // Special super colors // Super Sonic Yellow - "SUPER1", // SKINCOLOR_SUPER1 - "SUPER2", // SKINCOLOR_SUPER2, - "SUPER3", // SKINCOLOR_SUPER3, - "SUPER4", // SKINCOLOR_SUPER4, - "SUPER5", // SKINCOLOR_SUPER5, + "SUPERSILVER1", + "SUPERSILVER2", + "SUPERSILVER3", + "SUPERSILVER4", + "SUPERSILVER5", - // Super Tails Orange - "TSUPER1", // SKINCOLOR_TSUPER1, - "TSUPER2", // SKINCOLOR_TSUPER2, - "TSUPER3", // SKINCOLOR_TSUPER3, - "TSUPER4", // SKINCOLOR_TSUPER4, - "TSUPER5", // SKINCOLOR_TSUPER5, + "SUPERRED1", + "SUPERRED2", + "SUPERRED3", + "SUPERRED4", + "SUPERRED5", - // Super Knuckles Red - "KSUPER1", // SKINCOLOR_KSUPER1, - "KSUPER2", // SKINCOLOR_KSUPER2, - "KSUPER3", // SKINCOLOR_KSUPER3, - "KSUPER4", // SKINCOLOR_KSUPER4, - "KSUPER5", // SKINCOLOR_KSUPER5, + "SUPERORANGE1", + "SUPERORANGE2", + "SUPERORANGE3", + "SUPERORANGE4", + "SUPERORANGE5", - // Hyper Sonic Pink - "PSUPER1", // SKINCOLOR_PSUPER1, - "PSUPER2", // SKINCOLOR_PSUPER2, - "PSUPER3", // SKINCOLOR_PSUPER3, - "PSUPER4", // SKINCOLOR_PSUPER4, - "PSUPER5", // SKINCOLOR_PSUPER5, + "SUPERGOLD1", + "SUPERGOLD2", + "SUPERGOLD3", + "SUPERGOLD4", + "SUPERGOLD5", - // Hyper Sonic Blue - "BSUPER1", // SKINCOLOR_BSUPER1, - "BSUPER2", // SKINCOLOR_BSUPER2, - "BSUPER3", // SKINCOLOR_BSUPER3, - "BSUPER4", // SKINCOLOR_BSUPER4, - "BSUPER5", // SKINCOLOR_BSUPER5, + "SUPERPERIDOT1", + "SUPERPERIDOT2", + "SUPERPERIDOT3", + "SUPERPERIDOT4", + "SUPERPERIDOT5", - // Aqua Super - "ASUPER1", // SKINCOLOR_ASUPER1, - "ASUPER2", // SKINCOLOR_ASUPER2, - "ASUPER3", // SKINCOLOR_ASUPER3, - "ASUPER4", // SKINCOLOR_ASUPER4, - "ASUPER5", // SKINCOLOR_ASUPER5, + "SUPERSKY1", + "SUPERSKY2", + "SUPERSKY3", + "SUPERSKY4", + "SUPERSKY5", - // Hyper Sonic Green - "GSUPER1", // SKINCOLOR_GSUPER1, - "GSUPER2", // SKINCOLOR_GSUPER2, - "GSUPER3", // SKINCOLOR_GSUPER3, - "GSUPER4", // SKINCOLOR_GSUPER4, - "GSUPER5", // SKINCOLOR_GSUPER5, + "SUPERPURPLE1", + "SUPERPURPLE2", + "SUPERPURPLE3", + "SUPERPURPLE4", + "SUPERPURPLE5", - // Hyper Sonic White - "WSUPER1", // SKINCOLOR_WSUPER1, - "WSUPER2", // SKINCOLOR_WSUPER2, - "WSUPER3", // SKINCOLOR_WSUPER3, - "WSUPER4", // SKINCOLOR_WSUPER4, - "WSUPER5", // SKINCOLOR_WSUPER5, + "SUPERRUST1", + "SUPERRUST2", + "SUPERRUST3", + "SUPERRUST4", + "SUPERRUST5", - // Creamy Super (Shadow?) - "CSUPER1", // SKINCOLOR_CSUPER1, - "CSUPER2", // SKINCOLOR_CSUPER2, - "CSUPER3", // SKINCOLOR_CSUPER3, - "CSUPER4", // SKINCOLOR_CSUPER4, - "CSUPER5" // SKINCOLOR_CSUPER5, + "SUPERTAN1", + "SUPERTAN2", + "SUPERTAN3", + "SUPERTAN4", + "SUPERTAN5", + + "CHAOSEMERALD1", + "CHAOSEMERALD2", + "CHAOSEMERALD3", + "CHAOSEMERALD4", + "CHAOSEMERALD5", + "CHAOSEMERALD6", + "CHAOSEMERALD7" }; static const char *const POWERS_LIST[] = { @@ -11169,13 +11246,23 @@ struct { {"LF2_VISITNEEDED",LF2_VISITNEEDED}, // Emeralds - {"EMERALD1",EMERALD1}, - {"EMERALD2",EMERALD2}, - {"EMERALD3",EMERALD3}, - {"EMERALD4",EMERALD4}, - {"EMERALD5",EMERALD5}, - {"EMERALD6",EMERALD6}, - {"EMERALD7",EMERALD7}, + {"EMERALD_CHAOS1",EMERALD_CHAOS1}, + {"EMERALD_CHAOS2",EMERALD_CHAOS2}, + {"EMERALD_CHAOS3",EMERALD_CHAOS3}, + {"EMERALD_CHAOS4",EMERALD_CHAOS4}, + {"EMERALD_CHAOS5",EMERALD_CHAOS5}, + {"EMERALD_CHAOS6",EMERALD_CHAOS6}, + {"EMERALD_CHAOS7",EMERALD_CHAOS7}, + {"EMERALD_ALLCHAOS",EMERALD_ALLCHAOS}, + {"EMERALD_SUPER1",EMERALD_SUPER1}, + {"EMERALD_SUPER2",EMERALD_SUPER2}, + {"EMERALD_SUPER3",EMERALD_SUPER3}, + {"EMERALD_SUPER4",EMERALD_SUPER4}, + {"EMERALD_SUPER5",EMERALD_SUPER5}, + {"EMERALD_SUPER6",EMERALD_SUPER6}, + {"EMERALD_SUPER7",EMERALD_SUPER7}, + {"EMERALD_ALLSUPER",EMERALD_ALLSUPER}, + {"EMERALD_ALL",EMERALD_ALL}, // SKINCOLOR_ doesn't include these..! {"MAXSKINCOLORS",MAXSKINCOLORS}, @@ -11269,6 +11356,7 @@ struct { {"DMG_EXPLODE",DMG_EXPLODE}, {"DMG_SQUISH",DMG_SQUISH}, {"DMG_STING",DMG_STING}, + {"DMG_KARMA",DMG_KARMA}, //// Death types {"DMG_INSTAKILL",DMG_INSTAKILL}, {"DMG_DEATHPIT",DMG_DEATHPIT}, @@ -11480,11 +11568,6 @@ struct { {"DI_SOUTHEAST",DI_SOUTHEAST}, {"NUMDIRS",NUMDIRS}, - // Sprite rotation axis (rotaxis_t) - {"ROTAXIS_X",ROTAXIS_X}, - {"ROTAXIS_Y",ROTAXIS_Y}, - {"ROTAXIS_Z",ROTAXIS_Z}, - // Buttons (ticcmd_t) // SRB2kart {"BT_ACCELERATE",BT_ACCELERATE}, {"BT_DRIFT",BT_DRIFT}, diff --git a/src/discord.c b/src/discord.c index 42b4d91e4..652303f65 100644 --- a/src/discord.c +++ b/src/discord.c @@ -12,9 +12,7 @@ #ifdef HAVE_DISCORDRPC -#ifdef HAVE_CURL -#include -#endif // HAVE_CURL +#include #include "i_system.h" #include "d_clisrv.h" @@ -27,6 +25,8 @@ #include "mserv.h" // cv_advertise #include "z_zone.h" #include "byteptr.h" +#include "stun.h" +#include "i_tcp.h" // current_port #include "discord.h" #include "doomdef.h" @@ -45,16 +45,7 @@ struct discordInfo_s discordInfo; discordRequest_t *discordRequestList = NULL; -#ifdef HAVE_CURL -struct SelfIPbuffer -{ - CURL *curl; - char *pointer; - size_t length; -}; - static char self_ip[IP_SIZE]; -#endif // HAVE_CURL /*-------------------------------------------------- static char *DRPC_XORIPString(const char *input) @@ -335,39 +326,23 @@ void DRPC_Init(void) DRPC_UpdatePresence(); } -#ifdef HAVE_CURL /*-------------------------------------------------- - static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) + static void DRPC_GotServerIP(UINT32 address) - Writing function for use with curl. Only intended to be used with simple text. + Callback triggered by successful STUN response. Input Arguments:- - s - Data to write - size - Always 1. - n - Length of data - userdata - Passed in from CURLOPT_WRITEDATA, intended to be SelfIPbuffer + address - IPv4 address of this machine, in network byte order. Return:- - Number of bytes wrote in this pass. + None --------------------------------------------------*/ -static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) +static void DRPC_GotServerIP(UINT32 address) { - struct SelfIPbuffer *buffer; - size_t newlength; - - buffer = userdata; - - newlength = buffer->length + size*n; - buffer->pointer = realloc(buffer->pointer, newlength+1); - - memcpy(buffer->pointer + buffer->length, s, size*n); - - buffer->pointer[newlength] = '\0'; - buffer->length = newlength; - - return size*n; + const unsigned char * p = (const unsigned char *)&address; + sprintf(self_ip, "%u.%u.%u.%u:%u", p[0], p[1], p[2], p[3], current_port); + DRPC_UpdatePresence(); } -#endif // HAVE_CURL /*-------------------------------------------------- static const char *DRPC_GetServerIP(void) @@ -387,64 +362,21 @@ static const char *DRPC_GetServerIP(void) { // We're not the server, so we could successfully get the IP! // No need to do anything else :) - return address; + sprintf(self_ip, "%s:%u", address, current_port); + return self_ip; } } -#ifdef HAVE_CURL - // This is a little bit goofy, but - // there's practically no good way to get your own public IP address, - // so we've gotta break out curl for this :V - if (!self_ip[0]) - { - CURL *curl; - - curl_global_init(CURL_GLOBAL_ALL); - curl = curl_easy_init(); - - if (curl) - { - // The API to get your public IP address from. - // Picked because it's stupid simple and it's been up for a long time. - const char *api = "http://ip4only.me/api/"; - - struct SelfIPbuffer buffer; - CURLcode success; - - buffer.length = 0; - buffer.pointer = malloc(buffer.length+1); - buffer.pointer[0] = '\0'; - - curl_easy_setopt(curl, CURLOPT_URL, api); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DRPC_WriteServerIP); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); - - success = curl_easy_perform(curl); - - if (success == CURLE_OK) - { - char *tmp; - tmp = strtok(buffer.pointer, ","); - - if (!strcmp(tmp, "IPv4")) // ensure correct type of IP - { - tmp = strtok(NULL, ","); - strncpy(self_ip, tmp, IP_SIZE); // Yay, we have the IP :) - } - } - - free(buffer.pointer); - curl_easy_cleanup(curl); - } - - curl_global_cleanup(); - } - if (self_ip[0]) + { return self_ip; + } else -#endif // HAVE_CURL - return NULL; // Could not get your IP for whatever reason, so we cannot do Discord invites + { + // There happens to be a good way to get it after all! :D + STUN_bind(DRPC_GotServerIP); + return NULL; + } } /*-------------------------------------------------- @@ -510,19 +442,6 @@ void DRPC_UpdatePresence(void) // Server info if (netgame) { - if (cv_advertise.value) - { - discordPresence.state = "Public"; - } - else - { - discordPresence.state = "Private"; - } - - discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! - discordPresence.partySize = D_NumPlayers(); // Players in server - discordPresence.partyMax = discordInfo.maxPlayers; // Max players - if (DRPC_InvitesAreAllowed() == true) { const char *join; @@ -536,7 +455,24 @@ void DRPC_UpdatePresence(void) joinSecretSet = true; } + else + { + return; + } } + + if (cv_advertise.value) + { + discordPresence.state = "Public"; + } + else + { + discordPresence.state = "Private"; + } + + discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field! + discordPresence.partySize = D_NumPlayers(); // Players in server + discordPresence.partyMax = discordInfo.maxPlayers; // Max players } else { diff --git a/src/doomdef.h b/src/doomdef.h index 9c4664135..c51cf4a5f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -386,6 +386,14 @@ typedef enum SKINCOLOR_SUPERTAN4, SKINCOLOR_SUPERTAN5, + SKINCOLOR_CHAOSEMERALD1, + SKINCOLOR_CHAOSEMERALD2, + SKINCOLOR_CHAOSEMERALD3, + SKINCOLOR_CHAOSEMERALD4, + SKINCOLOR_CHAOSEMERALD5, + SKINCOLOR_CHAOSEMERALD6, + SKINCOLOR_CHAOSEMERALD7, + SKINCOLOR_FIRSTFREESLOT, SKINCOLOR_LASTFREESLOT = SKINCOLOR_FIRSTFREESLOT + NUMCOLORFREESLOTS - 1, @@ -661,10 +669,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Render flats on walls #define WALLFLATS -/// - SRB2Kart options - -/// Camera always has noclip. -#define NOCLIPCAM - /// Divide volume of music and sounds by this much (loudest sounds on earth) #define VOLUME_DIVIDER 4 #define USER_VOLUME_SCALE 2 @@ -682,4 +686,11 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; #undef UPDATE_ALERT #endif +/// - SRB2Kart options - +/// Camera always has noclip. +#define NOCLIPCAM + +/// Other karma comeback modes +//#define OTHERKARMAMODES + #endif // __DOOMDEF__ diff --git a/src/doomstat.h b/src/doomstat.h index 629e5cead..2df6caeb5 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -456,30 +456,31 @@ enum GameTypeRules { // Race rules GTR_CIRCUIT = 1, // Enables the finish line, laps, and the waypoint system. - GTR_RINGS = 1<<1, // Rings will be spawned in this mode. (Don't get too cheeky, ring sting is still enabled :]) GTR_BOTS = 1<<2, // Allows bots in this gametype. Combine with BotTiccmd hooks to make bots support your gametype. // Battle gametype rules GTR_BUMPERS = 1<<3, // Enables the bumper health system - GTR_WANTED = 1<<4, // Enables the wanted anti-camping system - GTR_KARMA = 1<<5, // Enables the Karma system if you're out of bumpers - GTR_ITEMARROWS = 1<<6, // Show item box arrows above players - GTR_CAPSULES = 1<<7, // Enables the wanted anti-camping system - GTR_BATTLESTARTS = 1<<8, // Use Battle Mode start positions. + GTR_SPHERES = 1<<4, // Replaces rings with blue spheres + GTR_PAPERITEMS = 1<<5, // Replaces item boxes with paper item spawners + GTR_WANTED = 1<<6, // Enables the wanted anti-camping system + GTR_KARMA = 1<<7, // Enables the Karma system if you're out of bumpers + GTR_ITEMARROWS = 1<<8, // Show item box arrows above players + GTR_CAPSULES = 1<<9, // Enables the wanted anti-camping system + GTR_BATTLESTARTS = 1<<10, // Use Battle Mode start positions. - GTR_POINTLIMIT = 1<<9, // Reaching point limit ends the round - GTR_TIMELIMIT = 1<<10, // Reaching time limit ends the round - GTR_OVERTIME = 1<<11, // Allow overtime behavior + GTR_POINTLIMIT = 1<<11, // Reaching point limit ends the round + GTR_TIMELIMIT = 1<<12, // Reaching time limit ends the round + GTR_OVERTIME = 1<<13, // Allow overtime behavior // Custom gametype rules - GTR_TEAMS = 1<<12, // Teams are forced on - GTR_NOTEAMS = 1<<13, // Teams are forced off - GTR_TEAMSTARTS = 1<<14, // Use team-based start positions + GTR_TEAMS = 1<<14, // Teams are forced on + GTR_NOTEAMS = 1<<15, // Teams are forced off + GTR_TEAMSTARTS = 1<<16, // Use team-based start positions // Grand Prix rules - GTR_CAMPAIGN = 1<<15, // Handles cup-based progression - GTR_LIVES = 1<<16, // Lives system, players are forced to spectate during Game Over. - GTR_SPECIALBOTS = 1<<17, // Bot difficulty gets stronger between rounds, and the rival system is enabled. + GTR_CAMPAIGN = 1<<17, // Handles cup-based progression + GTR_LIVES = 1<<18, // Lives system, players are forced to spectate during Game Over. + GTR_SPECIALBOTS = 1<<19, // Bot difficulty gets stronger between rounds, and the rival system is enabled. // free: to and including 1<<31 }; @@ -521,15 +522,34 @@ extern UINT32 matchesplayed; extern UINT8 stagefailed; // Emeralds stored as bits to throw savegame hackers off. +typedef enum +{ + EMERALD_CHAOS1 = 1, + EMERALD_CHAOS2 = 1<<1, + EMERALD_CHAOS3 = 1<<2, + EMERALD_CHAOS4 = 1<<3, + EMERALD_CHAOS5 = 1<<4, + EMERALD_CHAOS6 = 1<<5, + EMERALD_CHAOS7 = 1<<6, + EMERALD_ALLCHAOS = EMERALD_CHAOS1|EMERALD_CHAOS2|EMERALD_CHAOS3|EMERALD_CHAOS4|EMERALD_CHAOS5|EMERALD_CHAOS6|EMERALD_CHAOS7, + + EMERALD_SUPER1 = 1<<7, + EMERALD_SUPER2 = 1<<8, + EMERALD_SUPER3 = 1<<9, + EMERALD_SUPER4 = 1<<10, + EMERALD_SUPER5 = 1<<11, + EMERALD_SUPER6 = 1<<12, + EMERALD_SUPER7 = 1<<13, + EMERALD_ALLSUPER = EMERALD_SUPER1|EMERALD_SUPER2|EMERALD_SUPER3|EMERALD_SUPER4|EMERALD_SUPER5|EMERALD_SUPER6|EMERALD_SUPER7, + + EMERALD_ALL = EMERALD_ALLCHAOS|EMERALD_ALLSUPER +} emeraldflags_t; + extern UINT16 emeralds; -#define EMERALD1 1 -#define EMERALD2 2 -#define EMERALD3 4 -#define EMERALD4 8 -#define EMERALD5 16 -#define EMERALD6 32 -#define EMERALD7 64 -#define ALL7EMERALDS(v) ((v & (EMERALD1|EMERALD2|EMERALD3|EMERALD4|EMERALD5|EMERALD6|EMERALD7)) == (EMERALD1|EMERALD2|EMERALD3|EMERALD4|EMERALD5|EMERALD6|EMERALD7)) + +#define ALLCHAOSEMERALDS(v) ((v & EMERALD_ALLCHAOS) == EMERALD_ALLCHAOS) +#define ALLSUPEREMERALDS(v) ((v & EMERALD_ALLSUPER) == EMERALD_ALLSUPER) +#define ALLEMERALDS(v) ((v & EMERALD_ALL) == EMERALD_ALL) #define NUM_LUABANKS 16 // please only make this number go up between versions, never down. you'll break saves otherwise. also, must fit in UINT8 extern INT32 luabanks[NUM_LUABANKS]; diff --git a/src/f_finale.c b/src/f_finale.c index 4355cf231..0d290330c 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -889,7 +889,7 @@ void F_StartGameEvaluation(void) // Just in case they're open ... somehow M_ClearMenus(true); - goodending = (ALL7EMERALDS(emeralds)); + goodending = (ALLCHAOSEMERALDS(emeralds)); gameaction = ga_nothing; paused = false; @@ -1154,7 +1154,7 @@ static void F_CacheEnding(void) endescp[4] = W_CachePatchName("ENDESCP4", PU_PATCH); // so we only need to check once - if ((goodending = ALL7EMERALDS(emeralds))) + if ((goodending = ALLCHAOSEMERALDS(emeralds))) { endfwrk[0] = W_CachePatchName("ENDFWRK3", PU_PATCH); endfwrk[1] = W_CachePatchName("ENDFWRK4", PU_PATCH); diff --git a/src/g_demo.c b/src/g_demo.c index 7c2f9f25c..693a4f095 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -104,7 +104,7 @@ demoghost *ghosts = NULL; // DEMO RECORDING // -#define DEMOVERSION 0x0004 +#define DEMOVERSION 0x0007 #define DEMOHEADER "\xF0" "KartReplay" "\x0F" #define DF_GHOST 0x01 // This demo contains ghost data too! @@ -112,7 +112,7 @@ demoghost *ghosts = NULL; #define DF_BREAKTHECAPSULES 0x04 // This demo is from Break the Capsules and contains its final completion time! #define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ??? -#define DF_LUAVARS 0x20 // this demo contains extra lua vars; this is mostly used for backwards compability +#define DF_LUAVARS 0x20 // this demo contains extra lua vars #define DF_ATTACKSHIFT 1 #define DF_ENCORE 0x40 @@ -419,7 +419,10 @@ void G_WriteDemoExtraData(void) { // write follower memset(name, 0, 16); - strncpy(name, followers[players[i].followerskin].skinname, 16); + if (players[i].followerskin == -1) + strncpy(name, "None", 16); + else + strncpy(name, followers[players[i].followerskin].skinname, 16); M_Memcpy(demo_p, name, 16); demo_p += 16; @@ -619,13 +622,21 @@ void G_WriteAllGhostTics(void) counter++; - if (counter % cv_netdemosyncquality.value != 0) // Only write 1 in this many ghost datas per tic to cut down on multiplayer replay size. + if (multiplayer && ((counter % cv_netdemosyncquality.value) != 0)) // Only write 1 in this many ghost datas per tic to cut down on multiplayer replay size. continue; WRITEUINT8(demo_p, i); G_WriteGhostTic(players[i].mo, i); } WRITEUINT8(demo_p, 0xFF); + + // attention here for the ticcmd size! + // latest demos with mouse aiming byte in ticcmd + if (demo_p >= demoend - (13 + 9 + 9)) + { + G_CheckDemoStatus(); // no more space + return; + } } void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) @@ -722,26 +733,22 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) ghostext[playernum].flags |= EZT_SPRITE; } + if (ghost->player && ( + ghostext[playernum].kartitem != ghost->player->kartstuff[k_itemtype] || + ghostext[playernum].kartamount != ghost->player->kartstuff[k_itemamount] || + ghostext[playernum].kartbumpers != ghost->player->kartstuff[k_bumper] + )) + { + ghostext[playernum].flags |= EZT_KART; + ghostext[playernum].kartitem = ghost->player->kartstuff[k_itemtype]; + ghostext[playernum].kartamount = ghost->player->kartstuff[k_itemamount]; + ghostext[playernum].kartbumpers = ghost->player->kartstuff[k_bumper]; + } + if (ghostext[playernum].flags) { ziptic |= GZT_EXTRA; - if (ghost->player) - { - if ( - ghostext[playernum].kartitem != ghost->player->kartstuff[k_itemtype] || - ghostext[playernum].kartamount != ghost->player->kartstuff[k_itemamount] || - ghostext[playernum].kartbumpers != ghost->player->kartstuff[k_bumper] - ) - { - ghostext[playernum].flags |= EZT_KART; - ghostext[playernum].kartitem = ghost->player->kartstuff[k_itemtype]; - ghostext[playernum].kartamount = ghost->player->kartstuff[k_itemamount]; - ghostext[playernum].kartbumpers = ghost->player->kartstuff[k_bumper]; - - } - } - if (ghostext[playernum].color == ghostext[playernum].lastcolor) ghostext[playernum].flags &= ~EZT_COLOR; if (ghostext[playernum].scale == ghostext[playernum].lastscale) @@ -836,14 +843,6 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) oldghost[playernum].flags2 &= ~MF2_AMBUSH; *ziptic_p = ziptic; - - // attention here for the ticcmd size! - // latest demos with mouse aiming byte in ticcmd - if (demo_p >= demoend - (13 + 9 + 9)) - { - G_CheckDemoStatus(); // no more space - return; - } } void G_ConsAllGhostTics(void) @@ -1067,16 +1066,18 @@ void G_GhostTicker(void) if (ziptic & DXD_FOLLOWER) g->p += 32; // ok (32 because there's both the skin and the colour) if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING) - I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + I_Error("Ghost is not a record attack ghost PLAYSTATE"); //@TODO lmao don't blow up like this } else if (ziptic == DW_RNG) g->p += 4; // RNG seed else - I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + I_Error("Ghost is not a record attack ghost DXD"); //@TODO lmao don't blow up like this ziptic = READUINT8(g->p); } + ziptic = READUINT8(g->p); + if (ziptic & ZT_FWD) g->p++; if (ziptic & ZT_TURNING) @@ -1086,9 +1087,9 @@ void G_GhostTicker(void) if (ziptic & ZT_AIMING) g->p += 2; if (ziptic & ZT_LATENCY) - g->p += 1; + g->p++; if (ziptic & ZT_FLAGS) - g->p += 1; + g->p++; // Grab ghost data. ziptic = READUINT8(g->p); @@ -1096,7 +1097,7 @@ void G_GhostTicker(void) if (ziptic == 0xFF) goto skippedghosttic; // Didn't write ghost info this frame else if (ziptic != 0) - I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + I_Error("Ghost is not a record attack ghost ZIPTIC"); //@TODO lmao don't blow up like this ziptic = READUINT8(g->p); if (ziptic & GZT_XYZ) @@ -1109,17 +1110,17 @@ void G_GhostTicker(void) { if (ziptic & GZT_MOMXY) { - g->oldmo.momx = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p); - g->oldmo.momy = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p); + g->oldmo.momx = READFIXED(g->p); + g->oldmo.momy = READFIXED(g->p); } if (ziptic & GZT_MOMZ) - g->oldmo.momz = (g->version < 0x000e) ? READINT16(g->p)<<8 : READFIXED(g->p); + g->oldmo.momz = READFIXED(g->p); g->oldmo.x += g->oldmo.momx; g->oldmo.y += g->oldmo.momy; g->oldmo.z += g->oldmo.momz; } if (ziptic & GZT_ANGLE) - g->mo->angle = READUINT8(g->p)<<24; + g->oldmo.angle = READUINT8(g->p)<<24; if (ziptic & GZT_FRAME) g->oldmo.frame = READUINT8(g->p); if (ziptic & GZT_SPR2) @@ -1205,30 +1206,6 @@ void G_GhostTicker(void) g->p += 12; // kartitem, kartamount, kartbumpers } - if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here. - I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this - -skippedghosttic: - // Tick ghost colors (Super and Mario Invincibility flashing) - switch(g->color) - { - case GHC_SUPER: // Super (P_DoSuperStuff) - if (g->mo->skin) - { - skin_t *skin = (skin_t *)g->mo->skin; - g->mo->color = skin->supercolor; - } - else - g->mo->color = SKINCOLOR_SUPERGOLD1; - g->mo->color += abs( ( (signed)( (unsigned)leveltime >> 1 ) % 9) - 4); - break; - case GHC_INVINCIBLE: // Mario invincibility (P_CheckInvincibilityTimer) - g->mo->color = (UINT16)(SKINCOLOR_RUBY + (leveltime % (FIRSTSUPERCOLOR - SKINCOLOR_RUBY))); // Passes through all saturated colours - break; - default: - break; - } - #define follow g->mo->tracer if (ziptic & GZT_FOLLOW) { // Even more... @@ -1294,6 +1271,31 @@ skippedghosttic: P_RemoveMobj(follow); P_SetTarget(&follow, NULL); } + +skippedghosttic: + // Tick ghost colors (Super and Mario Invincibility flashing) + switch(g->color) + { + case GHC_SUPER: // Super (P_DoSuperStuff) + if (g->mo->skin) + { + skin_t *skin = (skin_t *)g->mo->skin; + g->mo->color = skin->supercolor; + } + else + g->mo->color = SKINCOLOR_SUPERGOLD1; + g->mo->color += abs( ( (signed)( (unsigned)leveltime >> 1 ) % 9) - 4); + break; + case GHC_INVINCIBLE: // Mario invincibility (P_CheckInvincibilityTimer) + g->mo->color = (UINT16)(SKINCOLOR_RUBY + (leveltime % (FIRSTSUPERCOLOR - SKINCOLOR_RUBY))); // Passes through all saturated colours + break; + default: + break; + } + + if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here. + I_Error("Ghost is not a record attack ghost GHOSTEND"); //@TODO lmao don't blow up like this + // Demo ends after ghost data. if (*g->p == DEMOMARKER) { @@ -1314,6 +1316,7 @@ skippedghosttic: Z_Free(g); continue; } + p = g; #undef follow } @@ -1436,8 +1439,11 @@ void G_PreviewRewind(tic_t previewtime) players[i].drawangle = info->playerinfo[i].player.drawangle + FixedMul((INT32) (next_info->playerinfo[i].player.drawangle - info->playerinfo[i].player.drawangle), tweenvalue); players[i].mo->sprite = info->playerinfo[i].mobj.sprite; + players[i].mo->sprite2 = info->playerinfo[i].mobj.sprite2; players[i].mo->frame = info->playerinfo[i].mobj.frame; + players[i].mo->hitlag = info->playerinfo[i].mobj.hitlag; + players[i].realtime = info->playerinfo[i].player.realtime; for (j = 0; j < NUMKARTSTUFF; j++) players[i].kartstuff[j] = info->playerinfo[i].player.kartstuff[j]; @@ -1462,7 +1468,7 @@ void G_ConfirmRewind(tic_t rewindtime) if (rewindtime <= starttime) { - demo.rewinding = false; + demo.rewinding = true; // this doesn't APPEAR to cause any misery, and it allows us to prevent running all the wipes again G_DoPlayDemo(NULL); // Restart the current demo } else @@ -1899,7 +1905,8 @@ void G_BeginRecording(void) if (encoremode) demoflags |= DF_ENCORE; - demoflags |= DF_LUAVARS; + if (multiplayer) + demoflags |= DF_LUAVARS; // Setup header. M_Memcpy(demo_p, DEMOHEADER, 12); demo_p += 12; @@ -2033,9 +2040,8 @@ void G_BeginRecording(void) WRITEUINT8(demo_p, 0xFF); // Denote the end of the player listing // player lua vars, always saved even if empty - LUA_Archive(&demo_p); - - WRITEUINT32(demo_p,P_GetInitSeed()); + if (demoflags & DF_LUAVARS) + LUA_Archive(&demo_p); memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldghost,0,sizeof(oldghost)); @@ -2712,7 +2718,6 @@ void G_DoPlayDemo(char *defdemoname) demo.version = READUINT16(demo_p); switch(demo.version) { - case 0x000d: case DEMOVERSION: // latest always supported break; // too old, cannot support. @@ -3101,7 +3106,6 @@ void G_AddGhost(char *defdemoname) ghostversion = READUINT16(p); switch(ghostversion) { - case 0x000d: case DEMOVERSION: // latest always supported break; // too old, cannot support. @@ -3145,6 +3149,14 @@ void G_AddGhost(char *defdemoname) return; } + if (flags & DF_LUAVARS) // can't be arsed to add support for grinding away ported lua material + { + CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Replay data contains luavars, cannot continue.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } + p++; // gametype G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts. @@ -3211,6 +3223,8 @@ void G_AddGhost(char *defdemoname) kartspeed = READUINT8(p); kartweight = READUINT8(p); + p += 4; // followitem (maybe change later) + if (READUINT8(p) != 0xFF) { CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); diff --git a/src/g_game.c b/src/g_game.c index 0151783e9..b0fcf7bf3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1252,7 +1252,7 @@ void G_StartTitleCard(void) { // The title card has been disabled for this map. // Oh well. - if (!G_IsTitleCardAvailable()) + if (!G_IsTitleCardAvailable() || demo.rewinding) { WipeStageTitle = false; return; @@ -1575,7 +1575,7 @@ boolean G_CouldView(INT32 playernum) // I don't know if we want this actually, but I'll humor the suggestion anyway if ((gametyperules & GTR_BUMPERS) && !demo.playback) { - if (player->kartstuff[k_bumper] <= 0) + if (player->bumpers <= 0) return false; } @@ -2056,6 +2056,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT8 botdifficulty; INT16 rings; + INT16 spheres; angle_t playerangleturn; UINT8 botdiffincrease; @@ -2071,9 +2072,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT32 roulettetype; INT32 growshrinktimer; INT32 bumper; - INT32 comebackpoints; INT32 wanted; boolean songcredit = false; + boolean eliminated; score = players[player].score; marescore = players[player].marescore; @@ -2139,8 +2140,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) itemamount = 0; growshrinktimer = 0; bumper = ((gametyperules & GTR_BUMPERS) ? K_StartingBumperCount() : 0); - rings = ((gametyperules & GTR_RINGS) ? 5 : 0); - comebackpoints = 0; + rings = ((gametyperules & GTR_SPHERES) ? 0 : 5); + spheres = 0; + eliminated = false; wanted = 0; } else @@ -2165,9 +2167,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) else growshrinktimer = 0; - bumper = players[player].kartstuff[k_bumper]; + bumper = players[player].bumpers; rings = players[player].rings; - comebackpoints = players[player].kartstuff[k_comebackpoints]; + spheres = players[player].spheres; + eliminated = players[player].eliminated; wanted = players[player].kartstuff[k_wanted]; } @@ -2215,6 +2218,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->bot = bot; p->botvars.difficulty = botdifficulty; p->rings = rings; + p->spheres = spheres; p->botvars.diffincrease = botdiffincrease; p->botvars.rival = botrival; p->xtralife = xtralife; @@ -2225,9 +2229,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->kartstuff[k_itemtype] = itemtype; p->kartstuff[k_itemamount] = itemamount; p->kartstuff[k_growshrinktimer] = growshrinktimer; - p->kartstuff[k_bumper] = bumper; - p->kartstuff[k_comebackpoints] = comebackpoints; - p->kartstuff[k_comebacktimer] = comebacktime; + p->bumpers = bumper; + p->karmadelay = comebacktime; + p->eliminated = eliminated; p->kartstuff[k_wanted] = wanted; p->kartstuff[k_eggmanblame] = -1; p->kartstuff[k_lastdraft] = -1; @@ -2787,9 +2791,9 @@ const char *Gametype_ConstantNames[NUMGAMETYPES] = UINT32 gametypedefaultrules[NUMGAMETYPES] = { // Race - GTR_CIRCUIT|GTR_RINGS|GTR_BOTS, + GTR_CIRCUIT|GTR_BOTS, // Battle - GTR_BUMPERS|GTR_WANTED|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME + GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_WANTED|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME }; // diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index f9e6f0c11..9cb48620e 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -101,21 +101,13 @@ typedef struct //Hurdler: Transform (coords + angles) //BP: transform order : scale(rotation_x(rotation_y(translation(v)))) -// Kart features -//#define USE_FTRANSFORM_ANGLEZ -//#define USE_FTRANSFORM_MIRROR - // Vanilla features #define USE_MODEL_NEXTFRAME typedef struct { FLOAT x,y,z; // position -#ifdef USE_FTRANSFORM_ANGLEZ FLOAT anglex,angley,anglez; // aimingangle / viewangle -#else - FLOAT anglex,angley; // aimingangle / viewangle -#endif FLOAT scalex,scaley,scalez; FLOAT fovxangle, fovyangle; UINT8 splitscreen; @@ -123,13 +115,10 @@ typedef struct boolean shearing; // 14042019 angle_t viewaiming; // 17052019 boolean roll; - SINT8 rollflip; FLOAT rollangle; // done to not override USE_FTRANSFORM_ANGLEZ - UINT8 rotaxis; FLOAT centerx, centery; -#ifdef USE_FTRANSFORM_MIRROR + FLOAT rollx, rollz; boolean mirror; // SRB2Kart: Encore Mode -#endif } FTransform; // Transformed vector, as passed to HWR API diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index 1480ee839..613005421 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -50,7 +50,7 @@ EXPORT void HWRAPI(ClearMipMapCache) (void); EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value); //Hurdler: added for new development -EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface); +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface, FBITFIELD blendmode); EXPORT void HWRAPI(CreateModelVBOs) (model_t *model); EXPORT void HWRAPI(SetTransform) (FTransform *stransform); EXPORT INT32 HWRAPI(GetTextureUsed) (void); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index bc7a673b7..e44ac6bc6 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -3617,9 +3617,9 @@ static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float v static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) { - const fixed_t thingxpos = thing->x + thing->sprxoff; - const fixed_t thingypos = thing->y + thing->spryoff; - const fixed_t thingzpos = thing->z + thing->sprzoff; + fixed_t thingxpos = thing->x + thing->sprxoff; + fixed_t thingypos = thing->y + thing->spryoff; + fixed_t thingzpos = thing->z + thing->sprzoff; GLPatch_t *gpatch; FOutVector shadowVerts[4]; @@ -3637,6 +3637,21 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) fixed_t slopez; pslope_t *groundslope; + // hitlag vibrating + if (thing->hitlag > 0) + { + fixed_t mul = thing->hitlag * (FRACUNIT / 10); + + if (leveltime & 1) + { + mul = -mul; + } + + thingxpos += FixedMul(thing->momx, mul); + thingypos += FixedMul(thing->momy, mul); + thingzpos += FixedMul(thing->momz, mul); + } + groundz = R_GetShadowZ(thing, &groundslope); floordiff = abs((flip < 0 ? thing->height : 0) + thingzpos - groundz); @@ -4263,20 +4278,10 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr) UINT8 brightmode = 0; extracolormap_t *colormap = sector->extra_colormap; - if (spr->mobj->drawflags & MFD_BRIGHTMASK) - { - if (spr->mobj->drawflags & MFD_FULLBRIGHT) - brightmode = 1; - else if (spr->mobj->drawflags & MFD_SEMIBRIGHT) - brightmode = 2; - } - else - { - if (spr->mobj->frame & FF_FULLBRIGHT) - brightmode = 1; - else if (spr->mobj->frame & FF_SEMIBRIGHT) - brightmode = 2; - } + if (spr->mobj->frame & FF_FULLBRIGHT) + brightmode = 1; + else if (spr->mobj->frame & FF_SEMIBRIGHT) + brightmode = 2; if (sector->numlights) { @@ -4305,9 +4310,7 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr) HWR_Lighting(&Surf, lightlevel, colormap); } - if (spr->mobj->drawflags & MFD_TRANSMASK) - blend = HWR_TranstableToAlpha((spr->mobj->drawflags & MFD_TRANSMASK)>>MFD_TRANSSHIFT, &Surf); - else if (spr->mobj->frame & FF_TRANSMASK) + if (spr->mobj->frame & FF_TRANSMASK) blend = HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &Surf); else { @@ -4867,9 +4870,9 @@ static void HWR_AddSprites(sector_t *sec) // BP why not use xtoviexangle/viewangletox like in bsp ?.... static void HWR_ProjectSprite(mobj_t *thing) { - const fixed_t thingxpos = thing->x + thing->sprxoff; - const fixed_t thingypos = thing->y + thing->spryoff; - const fixed_t thingzpos = thing->z + thing->sprzoff; + fixed_t thingxpos = thing->x + thing->sprxoff; + fixed_t thingypos = thing->y + thing->spryoff; + fixed_t thingzpos = thing->z + thing->sprzoff; gl_vissprite_t *vis; float tr_x, tr_y; @@ -4902,11 +4905,27 @@ static void HWR_ProjectSprite(mobj_t *thing) #ifdef ROTSPRITE patch_t *rotsprite = NULL; INT32 rollangle = 0; + angle_t spriterotangle = 0; #endif if (!thing) return; + // hitlag vibrating + if (thing->hitlag > 0) + { + fixed_t mul = thing->hitlag * (FRACUNIT / 10); + + if (leveltime & 1) + { + mul = -mul; + } + + thingxpos += FixedMul(thing->momx, mul); + thingypos += FixedMul(thing->momy, mul); + thingzpos += FixedMul(thing->momz, mul); + } + dispoffset = thing->info->dispoffset; this_scale = FIXED_TO_FLOAT(thing->scale); @@ -5025,9 +5044,11 @@ static void HWR_ProjectSprite(mobj_t *thing) spr_topoffset = spritecachedinfo[lumpoff].topoffset; #ifdef ROTSPRITE - if (thing->rollangle) + spriterotangle = R_SpriteRotationAngle(thing); + + if (spriterotangle != 0) { - rollangle = R_GetRollAngle(thing->rollangle); + rollangle = R_GetRollAngle(spriterotangle); if (!(sprframe->rotsprite.cached & (1<sprite, (thing->frame & FF_FRAMEMASK), sprinfo, sprframe, rot, flip); rotsprite = sprframe->rotsprite.patch[rot][rollangle]; @@ -5508,6 +5529,8 @@ static void HWR_DrawSkyBackground(player_t *player) fixed_t rol = AngleFixed(player->viewrollangle); dometransform.rollangle = FIXED_TO_FLOAT(rol); dometransform.roll = true; + dometransform.rollx = 1.0f; + dometransform.rollz = 0.0f; } dometransform.splitscreen = r_splitscreen; @@ -5786,6 +5809,8 @@ void HWR_RenderSkyboxView(player_t *player) fixed_t rol = AngleFixed(player->viewrollangle); atransform.rollangle = FIXED_TO_FLOAT(rol); atransform.roll = true; + atransform.rollx = 1.0f; + atransform.rollz = 0.0f; } atransform.splitscreen = r_splitscreen; @@ -5987,6 +6012,8 @@ void HWR_RenderPlayerView(void) fixed_t rol = AngleFixed(player->viewrollangle); atransform.rollangle = FIXED_TO_FLOAT(rol); atransform.roll = true; + atransform.rollx = 1.0f; + atransform.rollz = 0.0f; } atransform.splitscreen = r_splitscreen; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 9896eb458..54c74d44c 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1340,9 +1340,9 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) // Look at HWR_ProjectSprite for more { - const fixed_t thingxpos = spr->mobj->x + spr->mobj->sprxoff; - const fixed_t thingypos = spr->mobj->y + spr->mobj->spryoff; - const fixed_t thingzpos = spr->mobj->z + spr->mobj->sprzoff; + fixed_t thingxpos = spr->mobj->x + spr->mobj->sprxoff; + fixed_t thingypos = spr->mobj->y + spr->mobj->spryoff; + fixed_t thingzpos = spr->mobj->z + spr->mobj->sprzoff; GLPatch_t *gpatch; INT32 durs = spr->mobj->state->tics; @@ -1353,22 +1353,41 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) const UINT8 hflip = (UINT8)(!(spr->mobj->mirrored) != !(spr->mobj->frame & FF_HORIZONTALFLIP)); spritedef_t *sprdef; spriteframe_t *sprframe; - spriteinfo_t *sprinfo; - angle_t ang; INT32 mod; float finalscale; + FBITFIELD blendmode = PF_Masked; + + // hitlag vibrating + if (spr->mobj->hitlag > 0) + { + fixed_t mul = spr->mobj->hitlag * (FRACUNIT / 10); + + if (leveltime & 1) + { + mul = -mul; + } + + thingxpos += FixedMul(spr->mobj->momx, mul); + thingypos += FixedMul(spr->mobj->momy, mul); + thingzpos += FixedMul(spr->mobj->momz, mul); + } // Apparently people don't like jump frames like that, so back it goes //if (tics > durs) //durs = tics; if (spr->mobj->drawflags & MFD_TRANSMASK) - HWR_TranstableToAlpha((spr->mobj->drawflags & MFD_TRANSMASK)>>MFD_TRANSSHIFT, &Surf); + blendmode = HWR_TranstableToAlpha((spr->mobj->drawflags & MFD_TRANSMASK)>>MFD_TRANSSHIFT, &Surf); else if (spr->mobj->frame & FF_TRANSMASK) - HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &Surf); + blendmode = HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &Surf); else Surf.PolyColor.s.alpha = 0xFF; + if (blendmode == PF_Masked) + { + blendmode |= PF_Occlude; + } + // dont forget to enabled the depth test because we can't do this like // before: polygons models are not sorted @@ -1378,12 +1397,10 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) { md2 = &md2_playermodels[(skin_t*)spr->mobj->skin-skins]; md2->skin = (skin_t*)spr->mobj->skin-skins; - sprinfo = &((skin_t *)spr->mobj->skin)->sprinfo[spr->mobj->sprite2]; } else { md2 = &md2_models[spr->mobj->sprite]; - sprinfo = &spriteinfo[spr->mobj->sprite]; } // texture loading before model init, so it knows if sprite graphics are used, which @@ -1578,60 +1595,35 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } p.rollangle = 0.0f; - p.rollflip = 1; - p.rotaxis = 0; + if (spr->mobj->rollangle) { + fixed_t camAngleDiff = AngleFixed(viewangle) - FLOAT_TO_FIXED(p.angley); // dumb reconversion back, I know fixed_t anglef = AngleFixed(spr->mobj->rollangle); + p.rollangle = FIXED_TO_FLOAT(anglef); p.roll = true; // rotation pivot - p.centerx = FIXED_TO_FLOAT(spr->mobj->radius/2); - p.centery = FIXED_TO_FLOAT(spr->mobj->height/(flip ? -2 : 2)); + p.centerx = FIXED_TO_FLOAT(spr->mobj->radius / 2); + p.centery = FIXED_TO_FLOAT(spr->mobj->height / 2); - // rotation axis - if (sprinfo->available) - p.rotaxis = (UINT8)(sprinfo->pivot[(spr->mobj->frame & FF_FRAMEMASK)].rotaxis); - - // for NiGHTS specifically but should work everywhere else - ang = R_PointToAngle (spr->mobj->x, spr->mobj->y) - (spr->mobj->player ? spr->mobj->player->drawangle : spr->mobj->angle); - if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right - p.rollflip = 1; - else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left - p.rollflip = -1; - - if (flip) - p.rollflip *= -1; + // rotation axes relative to camera + p.rollx = FIXED_TO_FLOAT(FINECOSINE(FixedAngle(camAngleDiff) >> ANGLETOFINESHIFT)); + p.rollz = FIXED_TO_FLOAT(FINESINE(FixedAngle(camAngleDiff) >> ANGLETOFINESHIFT)); } - p.anglex = 0.0f; - -#ifdef USE_FTRANSFORM_ANGLEZ - // Slope rotation from Kart - p.anglez = 0.0f; - if (spr->mobj->standingslope) - { - fixed_t tempz = spr->mobj->standingslope->normal.z; - fixed_t tempy = spr->mobj->standingslope->normal.y; - fixed_t tempx = spr->mobj->standingslope->normal.x; - fixed_t tempangle = AngleFixed(R_PointToAngle2(0, 0, FixedSqrt(FixedMul(tempy, tempy) + FixedMul(tempz, tempz)), tempx)); - p.anglez = FIXED_TO_FLOAT(tempangle); - tempangle = -AngleFixed(R_PointToAngle2(0, 0, tempz, tempy)); - p.anglex = FIXED_TO_FLOAT(tempangle); - } -#endif + p.anglez = FIXED_TO_FLOAT(AngleFixed(spr->mobj->pitch)); + p.anglex = FIXED_TO_FLOAT(AngleFixed(spr->mobj->roll)); // SRB2CBTODO: MD2 scaling support finalscale *= FIXED_TO_FLOAT(spr->mobj->scale); p.flip = atransform.flip; -#ifdef USE_FTRANSFORM_MIRROR - p.mirror = atransform.mirror; // from Kart -#endif + p.mirror = atransform.mirror; HWD.pfnSetShader(SHADER_MODEL); // model shader - HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, hflip, &Surf); + HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, hflip, &Surf, blendmode); } return true; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 942d3d3de..1ded58204 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2513,7 +2513,7 @@ EXPORT void HWRAPI(CreateModelVBOs) (model_t *model) #define BUFFER_OFFSET(i) ((void*)(i)) -static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface, FBITFIELD blendmode) { static GLRGBAFloat poly = {0,0,0,0}; static GLRGBAFloat tint = {0,0,0,0}; @@ -2593,7 +2593,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 else pglColor4ubv((GLubyte*)&Surface->PolyColor.s); - SetBlend((poly.alpha < 1 ? PF_Translucent : (PF_Masked|PF_Occlude))|PF_Modulated); + SetBlend(blendmode|PF_Modulated); tint.red = byte2float[Surface->TintColor.s.red]; tint.green = byte2float[Surface->TintColor.s.green]; @@ -2610,7 +2610,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 pglEnable(GL_CULL_FACE); pglEnable(GL_NORMALIZE); -#ifdef USE_FTRANSFORM_MIRROR // flipped is if the object is vertically flipped // hflipped is if the object is horizontally flipped // pos->flip is if the screen is flipped vertically @@ -2623,17 +2622,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 else pglCullFace(GL_BACK); } -#else - // pos->flip is if the screen is flipped too - if (flipped ^ hflipped ^ pos->flip) // If one or three of these are active, but not two, invert the model's culling - { - pglCullFace(GL_FRONT); - } - else - { - pglCullFace(GL_BACK); - } -#endif pglPushMatrix(); // should be the same as glLoadIdentity //Hurdler: now it seems to work @@ -2643,22 +2631,14 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 if (hflipped) scalez = -scalez; -#ifdef USE_FTRANSFORM_ANGLEZ - pglRotatef(pos->anglez, 0.0f, 0.0f, -1.0f); // rotate by slope from Kart -#endif - pglRotatef(pos->angley, 0.0f, -1.0f, 0.0f); + pglRotatef(pos->anglez, 0.0f, 0.0f, -1.0f); pglRotatef(pos->anglex, 1.0f, 0.0f, 0.0f); + pglRotatef(pos->angley, 0.0f, -1.0f, 0.0f); if (pos->roll) { - float roll = (1.0f * pos->rollflip); pglTranslatef(pos->centerx, pos->centery, 0); - if (pos->rotaxis == 2) // Z - pglRotatef(pos->rollangle, 0.0f, 0.0f, roll); - else if (pos->rotaxis == 1) // Y - pglRotatef(pos->rollangle, 0.0f, roll, 0.0f); - else // X - pglRotatef(pos->rollangle, roll, 0.0f, 0.0f); + pglRotatef(pos->rollangle, pos->rollx, 0.0f, pos->rollz); pglTranslatef(-pos->centerx, -pos->centery, 0); } @@ -2812,9 +2792,9 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 // -----------------+ // HWRAPI DrawModel : Draw a model // -----------------+ -EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface) +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface, FBITFIELD blendmode) { - DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, hflipped, Surface); + DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, hflipped, Surface, blendmode); } // -----------------+ @@ -2831,13 +2811,9 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform) if (stransform) { used_fov = stransform->fovxangle; -#ifdef USE_FTRANSFORM_MIRROR - // mirroring from Kart if (stransform->mirror) pglScalef(-stransform->scalex, stransform->scaley, -stransform->scalez); - else -#endif - if (stransform->flip) + else if (stransform->flip) pglScalef(stransform->scalex, -stransform->scaley, -stransform->scalez); else pglScalef(stransform->scalex, stransform->scaley, -stransform->scalez); diff --git a/src/i_net.h b/src/i_net.h index 5d93f191e..8caa0edcc 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -77,11 +77,19 @@ typedef struct char data[MAXPACKETLENGTH]; } ATTRPACK doomcom_t; +typedef struct +{ + INT32 magic; + INT32 addr; + INT16 port; +} ATTRPACK holepunch_t; + #if defined(_MSC_VER) #pragma pack() #endif extern doomcom_t *doomcom; +extern holepunch_t *holepunchpacket; /** \brief return packet in doomcom struct */ @@ -140,6 +148,15 @@ extern boolean (*I_NetOpenSocket)(void); extern void (*I_NetCloseSocket)(void); +/** \brief send a hole punching request +*/ +extern void (*I_NetRequestHolePunch)(void); + +/** \brief register this machine on the hole punching server +*/ +extern void (*I_NetRegisterHolePunch)(void); + + extern boolean (*I_Ban) (INT32 node); extern void (*I_ClearBans)(void); extern const char *(*I_GetNodeAddress) (INT32 node); diff --git a/src/i_tcp.c b/src/i_tcp.c index 5180869a5..483062793 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -152,6 +152,7 @@ static UINT8 UPNP_support = TRUE; #include "d_netfil.h" #include "i_tcp.h" #include "m_argv.h" +#include "stun.h" #include "doomstat.h" @@ -203,6 +204,7 @@ static size_t broadcastaddresses = 0; static boolean nodeconnected[MAXNETNODES+1]; static mysockaddr_t banned[MAXBANS]; static UINT8 bannedmask[MAXBANS]; +static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11); #endif static size_t numbans = 0; @@ -559,6 +561,27 @@ void Command_Numnodes(void) #endif #ifndef NONET +static boolean hole_punch(ssize_t c) +{ + if (c == 10 && holepunchpacket->magic == hole_punch_magic) + { + mysockaddr_t addr; + addr.ip4.sin_family = AF_INET; + addr.ip4.sin_addr.s_addr = holepunchpacket->addr; + addr.ip4.sin_port = holepunchpacket->port; + sendto(mysockets[0], NULL, 0, 0, &addr.any, sizeof addr.ip4); + + CONS_Debug(DBG_NETPLAY, + "hole punching request from %s\n", SOCK_AddrToStr(&addr)); + + return true; + } + else + { + return false; + } +} + // Returns true if a packet was received from a new node, false in all other cases static boolean SOCK_Get(void) { @@ -573,8 +596,20 @@ static boolean SOCK_Get(void) fromlen = (socklen_t)sizeof(fromaddress); c = recvfrom(mysockets[n], (char *)&doomcom->data, MAXPACKETLENGTH, 0, (void *)&fromaddress, &fromlen); - if (c != ERRSOCKET) + if (c > 0) { +#ifdef USE_STUN + if (STUN_got_response(doomcom->data, c)) + { + return false; + } +#endif + + if (hole_punch(c)) + { + return false; + } + // find remote node number for (j = 1; j <= MAXNETNODES; j++) //include LAN { @@ -1257,17 +1292,14 @@ void I_ShutdownTcpDriver(void) } #ifndef NONET -static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) +static boolean SOCK_GetAddr(struct sockaddr_in *sin, const char *address, const char *port, boolean test) { - SINT8 newnode = -1; struct my_addrinfo *ai = NULL, *runp, hints; int gaie; - if (!port || !port[0]) + if (!port || !port[0]) port = DEFAULTPORT; - DEBFILE(va("Creating new node: %s@%s\n", address, port)); - memset (&hints, 0x00, sizeof (hints)); hints.ai_flags = 0; hints.ai_family = AF_UNSPEC; @@ -1275,30 +1307,91 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) hints.ai_protocol = IPPROTO_UDP; gaie = I_getaddrinfo(address, port, &hints, &ai); - if (gaie == 0) - { - newnode = getfreenode(); - } - if (newnode == -1) + + if (gaie != 0) { I_freeaddrinfo(ai); - return -1; + return false; + } + + runp = ai; + + if (test) + { + while (runp != NULL) + { + if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0) + break; + + runp = runp->ai_next; + } + } + + if (runp != NULL) + memcpy(sin, runp->ai_addr, runp->ai_addrlen); + + I_freeaddrinfo(ai); + + return (runp != NULL); +} + +static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) +{ + SINT8 newnode = getfreenode(); + + DEBFILE(va("Creating new node: %s@%s\n", address, port)); + + if (newnode != -1) + { + if (!SOCK_GetAddr(&clientaddress[newnode].ip4, address, port, true)) + { + nodeconnected[newnode] = false; + return -1; + } + } + + return newnode; +} + +static void rendezvous(int size) +{ + char *addrs = strdup(cv_rendezvousserver.string); + + char *host = strtok(addrs, ":"); + char *port = strtok(NULL, ":"); + + mysockaddr_t rzv; + + if (SOCK_GetAddr(&rzv.ip4, host, (port ? port : "7777"), false)) + { + holepunchpacket->magic = hole_punch_magic; + sendto(mysockets[0], doomcom->data, size, 0, &rzv.any, sizeof rzv.ip4); } else - runp = ai; - - while (runp != NULL) { - // find ip of the server - if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0) - { - memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen); - break; - } - runp = runp->ai_next; + CONS_Alert(CONS_ERROR, "Failed to contact rendezvous server (%s).\n", + cv_rendezvousserver.string); } - I_freeaddrinfo(ai); - return newnode; + + free(addrs); +} + +static void SOCK_RequestHolePunch(void) +{ + mysockaddr_t * addr = &clientaddress[doomcom->remotenode]; + + holepunchpacket->addr = addr->ip4.sin_addr.s_addr; + holepunchpacket->port = addr->ip4.sin_port; + + CONS_Debug(DBG_NETPLAY, + "requesting hole punch to node %s\n", SOCK_AddrToStr(addr)); + + rendezvous(10); +} + +static void SOCK_RegisterHolePunch(void) +{ + rendezvous(4); } #endif @@ -1325,6 +1418,9 @@ static boolean SOCK_OpenSocket(void) I_NetCanGet = SOCK_CanGet; #endif + I_NetRequestHolePunch = SOCK_RequestHolePunch; + I_NetRegisterHolePunch = SOCK_RegisterHolePunch; + // build the socket but close it first SOCK_CloseSocket(); return UDP_Socket(); diff --git a/src/info.c b/src/info.c index 5677e4270..ae7e58b8e 100644 --- a/src/info.c +++ b/src/info.c @@ -135,11 +135,12 @@ char sprnames[NUMSPRITES + 1][5] = "TOKE", // Special Stage Token "RFLG", // Red CTF Flag "BFLG", // Blue CTF Flag - //"SPHR", // Sphere + "BSPH", // Sphere "NCHP", // NiGHTS chip "NSTR", // NiGHTS star "EMBM", // Emblem - "CEMG", // Chaos Emeralds + "EMRC", // Chaos Emeralds + "ESPK", "SHRD", // Emerald Hunt // Interactive Objects @@ -557,6 +558,10 @@ char sprnames[NUMSPRITES + 1][5] = "SINK", // Kitchen Sink "SITR", // Kitchen Sink Trail "KBLN", // Battle Mode Bumper + "BEXC", // Battle Bumper Explosion: Crystal + "BEXS", // Battle Bumper Explosion: Shell + "BDEB", // Battle Bumper Explosion: Debris + "BEXB", // Battle Bumper Explosion: Blast "DEZL", // DEZ Laser respawn @@ -713,7 +718,9 @@ char sprnames[NUMSPRITES + 1][5] = "DRAF", "GRES", - "OTFG", + "OTBU", + "OTLS", + "OTCP", "DBOS", // Drift boost flame @@ -730,6 +737,9 @@ char sprnames[NUMSPRITES + 1][5] = "DBCL", // Drift boost clip "DBNC", // Drift boost clip's sparks "DBST", // Drift boost plume + + "SDDS", // Spindash dust + "SDWN", // Spindash wind }; char spr2names[NUMPLAYERSPRITES][5] = @@ -1799,20 +1809,49 @@ state_t states[NUMSTATES] = {SPR_RING, 20, 1, {NULL}, 0, 0, S_FASTRING12}, // S_FASTRING11 {SPR_RING, 22, 1, {NULL}, 0, 0, S_FASTRING1}, // S_FASTRING12 - // Blue Sphere for special stages - {SPR_SPHR, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERE - {SPR_SPHR, FF_FULLBRIGHT -#ifdef MANIASPHERES - |FF_ANIMATE|FF_RANDOMANIM -#endif - , -1, {NULL}, 1, 4, S_NULL}, // S_BLUESPHEREBONUS - {SPR_SPHR, 0, 20, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERESPARK + // Blue Sphere + {SPR_BSPH, FF_SEMIBRIGHT|2, TICRATE, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE1}, // S_BLUESPHERE + {SPR_BSPH, FF_SEMIBRIGHT|2, TICRATE, {A_SetRandomTics}, 1, TICRATE, S_BLUESPHERE_BOUNCE1}, // S_BLUESPHERE_SPAWN + + {SPR_BSPH, FF_SEMIBRIGHT, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE2}, // S_BLUESPHERE_BOUNCE1 + {SPR_BSPH, FF_SEMIBRIGHT|4, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE3}, // S_BLUESPHERE_BOUNCE2 + + {SPR_BSPH, FF_SEMIBRIGHT, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE4}, // S_BLUESPHERE_BOUNCE3 + {SPR_BSPH, FF_SEMIBRIGHT|4, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE5}, // S_BLUESPHERE_BOUNCE4 + + {SPR_BSPH, FF_SEMIBRIGHT, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE6}, // S_BLUESPHERE_BOUNCE5 + {SPR_BSPH, FF_SEMIBRIGHT|2, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE7}, // S_BLUESPHERE_BOUNCE6 + {SPR_BSPH, FF_SEMIBRIGHT|4, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE8}, // S_BLUESPHERE_BOUNCE7 + {SPR_BSPH, FF_SEMIBRIGHT|2, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE9}, // S_BLUESPHERE_BOUNCE8 + + {SPR_BSPH, FF_SEMIBRIGHT, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE10}, // S_BLUESPHERE_BOUNCE9 + {SPR_BSPH, FF_SEMIBRIGHT|2, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE11}, // S_BLUESPHERE_BOUNCE10 + {SPR_BSPH, FF_SEMIBRIGHT|4, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE12}, // S_BLUESPHERE_BOUNCE11 + {SPR_BSPH, FF_SEMIBRIGHT|2, 1, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE13}, // S_BLUESPHERE_BOUNCE12 + + {SPR_BSPH, FF_SEMIBRIGHT, 2, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE14}, // S_BLUESPHERE_BOUNCE13 + {SPR_BSPH, FF_SEMIBRIGHT|1, 2, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE15}, // S_BLUESPHERE_BOUNCE14 + {SPR_BSPH, FF_SEMIBRIGHT|2, 2, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE16}, // S_BLUESPHERE_BOUNCE15 + {SPR_BSPH, FF_SEMIBRIGHT|3, 2, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE17}, // S_BLUESPHERE_BOUNCE16 + {SPR_BSPH, FF_SEMIBRIGHT|4, 2, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE18}, // S_BLUESPHERE_BOUNCE17 + {SPR_BSPH, FF_SEMIBRIGHT|3, 4, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE19}, // S_BLUESPHERE_BOUNCE18 + {SPR_BSPH, FF_SEMIBRIGHT|2, 4, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE20}, // S_BLUESPHERE_BOUNCE19 + {SPR_BSPH, FF_SEMIBRIGHT|1, 4, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE21}, // S_BLUESPHERE_BOUNCE20 + + {SPR_BSPH, FF_SEMIBRIGHT, 6, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE22}, // S_BLUESPHERE_BOUNCE21 + {SPR_BSPH, FF_SEMIBRIGHT|1, 6, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE23}, // S_BLUESPHERE_BOUNCE22 + {SPR_BSPH, FF_SEMIBRIGHT|2, 6, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE24}, // S_BLUESPHERE_BOUNCE23 + {SPR_BSPH, FF_SEMIBRIGHT|3, 9, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE25}, // S_BLUESPHERE_BOUNCE24 + {SPR_BSPH, FF_SEMIBRIGHT|4, 9, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE26}, // S_BLUESPHERE_BOUNCE25 + {SPR_BSPH, FF_SEMIBRIGHT|3, 9, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE27}, // S_BLUESPHERE_BOUNCE26 + {SPR_BSPH, FF_SEMIBRIGHT|2, 9, {NULL}, 0, 0, S_BLUESPHERE_BOUNCE28}, // S_BLUESPHERE_BOUNCE27 + {SPR_BSPH, FF_SEMIBRIGHT|1, 9, {NULL}, 0, 0, S_BLUESPHERE}, // S_BLUESPHERE_BOUNCE28 // Bomb Sphere - {SPR_SPHR, FF_FULLBRIGHT|3, 2, {NULL}, 0, 0, S_BOMBSPHERE2}, // S_BOMBSPHERE1 - {SPR_SPHR, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE3}, // S_BOMBSPHERE2 - {SPR_SPHR, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_BOMBSPHERE4}, // S_BOMBSPHERE3 - {SPR_SPHR, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE1}, // S_BOMBSPHERE4 + {SPR_BSPH, FF_FULLBRIGHT|3, 2, {NULL}, 0, 0, S_BOMBSPHERE2}, // S_BOMBSPHERE1 + {SPR_BSPH, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE3}, // S_BOMBSPHERE2 + {SPR_BSPH, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_BOMBSPHERE4}, // S_BOMBSPHERE3 + {SPR_BSPH, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE1}, // S_BOMBSPHERE4 // NiGHTS Chip {SPR_NCHP, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 15, 2, S_NULL}, // S_NIGHTSCHIP @@ -1865,13 +1904,17 @@ state_t states[NUMSTATES] = {SPR_EMBM, 25, -1, {NULL}, 0, 0, S_NULL}, // S_EMBLEM26 // Chaos Emeralds - {SPR_CEMG, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_CEMG1 - {SPR_CEMG, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_CEMG2 - {SPR_CEMG, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_NULL}, // S_CEMG3 - {SPR_CEMG, FF_FULLBRIGHT|3, -1, {NULL}, 0, 0, S_NULL}, // S_CEMG4 - {SPR_CEMG, FF_FULLBRIGHT|4, -1, {NULL}, 0, 0, S_NULL}, // S_CEMG5 - {SPR_CEMG, FF_FULLBRIGHT|5, -1, {NULL}, 0, 0, S_NULL}, // S_CEMG6 - {SPR_CEMG, FF_FULLBRIGHT|6, -1, {NULL}, 0, 0, S_NULL}, // S_CEMG7 + {SPR_EMRC, FF_SEMIBRIGHT, 1, {NULL}, 0, 0, S_CHAOSEMERALD2}, // S_CHAOSEMERALD1 + {SPR_EMRC, FF_FULLBRIGHT|FF_TRANSADD, 1, {NULL}, 0, 0, S_CHAOSEMERALD1}, // S_CHAOSEMERALD2 + {SPR_EMRC, FF_FULLBRIGHT|1, -1, {NULL}, 1, 0, S_NULL}, // S_CHAOSEMERALD_UNDER + + {SPR_ESPK, FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_EMERALDSPARK2}, // S_EMERALDSPARK1 + {SPR_ESPK, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_EMERALDSPARK3}, // S_EMERALDSPARK2 + {SPR_ESPK, FF_FULLBRIGHT|2, 3, {NULL}, 0, 0, S_EMERALDSPARK4}, // S_EMERALDSPARK3 + {SPR_ESPK, FF_FULLBRIGHT|3, 3, {NULL}, 0, 0, S_EMERALDSPARK5}, // S_EMERALDSPARK4 + {SPR_ESPK, FF_FULLBRIGHT|4, 3, {NULL}, 0, 0, S_EMERALDSPARK6}, // S_EMERALDSPARK5 + {SPR_ESPK, FF_FULLBRIGHT|5, 3, {NULL}, 0, 0, S_EMERALDSPARK7}, // S_EMERALDSPARK6 + {SPR_ESPK, FF_FULLBRIGHT|6, 3, {NULL}, 0, 0, S_NULL}, // S_EMERALDSPARK7 // Emerald hunt shards {SPR_SHRD, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHRD1 @@ -3715,14 +3758,14 @@ state_t states[NUMSTATES] = {SPR_CAPS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EGGCAPSULE // Orbiting Chaos Emeralds/Ideya for NiGHTS - {SPR_CEMG, FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM1}, // S_ORBITEM1 - {SPR_CEMG, FF_FULLBRIGHT|1, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM2}, // S_ORBITEM2 - {SPR_CEMG, FF_FULLBRIGHT|2, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM3}, // S_ORBITEM3 - {SPR_CEMG, FF_FULLBRIGHT|3, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM4}, // S_ORBITEM4 - {SPR_CEMG, FF_FULLBRIGHT|4, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM5}, // S_ORBITEM5 - {SPR_CEMG, FF_FULLBRIGHT|5, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM6}, // S_ORBITEM6 - {SPR_CEMG, FF_FULLBRIGHT|6, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM7}, // S_ORBITEM7 - {SPR_CEMG, FF_FULLBRIGHT|7, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM8 + {SPR_EMRC, FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM1}, // S_ORBITEM1 + {SPR_EMRC, FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM2}, // S_ORBITEM2 + {SPR_EMRC, FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM3}, // S_ORBITEM3 + {SPR_EMRC, FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM4}, // S_ORBITEM4 + {SPR_EMRC, FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM5}, // S_ORBITEM5 + {SPR_EMRC, FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM6}, // S_ORBITEM6 + {SPR_EMRC, FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM7}, // S_ORBITEM7 + {SPR_EMRC, FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM8 {SPR_IDYA, FF_TRANS20|FF_FULLBRIGHT, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBIDYA1}, // S_ORBIDYA1 {SPR_IDYA, FF_TRANS20|FF_FULLBRIGHT|1, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBIDYA2}, // S_ORBIDYA2 {SPR_IDYA, FF_TRANS20|FF_FULLBRIGHT|2, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBIDYA3}, // S_ORBIDYA3 @@ -4362,9 +4405,47 @@ state_t states[NUMSTATES] = {SPR_SITR, 1, 5, {NULL}, 0, 0, S_SINKTRAIL3}, // S_SINKTRAIL2 {SPR_SITR, 2, 3, {NULL}, 0, 0, S_NULL}, // S_SINKTRAIL3 - {SPR_KBLN, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_BATTLEBUMPER1}, // S_BATTLEBUMPER1 - {SPR_KBLN, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_BATTLEBUMPER2}, // S_BATTLEBUMPER2 - {SPR_KBLN, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_BATTLEBUMPER3}, // S_BATTLEBUMPER3 + {SPR_KBLN, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLEBUMPER1 + {SPR_KBLN, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLEBUMPER2 + {SPR_KBLN, FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLEBUMPER3 + + {SPR_BEXC, FF_SEMIBRIGHT, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALA2}, // S_BATTLEBUMPER_EXCRYSTALA1 + {SPR_BEXC, FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALA3}, // S_BATTLEBUMPER_EXCRYSTALA2 + {SPR_BEXC, FF_SEMIBRIGHT, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALA4}, // S_BATTLEBUMPER_EXCRYSTALA3 + {SPR_BEXC, FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALA1}, // S_BATTLEBUMPER_EXCRYSTALA4 + + {SPR_BEXC, FF_SEMIBRIGHT|3, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALB2}, // S_BATTLEBUMPER_EXCRYSTALB1 + {SPR_BEXC, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALB3}, // S_BATTLEBUMPER_EXCRYSTALB2 + {SPR_BEXC, FF_SEMIBRIGHT|3, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALB4}, // S_BATTLEBUMPER_EXCRYSTALB3 + {SPR_BEXC, FF_FULLBRIGHT|5, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALB1}, // S_BATTLEBUMPER_EXCRYSTALB4 + + {SPR_BEXC, FF_SEMIBRIGHT|6, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALC2}, // S_BATTLEBUMPER_EXCRYSTALC1 + {SPR_BEXC, FF_FULLBRIGHT|7, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALC3}, // S_BATTLEBUMPER_EXCRYSTALC2 + {SPR_BEXC, FF_SEMIBRIGHT|6, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALC4}, // S_BATTLEBUMPER_EXCRYSTALC3 + {SPR_BEXC, FF_FULLBRIGHT|8, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXCRYSTALC1}, // S_BATTLEBUMPER_EXCRYSTALC4 + + {SPR_BEXS, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXSHELLA2}, // S_BATTLEBUMPER_EXSHELLA1 + {SPR_BEXS, FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXSHELLA1}, // S_BATTLEBUMPER_EXSHELLA2 + + {SPR_BEXS, FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXSHELLB2}, // S_BATTLEBUMPER_EXSHELLB1 + {SPR_BEXS, FF_FULLBRIGHT|3, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXSHELLB1}, // S_BATTLEBUMPER_EXSHELLB2 + + {SPR_BEXS, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXSHELLC2}, // S_BATTLEBUMPER_EXSHELLC1 + {SPR_BEXS, FF_FULLBRIGHT|5, 1, {NULL}, 0, 0, S_BATTLEBUMPER_EXSHELLC1}, // S_BATTLEBUMPER_EXSHELLC2 + + {SPR_BDEB, FF_FULLBRIGHT|FF_ANIMATE, 84, {NULL}, 13, 6, S_BATTLEBUMPER_EXDEBRIS2}, // S_BATTLEBUMPER_EXDEBRIS1 + {SPR_BDEB, FF_FULLBRIGHT|13, 20, {NULL}, 0, 0, S_NULL}, // S_BATTLEBUMPER_EXDEBRIS2 + + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST2}, // S_BATTLEBUMPER_EXBLAST1 + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS10, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST3}, // S_BATTLEBUMPER_EXBLAST2 + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS20, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST4}, // S_BATTLEBUMPER_EXBLAST3 + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS30, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST5}, // S_BATTLEBUMPER_EXBLAST4 + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS40, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST6}, // S_BATTLEBUMPER_EXBLAST5 + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS50, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST7}, // S_BATTLEBUMPER_EXBLAST6 + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS60, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST8}, // S_BATTLEBUMPER_EXBLAST7 + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS70, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST9}, // S_BATTLEBUMPER_EXBLAST8 + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS80, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST10}, // S_BATTLEBUMPER_EXBLAST9 + {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS90, 2, {NULL}, 0, 0, S_NULL}, // S_BATTLEBUMPER_EXBLAST10 {SPR_DEZL, FF_FULLBRIGHT|FF_PAPERSPRITE, 8, {NULL}, 0, 0, S_NULL}, // S_DEZLASER {SPR_DEZL, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL2}, // S_DEZLASER_TRAIL1 @@ -4995,9 +5076,10 @@ state_t states[NUMSTATES] = {SPR_GRES, FF_ANIMATE|FF_PAPERSPRITE, -1, {NULL}, 2, 4, S_NULL}, // S_TIREGREASE - {SPR_OTFG, FF_FULLBRIGHT|FF_TRANS50, TICRATE, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEFOG - {SPR_OTFG, 2|FF_FULLBRIGHT|FF_PAPERSPRITE, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEORB - {SPR_OTFG, 1|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEBEAM + {SPR_OTBU, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIME_BULB1 + {SPR_OTBU, FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIME_BULB2 + {SPR_OTLS, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_OVERTIME_LASER + {SPR_OTCP, 0, -1, {NULL}, 0, 0, S_NULL}, // S_OVERTIME_CENTER {SPR_CAPS, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLECAPSULE_SIDE1 {SPR_CAPS, FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_BATTLECAPSULE_SIDE2 @@ -5026,6 +5108,9 @@ state_t states[NUMSTATES] = {SPR_WTRL, FF_TRANS50|FF_PAPERSPRITE|14, 2, {NULL}, 0, 0, S_NULL}, // S_WATERTRAILUNDERLAY7 {SPR_WTRL, FF_TRANS50|FF_PAPERSPRITE|15, 2, {NULL}, 0, 0, S_NULL}, // S_WATERTRAILUNDERLAY8 + {SPR_SDDS, FF_ANIMATE, 9, {NULL}, 9, 1, S_NULL}, // S_SPINDASHDUST + {SPR_SDWN, FF_ANIMATE|FF_PAPERSPRITE, 18, {NULL}, 9, 2, S_NULL}, // S_SPINDASHWIND + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -7923,35 +8008,35 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, { // MT_BLUESPHERE - 1706, // doomednum - S_BLUESPHERE, // spawnstate + -1, // doomednum + S_BLUESPHERE_SPAWN, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound - MT_FLINGBLUESPHERE, // reactiontime + MT_FLINGBLUESPHERE, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_BLUESPHERESPARK, // deathstate + S_NULL, // deathstate S_NULL, // xdeathstate sfx_s3k65, // deathsound 38*FRACUNIT, // speed - 16*FRACUNIT, // radius - 24*FRACUNIT, // height + 48*FRACUNIT, // radius + 48*FRACUNIT, // height 0, // display offset 100, // mass 0, // damage sfx_None, // activesound - MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags - S_BLUESPHEREBONUS // raisestate + MF_RUNSPAWNFUNC|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + S_NULL // raisestate }, { // MT_FLINGBLUESPHERE -1, // doomednum - S_BLUESPHERE, // spawnstate + S_BLUESPHERE_SPAWN, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -7962,7 +8047,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_BLUESPHERESPARK, // deathstate + S_NULL, // deathstate S_NULL, // xdeathstate sfx_s3k65, // deathsound 38*FRACUNIT, // speed @@ -7973,7 +8058,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // damage sfx_None, // activesound MF_SLIDEME|MF_SPECIAL, // flags - S_BLUESPHEREBONUS // raisestate + S_NULL // raisestate }, { // MT_BOMBSPHERE @@ -8165,9 +8250,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_EMERALD1 - 313, // doomednum - S_CEMG1, // spawnstate + { // MT_EMERALD + -1, // doomednum + S_CHAOSEMERALD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -8178,22 +8263,23 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_SPRK1, // deathstate + S_NULL, // deathstate S_NULL, // xdeathstate - sfx_cgot, // deathsound - EMERALD1, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height + sfx_s3k9c, // deathsound + 0, // speed + 72*FRACUNIT, // radius + 72*FRACUNIT, // height 0, // display offset 16, // mass 0, // damage sfx_None, // activesound - MF_NOGRAVITY|MF_SPECIAL, // flags + MF_SPECIAL|MF_PICKUPFROMBELOW|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, - { // MT_EMERALD2 - 314, // doomednum - S_CEMG2, // spawnstate + + { // MT_EMERALDSPARK + -1, // doomednum + S_EMERALDSPARK1,// spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -8204,147 +8290,17 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_SPRK1, // deathstate + S_NULL, // deathstate S_NULL, // xdeathstate - sfx_cgot, // deathsound - EMERALD2, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height + sfx_None, // deathsound + 0, // speed + 8*FRACUNIT, // radius + 8*FRACUNIT, // height 0, // display offset 16, // mass 0, // damage sfx_None, // activesound - MF_NOGRAVITY|MF_SPECIAL, // flags - S_NULL // raisestate - }, - { // MT_EMERALD3 - 315, // doomednum - S_CEMG3, // 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_SPRK1, // deathstate - S_NULL, // xdeathstate - sfx_cgot, // deathsound - EMERALD3, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height - 0, // display offset - 16, // mass - 0, // damage - sfx_None, // activesound - MF_NOGRAVITY|MF_SPECIAL, // flags - S_NULL // raisestate - }, - { // MT_EMERALD4 - 316, // doomednum - S_CEMG4, // 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_SPRK1, // deathstate - S_NULL, // xdeathstate - sfx_cgot, // deathsound - EMERALD4, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height - 0, // display offset - 16, // mass - 0, // damage - sfx_None, // activesound - MF_NOGRAVITY|MF_SPECIAL, // flags - S_NULL // raisestate - }, - { // MT_EMERALD5 - 317, // doomednum - S_CEMG5, // 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_SPRK1, // deathstate - S_NULL, // xdeathstate - sfx_cgot, // deathsound - EMERALD5, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height - 0, // display offset - 16, // mass - 0, // damage - sfx_None, // activesound - MF_NOGRAVITY|MF_SPECIAL, // flags - S_NULL // raisestate - }, - { // MT_EMERALD6 - 318, // doomednum - S_CEMG6, // 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_SPRK1, // deathstate - S_NULL, // xdeathstate - sfx_cgot, // deathsound - EMERALD6, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height - 0, // display offset - 16, // mass - 0, // damage - sfx_None, // activesound - MF_NOGRAVITY|MF_SPECIAL, // flags - S_NULL // raisestate - }, - { // MT_EMERALD7 - 319, // doomednum - S_CEMG7, // 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_SPRK1, // deathstate - S_NULL, // xdeathstate - sfx_cgot, // deathsound - EMERALD7, // speed - 16*FRACUNIT, // radius - 32*FRACUNIT, // height - 0, // display offset - 16, // mass - 0, // damage - sfx_None, // activesound - MF_NOGRAVITY|MF_SPECIAL, // flags + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -8402,33 +8358,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_FLINGEMERALD - -1, // doomednum - S_CEMG1, // spawnstate - 1000, // spawnhealth - S_NULL, // seestate - sfx_None, // seesound - 0, // reactiontime - sfx_None, // attacksound - S_NULL, // painstate - 0, // painchance - sfx_None, // painsound - S_NULL, // meleestate - S_NULL, // missilestate - S_SPRK1, // deathstate - S_NULL, // xdeathstate - sfx_cgot, // deathsound - 60*FRACUNIT, // speed - 16*FRACUNIT, // radius - 48*FRACUNIT, // height - 0, // display offset - 100, // mass - 0, // damage - sfx_None, // activesound - MF_SLIDEME|MF_SPECIAL, // flags - S_NULL // raisestate - }, - { // MT_FAN 540, // doomednum S_FAN, // spawnstate @@ -8560,7 +8489,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 25*FRACUNIT, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_YELLOWSPRING2 // raisestate }, @@ -8587,7 +8516,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 40*FRACUNIT, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_REDSPRING2 // raisestate }, @@ -8614,7 +8543,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 64*FRACUNIT, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_BLUESPRING2 // raisestate }, @@ -8641,7 +8570,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 15*FRACUNIT, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_GREYSPRING2 // raisestate }, @@ -8668,7 +8597,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 25*FRACUNIT, // mass 25*FRACUNIT, // damage sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_YDIAG2 // raisestate }, @@ -8695,7 +8624,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 40*FRACUNIT, // mass 40*FRACUNIT, // damage sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_RDIAG2 // raisestate }, @@ -8722,7 +8651,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 64*FRACUNIT, // mass 64*FRACUNIT, // damage sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_BDIAG2 // raisestate }, @@ -8749,7 +8678,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 15*FRACUNIT, // mass 15*FRACUNIT, // damage sfx_None, // activesound - MF_SOLID|MF_SPRING|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_SPRING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_GDIAG2 // raisestate }, @@ -19073,7 +19002,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_GOTEMERALD -1, // doomednum - S_CEMG1, // spawnstate + S_CHAOSEMERALD1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -22908,13 +22837,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_itpick, // deathsound 0, // speed - 32*FRACUNIT, // radius - 32*FRACUNIT, // height + 48*FRACUNIT, // radius + 64*FRACUNIT, // height 0, // display offset 100, // mass 0, // damage sfx_None, // activesound - MF_SLIDEME|MF_SPECIAL|MF_DONTENCOREMAP, // flags + MF_SLIDEME|MF_SPECIAL|MF_PICKUPFROMBELOW|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -24200,7 +24129,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_NULL, // deathstate + S_BATTLEBUMPER1, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 4*FRACUNIT, // speed @@ -24214,6 +24143,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_BATTLEBUMPER_DEBRIS + -1, // doomednum + S_BATTLEBUMPER_EXDEBRIS1,// spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_SCENERY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_BATTLEBUMPER_BLAST + -1, // doomednum + S_BATTLEBUMPER_EXBLAST1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8*FRACUNIT, // radius + 16*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_SCENERY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_DEZLASER -1, // doomednum S_DEZLASER, // spawnstate @@ -28402,9 +28385,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_OVERTIMEFOG + { // MT_OVERTIME_PARTICLE -1, // doomednum - S_OVERTIMEFOG, // spawnstate + S_NULL, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -28420,8 +28403,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // deathsound 0, // speed 16<kartstuff[k_position] == 1); +#else UINT8 i; if (!(gametyperules & GTR_WANTED)) @@ -54,28 +57,26 @@ boolean K_IsPlayerWanted(player_t *player) return true; } return false; +#endif } void K_CalculateBattleWanted(void) { - UINT8 numingame = 0, numplaying = 0, numwanted = 0; - SINT8 bestbumperplayer = -1, bestbumper = -1; + UINT8 numingame = 0, numwanted = 0; SINT8 camppos[MAXPLAYERS]; // who is the biggest camper UINT8 ties = 0, nextcamppos = 0; - boolean setbumper = false; UINT8 i, j; +#if 0 if (!(gametyperules & GTR_WANTED)) +#endif { - for (i = 0; i < 4; i++) - battlewanted[i] = -1; + memset(battlewanted, -1, sizeof (battlewanted)); return; } wantedcalcdelay = wantedfrequency; - - for (i = 0; i < MAXPLAYERS; i++) - camppos[i] = -1; // initialize + memset(camppos, -1, sizeof (camppos)); // initialize for (i = 0; i < MAXPLAYERS; i++) { @@ -87,33 +88,38 @@ void K_CalculateBattleWanted(void) if (players[i].exiting) // We're done, don't calculate. return; - numplaying++; - - if (players[i].kartstuff[k_bumper] <= 0) // Not alive, so don't do anything else + if (players[i].bumpers <= 0) // Not alive, so don't do anything else continue; numingame++; - if (bestbumper == -1 || players[i].kartstuff[k_bumper] > bestbumper) - { - bestbumper = players[i].kartstuff[k_bumper]; - bestbumperplayer = i; - } - else if (players[i].kartstuff[k_bumper] == bestbumper) - bestbumperplayer = -1; // Tie, no one has best bumper. - for (j = 0; j < MAXPLAYERS; j++) { if (!playeringame[j] || players[j].spectator) continue; - if (players[j].kartstuff[k_bumper] <= 0) + + if (players[j].bumpers <= 0) continue; + if (j == i) continue; - if (players[j].kartstuff[k_wanted] == players[i].kartstuff[k_wanted] && players[j].marescore > players[i].marescore) + + if (K_NumEmeralds(&players[j]) > K_NumEmeralds(&players[i])) + { position++; + } + else if (players[j].bumpers > players[i].bumpers) + { + position++; + } + else if (players[j].marescore > players[i].marescore) + { + position++; + } else if (players[j].kartstuff[k_wanted] > players[i].kartstuff[k_wanted]) + { position++; + } } position--; // Make zero based @@ -124,7 +130,7 @@ void K_CalculateBattleWanted(void) camppos[position] = i; } - if (numplaying <= 2 || (numingame <= 2 && bestbumper == 1)) // In 1v1s then there's no need for WANTED. In bigger netgames, don't show anyone as WANTED when they're equally matched. + if (numingame <= 2) // In 1v1s then there's no need for WANTED. numwanted = 0; else numwanted = min(4, 1 + ((numingame-2) / 4)); @@ -132,19 +138,11 @@ void K_CalculateBattleWanted(void) for (i = 0; i < 4; i++) { if (i+1 > numwanted) // Not enough players for this slot to be wanted! - battlewanted[i] = -1; - else if (bestbumperplayer != -1 && !setbumper) // If there's a player who has an untied bumper lead over everyone else, they are the first to be wanted. { - battlewanted[i] = bestbumperplayer; - setbumper = true; // Don't set twice + battlewanted[i] = -1; } else { - // Don't accidentally set the same player, if the bestbumperplayer is also a huge camper. - while (bestbumperplayer != -1 && camppos[nextcamppos] != -1 - && bestbumperplayer == camppos[nextcamppos]) - nextcamppos++; - // Do not add *any* more people if there's too many times that are tied with others. // This could theoretically happen very easily if people don't hit each other for a while after the start of a match. // (I will be sincerely impressed if more than 2 people tie after people start hitting each other though) @@ -230,7 +228,7 @@ void K_CheckBumpers(void) numingame++; winnerscoreadd += players[i].marescore; - if (players[i].kartstuff[k_bumper] <= 0) // if you don't have any bumpers, you're probably not a winner + if (players[i].bumpers <= 0) // if you don't have any bumpers, you're probably not a winner { nobumpers = true; continue; @@ -277,125 +275,411 @@ void K_CheckBumpers(void) P_DoPlayerExit(&players[i]); } -#define MAXPLANESPERSECTOR (MAXFFLOORS+1)*2 - -static void K_SpawnOvertimeParticles(fixed_t x, fixed_t y, fixed_t scale, mobjtype_t type, boolean ceiling) +void K_CheckEmeralds(player_t *player) { UINT8 i; - fixed_t flatz[MAXPLANESPERSECTOR]; - boolean flip[MAXPLANESPERSECTOR]; - UINT8 numflats = 0; - mobj_t *mo; - subsector_t *ss = R_PointInSubsectorOrNull(x, y); - sector_t *sec; - if (!ss) + if (!ALLCHAOSEMERALDS(player->powers[pw_emeralds])) + { return; - sec = ss->sector; - - // convoluted stuff JUST to get all of the planes we need to draw orbs on :V - - for (i = 0; i < MAXPLANESPERSECTOR; i++) - flip[i] = false; - - if (sec->floorpic != skyflatnum) - { - flatz[numflats] = P_GetZAt(sec->f_slope, x, y, sec->floorheight); - numflats++; - } - if (sec->ceilingpic != skyflatnum && ceiling) - { - flatz[numflats] = P_GetZAt(sec->c_slope, x, y, sec->ceilingheight) - FixedMul(mobjinfo[type].height, scale); - flip[numflats] = true; - numflats++; } - if (sec->ffloors) + player->marescore++; // lol + + for (i = 0; i < MAXPLAYERS; i++) { - ffloor_t *rover; - for (rover = sec->ffloors; rover; rover = rover->next) + if (!playeringame[i] || players[i].spectator) { - if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER)) - continue; - if (*rover->toppic != skyflatnum) - { - flatz[numflats] = P_GetZAt(*rover->t_slope, x, y, *rover->topheight); - numflats++; - } - if (*rover->bottompic != skyflatnum && ceiling) - { - flatz[numflats] = P_GetZAt(*rover->b_slope, x, y, *rover->bottomheight); - flip[numflats] = true; - numflats++; - } + continue; } + + if (&players[i] == player) + { + continue; + } + + players[i].bumpers = 0; } - if (numflats <= 0) // no flats - return; + K_CheckBumpers(); +} - for (i = 0; i < numflats; i++) +mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT32 emeraldType) +{ + boolean validEmerald = true; + mobj_t *emerald = P_SpawnMobj(x, y, z, MT_EMERALD); + mobj_t *overlay; + + P_Thrust(emerald, + FixedAngle(P_RandomFixed() * 180) + angle, + 32 * mapobjectscale); + + emerald->momz = flip * 24 * mapobjectscale; + if (emerald->eflags & MFE_UNDERWATER) + emerald->momz = (117 * emerald->momz) / 200; + + emerald->threshold = 10; + + switch (emeraldType) { - mo = P_SpawnMobj(x, y, flatz[i], type); + case EMERALD_CHAOS1: + emerald->color = SKINCOLOR_CHAOSEMERALD1; + break; + case EMERALD_CHAOS2: + emerald->color = SKINCOLOR_CHAOSEMERALD2; + break; + case EMERALD_CHAOS3: + emerald->color = SKINCOLOR_CHAOSEMERALD3; + break; + case EMERALD_CHAOS4: + emerald->color = SKINCOLOR_CHAOSEMERALD4; + break; + case EMERALD_CHAOS5: + emerald->color = SKINCOLOR_CHAOSEMERALD5; + break; + case EMERALD_CHAOS6: + emerald->color = SKINCOLOR_CHAOSEMERALD6; + break; + case EMERALD_CHAOS7: + emerald->color = SKINCOLOR_CHAOSEMERALD7; + break; + default: + CONS_Printf("Invalid emerald type %d\n", emeraldType); + validEmerald = false; + break; + } - // Lastly, if this can see the skybox mobj, then... we just wasted our time :V - if (skyboxmo[0] && !P_MobjWasRemoved(skyboxmo[0])) + if (validEmerald == true) + { + emerald->extravalue1 = emeraldType; + } + + overlay = P_SpawnMobjFromMobj(emerald, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&overlay->target, emerald); + P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER); + overlay->color = emerald->color; + + return emerald; +} + +void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType) +{ + UINT8 i; + SINT8 flip = P_MobjFlip(player->mo); + + for (i = 0; i < 14; i++) + { + UINT32 emeraldFlag = (1 << i); + + if ((player->powers[pw_emeralds] & emeraldFlag) && (emeraldFlag & emeraldType)) { - const fixed_t sbz = skyboxmo[0]->z; - fixed_t checkz = sec->floorheight; + mobj_t *emerald = K_SpawnChaosEmerald(player->mo->x, player->mo->y, player->mo->z, player->mo->angle - ANGLE_90, flip, emeraldFlag); + P_SetTarget(&emerald->target, player->mo); - while (checkz < sec->ceilingheight) - { - P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, checkz); - if (P_CheckSight(skyboxmo[0], mo)) - { - P_RemoveMobj(mo); - break; - } - else - checkz += 32*mapobjectscale; - } - - P_TeleportMove(skyboxmo[0], skyboxmo[0]->x, skyboxmo[0]->y, sbz); - - if (P_MobjWasRemoved(mo)) - continue; - } - - P_SetScale(mo, scale); - - if (flip[i]) - { - mo->flags2 |= MF2_OBJECTFLIP; - mo->eflags |= MFE_VERTICALFLIP; - } - - switch(type) - { - case MT_OVERTIMEFOG: - mo->destscale = 8*mo->scale; - mo->momz = P_RandomRange(1,8)*mo->scale; - break; - case MT_OVERTIMEORB: - //mo->destscale = mo->scale/4; - mo->frame += ((leveltime/4) % 8); - /*if (battleovertime.enabled < 10*TICRATE) - mo->drawflags |= MFD_SHADOW;*/ - mo->angle = R_PointToAngle2(mo->x, mo->y, battleovertime.x, battleovertime.y) + ANGLE_90; - mo->z += P_RandomRange(0,48) * mo->scale; - break; - default: - break; + player->powers[pw_emeralds] &= ~emeraldFlag; } } } -#undef MAXPLANESPERSECTOR +UINT8 K_NumEmeralds(player_t *player) +{ + UINT8 i; + UINT8 num = 0; + + for (i = 0; i < 14; i++) + { + UINT32 emeraldFlag = (1 << i); + + if (player->powers[pw_emeralds] & emeraldFlag) + { + num++; + } + } + + return num; +} + +void K_RunPaperItemSpawners(void) +{ + const boolean overtime = (battleovertime.enabled >= 10*TICRATE); + tic_t interval = 8*TICRATE; + + UINT32 emeraldsSpawned = 0; + UINT32 firstUnspawnedEmerald = 0; + + thinker_t *th; + mobj_t *mo; + + UINT8 pcount = 0; + INT16 i; + + if (leveltime < starttime) + { + // Round hasn't started yet! + return; + } + + if (overtime == true) + { + if (battleovertime.radius < 512*mapobjectscale) + { + // Barrier has closed in too much + return; + } + + // Double frequency of items + interval /= 2; + } + + if (((leveltime - starttime) % interval) != 0) + { + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } + + emeraldsSpawned |= players[i].powers[pw_emeralds]; + + if ((players[i].exiting > 0 || players[i].eliminated) + || ((gametyperules & GTR_BUMPERS) && players[i].bumpers <= 0)) + { + continue; + } + + pcount++; + } + + if (overtime == true) + { + SINT8 flip = 1; + + // Just find emeralds, no paper spots + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo = (mobj_t *)th; + + if (mo->type == MT_EMERALD) + { + emeraldsSpawned |= mo->extravalue1; + } + } + + for (i = 0; i < 7; i++) + { + UINT32 emeraldFlag = (1 << i); + + if (!(emeraldsSpawned & emeraldFlag)) + { + firstUnspawnedEmerald = emeraldFlag; + break; + } + } + + if (firstUnspawnedEmerald != 0) + { + K_SpawnChaosEmerald( + battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + firstUnspawnedEmerald + ); + } + else + { + K_CreatePaperItem( + battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + 0, 0 + ); + } + } + else + { + if (pcount > 0) + { +#define MAXITEM 64 + UINT8 item = 0; + mobj_t *spotList[MAXITEM]; + boolean spotUsed[MAXITEM]; + + INT16 starti = 0; + + memset(spotUsed, false, sizeof(spotUsed)); + + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo = (mobj_t *)th; + + if (mo->type == MT_PAPERITEMSPOT) + { + if (item >= MAXITEM) + continue; + + spotList[item] = mo; + item++; + } + else if (mo->type == MT_EMERALD) + { + emeraldsSpawned |= mo->extravalue1; + } + } + + if (item <= 0) + { + return; + } + + for (i = 0; i < 7; i++) + { + UINT32 emeraldFlag = (1 << i); + + if (!(emeraldsSpawned & emeraldFlag)) + { + firstUnspawnedEmerald = emeraldFlag; + starti = -1; + break; + } + } + + for (i = starti; i < min(item + starti, pcount); i++) + { + UINT8 r = P_RandomKey(item); + UINT8 recursion = 0; + mobj_t *drop = NULL; + SINT8 flip = 1; + + while (spotUsed[r] == true) + { + r = P_RandomKey(item); + + if ((recursion++) > MAXITEM) + { + // roll with it anyway I guess + break; + } + } + + flip = P_MobjFlip(spotList[r]); + + // When -1, we're spawning a Chaos Emerald. + if (i == -1) + { + drop = K_SpawnChaosEmerald( + spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + firstUnspawnedEmerald + ); + } + else + { + drop = K_CreatePaperItem( + spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + 0, 0 + ); + } + + K_FlipFromObject(drop, spotList[r]); + spotUsed[r] = true; + } + } + } +} + +static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale) +{ + UINT8 i, j; + + for (i = 0; i <= r_splitscreen; i++) + { + player_t *player = &players[displayplayers[i]]; + fixed_t zpos; + SINT8 flip; + + if (player == NULL || player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + continue; + } + + if (player->mo->eflags & MFE_VERTICALFLIP) + { + zpos = player->mo->z + player->mo->height; + } + else + { + zpos = player->mo->z; + } + + flip = P_MobjFlip(player->mo); + + for (j = 0; j < 3; j++) + { + mobj_t *mo = P_SpawnMobj(x, y, zpos, MT_OVERTIME_PARTICLE); + + if (player->mo->eflags & MFE_VERTICALFLIP) + { + mo->flags2 |= MF2_OBJECTFLIP; + mo->eflags |= MFE_VERTICALFLIP; + } + + mo->angle = R_PointToAngle2(mo->x, mo->y, battleovertime.x, battleovertime.y) + ANGLE_90; + mo->drawflags |= (MFD_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player))); + + P_SetScale(mo, scale); + + switch (j) + { + case 0: + P_SetMobjState(mo, S_OVERTIME_BULB1); + + if (leveltime & 1) + mo->frame += 1; + + //P_SetScale(mo, mapobjectscale); + zpos += 35 * mo->scale * flip; + break; + case 1: + P_SetMobjState(mo, S_OVERTIME_LASER); + + if (leveltime & 1) + mo->frame += 3; + else + mo->frame += (leveltime / 2) % 3; + + //P_SetScale(mo, scale); + zpos += 346 * mo->scale * flip; + + if (battleovertime.enabled < 10*TICRATE) + mo->drawflags |= MFD_TRANS50; + break; + case 2: + P_SetMobjState(mo, S_OVERTIME_BULB2); + + if (leveltime & 1) + mo->frame += 1; + + //P_SetScale(mo, mapobjectscale); + break; + default: + I_Error("Bruh moment has occured\n"); + return; + } + } + } +} void K_RunBattleOvertime(void) { - UINT16 i, j; - if (battleovertime.enabled < 10*TICRATE) { battleovertime.enabled++; @@ -404,70 +688,37 @@ void K_RunBattleOvertime(void) if (battleovertime.enabled == 10*TICRATE) S_StartSound(NULL, sfx_kc40); } - else + else if (battleovertime.radius > 0) { - if (battleovertime.radius > battleovertime.minradius) - battleovertime.radius -= mapobjectscale; + if (battleovertime.radius > 2*mapobjectscale) + battleovertime.radius -= 2*mapobjectscale; else - battleovertime.radius = battleovertime.minradius; + battleovertime.radius = 0; } - if (leveltime & 1) + if (battleovertime.radius > 0) { - UINT8 transparency = tr_trans50; + const fixed_t pi = (22 * FRACUNIT) / 7; // loose approximation, this doesn't need to be incredibly precise + const INT32 orbs = 32; + const angle_t angoff = ANGLE_MAX / orbs; + const UINT8 spriteSpacing = 128; - if (!splitscreen && players[displayplayers[0]].mo) - { - INT32 dist = P_AproxDistance(battleovertime.x-players[displayplayers[0]].mo->x, battleovertime.y-players[displayplayers[0]].mo->y); - transparency = max(0, NUMTRANSMAPS - ((256 + (dist>>FRACBITS)) / 256)); - } + fixed_t circumference = FixedMul(pi, battleovertime.radius * 2); + fixed_t scale = max(circumference / spriteSpacing / orbs, mapobjectscale); - if (transparency < NUMTRANSMAPS) - { - mobj_t *beam = P_SpawnMobj(battleovertime.x, battleovertime.y, battleovertime.z + (mobjinfo[MT_RANDOMITEM].height/2), MT_OVERTIMEBEAM); - P_SetScale(beam, beam->scale*2); - if (transparency > 0) - beam->frame |= transparency<>FRACBITS / 2));*/ - - for (i = 0; i < 16; i++) - { - j = 0; - while (j < 32) // max attempts - { - fixed_t x = battleovertime.x + ((P_RandomRange(-64,64) * 128)<player) { - if (t2->player->powers[pw_flashing] + if ((t2->player->powers[pw_flashing] > 0 && t2->hitlag == 0) && !(t1->type == MT_ORBINAUT || t1->type == MT_JAWZ || t1->type == MT_JAWZ_DUD)) return true; @@ -58,11 +58,6 @@ boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) || t2->type == MT_BALLHOG) { // Other Item Damage - if (t2->eflags & MFE_VERTICALFLIP) - t2->z -= t2->height; - else - t2->z += t2->height; - S_StartSound(t2, t2->info->deathsound); P_KillMobj(t2, t1, t1, DMG_NORMAL); @@ -94,11 +89,6 @@ boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) if (damageitem) { // This Item Damage - if (t1->eflags & MFE_VERTICALFLIP) - t1->z -= t1->height; - else - t1->z += t1->height; - S_StartSound(t1, t1->info->deathsound); P_KillMobj(t1, t2, t2, DMG_NORMAL); @@ -133,7 +123,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) if (t2->player) { - if (t2->player->powers[pw_flashing]) + if (t2->player->powers[pw_flashing] > 0 && t2->hitlag == 0) return true; // Banana snipe! @@ -147,7 +137,6 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) } else { - // Player Damage P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL); } @@ -159,11 +148,6 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) || t2->type == MT_BALLHOG) { // Other Item Damage - if (t2->eflags & MFE_VERTICALFLIP) - t2->z -= t2->height; - else - t2->z += t2->height; - S_StartSound(t2, t2->info->deathsound); P_KillMobj(t2, t1, t1, DMG_NORMAL); @@ -184,11 +168,6 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) if (damageitem) { // This Item Damage - if (t1->eflags & MFE_VERTICALFLIP) - t1->z -= t1->height; - else - t1->z += t1->height; - S_StartSound(t1, t1->info->deathsound); P_KillMobj(t1, t2, t2, DMG_NORMAL); @@ -219,11 +198,15 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) if (!P_CanPickupItem(t2->player, 2)) return true; - if ((gametyperules & GTR_BUMPERS) && t2->player->kartstuff[k_bumper] <= 0) + if ((gametyperules & GTR_BUMPERS) && t2->player->bumpers <= 0) { - if (t2->player->kartstuff[k_comebackmode] || t2->player->kartstuff[k_comebacktimer]) +#ifdef OTHERKARMAMODES + if (t2->player->kartstuff[k_comebackmode] || t2->player->karmadelay) return true; t2->player->kartstuff[k_comebackmode] = 2; +#else + return true; +#endif } else { @@ -253,7 +236,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) if (t1->target && t1->target->player) { - if ((gametyperules & GTR_CIRCUIT) || t1->target->player->kartstuff[k_bumper] > 0) + if ((gametyperules & GTR_CIRCUIT) || t1->target->player->bumpers > 0) t2->player->kartstuff[k_eggmanblame] = t1->target->player-players; else t2->player->kartstuff[k_eggmanblame] = t2->player-players; @@ -283,15 +266,19 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2) if (t2->player) { - if (t2->player->powers[pw_flashing]) + if (t2->player->powers[pw_flashing] > 0 && t2->hitlag == 0) return true; // Bomb punting if ((t1->state >= &states[S_SSMINE1] && t1->state <= &states[S_SSMINE4]) || (t1->state >= &states[S_SSMINE_DEPLOY8] && t1->state <= &states[S_SSMINE_DEPLOY13])) + { P_KillMobj(t1, t2, t2, DMG_NORMAL); + } else + { K_PuntMine(t1, t2); + } } else if (t2->type == MT_ORBINAUT || t2->type == MT_JAWZ || t2->type == MT_JAWZ_DUD || t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_JAWZ_SHIELD) @@ -300,11 +287,6 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2) P_KillMobj(t1, t2, t2, DMG_NORMAL); // Other Item Damage - if (t2->eflags & MFE_VERTICALFLIP) - t2->z -= t2->height; - else - t2->z += t2->height; - S_StartSound(t2, t2->info->deathsound); P_KillMobj(t2, t1, t1, DMG_NORMAL); @@ -326,10 +308,17 @@ boolean K_MineExplosionCollide(mobj_t *t1, mobj_t *t2) { if (t2->player) { - if (t2->player->powers[pw_flashing]) + if (t2->player->powers[pw_flashing] > 0 && t2->hitlag == 0) return true; - P_DamageMobj(t2, t1, t1->target, 1, (t1->state == &states[S_MINEEXPLOSION1]) ? DMG_EXPLODE : DMG_NORMAL); + if (t1->state == &states[S_MINEEXPLOSION1]) + { + P_DamageMobj(t2, t1, t1->target, 1, DMG_EXPLODE); + } + else + { + P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL); + } } else if (t2->flags & MF_SHOOTABLE) { @@ -347,14 +336,16 @@ boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2) if (t2->player) { - if (t2->player->powers[pw_flashing]) + if (t2->player->powers[pw_flashing] > 0 && t2->hitlag == 0) return true; S_StartSound(NULL, sfx_bsnipe); // let all players hear it. + HU_SetCEchoFlags(0); HU_SetCEchoDuration(5); HU_DoCEcho(va("%s\\was hit by a kitchen sink.\\\\\\\\", player_names[t2->player-players])); I_OutputMsg("%s was hit by a kitchen sink.\n", player_names[t2->player-players]); + P_DamageMobj(t2, t1, t1->target, 1, DMG_INSTAKILL); P_KillMobj(t1, t2, t2, DMG_NORMAL); } diff --git a/src/k_hud.c b/src/k_hud.c index 42c7fd7c2..ec80a8f96 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -50,6 +50,7 @@ static patch_t *kp_bumperstickerwide; static patch_t *kp_capsulesticker; static patch_t *kp_capsulestickerwide; static patch_t *kp_karmasticker; +static patch_t *kp_spheresticker; static patch_t *kp_splitkarmabomb; static patch_t *kp_timeoutsticker; @@ -87,6 +88,9 @@ static patch_t *kp_rankbumper; static patch_t *kp_tinybumper[2]; static patch_t *kp_ranknobumpers; static patch_t *kp_rankcapsule; +static patch_t *kp_rankemerald; +static patch_t *kp_rankemeraldflash; +static patch_t *kp_rankemeraldback; static patch_t *kp_battlewin; static patch_t *kp_battlecool; @@ -174,6 +178,7 @@ void K_LoadKartHUDGraphics(void) kp_capsulesticker = W_CachePatchName("K_STCAPN", PU_HUDGFX); kp_capsulestickerwide = W_CachePatchName("K_STCAPW", PU_HUDGFX); kp_karmasticker = W_CachePatchName("K_STKARM", PU_HUDGFX); + kp_spheresticker = W_CachePatchName("K_STBSMT", PU_HUDGFX); kp_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX); kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX); @@ -348,6 +353,9 @@ void K_LoadKartHUDGraphics(void) kp_tinybumper[1] = W_CachePatchName("K_BLNB", PU_HUDGFX); kp_ranknobumpers = W_CachePatchName("K_NOBLNS", PU_HUDGFX); kp_rankcapsule = W_CachePatchName("K_CAPICO", PU_HUDGFX); + kp_rankemerald = W_CachePatchName("K_EMERC", PU_HUDGFX); + kp_rankemeraldflash = W_CachePatchName("K_EMERW", PU_HUDGFX); + kp_rankemeraldback = W_CachePatchName("K_EMERBK", PU_HUDGFX); // Battle graphics kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX); @@ -729,11 +737,12 @@ void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du if (options & V_SLIDEIN) { - tic_t length = TICRATE/2; + const tic_t length = TICRATE/2; + const tic_t end = (lt_endtime + length); - if (leveltime < introtime + length) + if (lt_ticker < end) { - INT32 offset = screenwidth - (((leveltime - introtime) * screenwidth) / length); + INT32 offset = screenwidth - ((lt_exitticker * screenwidth) / length); boolean slidefromright = false; if (r_splitscreen > 1) @@ -1513,7 +1522,7 @@ static boolean K_drawKartPositionFaces(void) INT32 i, j, ranklines, strank = -1; boolean completed[MAXPLAYERS]; INT32 rankplayer[MAXPLAYERS]; - INT32 bumperx, numplayersingame = 0; + INT32 bumperx, emeraldx, numplayersingame = 0; UINT8 *colormap; ranklines = 0; @@ -1594,6 +1603,7 @@ static boolean K_drawKartPositionFaces(void) if (!players[rankplayer[i]].mo) continue; bumperx = FACE_X+19; + emeraldx = FACE_X+16; if (players[rankplayer[i]].mo->color) { @@ -1607,22 +1617,35 @@ static boolean K_drawKartPositionFaces(void) if (LUA_HudEnabled(hud_battlebumpers)) { - if (gametype == GT_BATTLE && players[rankplayer[i]].kartstuff[k_bumper] > 0) + if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].bumpers > 0) { V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_tinybumper[0], colormap); - for (j = 1; j < players[rankplayer[i]].kartstuff[k_bumper]; j++) + for (j = 1; j < players[rankplayer[i]].bumpers; j++) { bumperx += 5; V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_tinybumper[1], colormap); } } - } // A new level of stupidity: checking if lua is enabled to close a bracket. :Fascinating: + } + } + + for (j = 0; j < 7; j++) + { + UINT32 emeraldFlag = (1 << j); + UINT16 emeraldColor = SKINCOLOR_CHAOSEMERALD1 + j; + + if (players[rankplayer[i]].powers[pw_emeralds] & emeraldFlag) + { + colormap = R_GetTranslationColormap(TC_DEFAULT, emeraldColor, GTC_CACHE); + V_DrawMappedPatch(emeraldx, Y+7, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_rankemerald, colormap); + emeraldx += 7; + } } if (i == strank) V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); - if (gametype == GT_BATTLE && players[rankplayer[i]].kartstuff[k_bumper] <= 0) + if (gametype == GT_BATTLE && players[rankplayer[i]].bumpers <= 0) V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers); else { @@ -1639,6 +1662,59 @@ static boolean K_drawKartPositionFaces(void) return false; } +static void K_drawKartEmeralds(void) +{ + static const INT32 emeraldOffsets[7][2] = { + {34, 0}, + {25, 8}, + {43, 8}, + {16, 0}, + {52, 0}, + {7, 8}, + {61, 8} + }; + + const INT32 startx = BASEVIDWIDTH - 77 - 8; + const INT32 starty = BASEVIDHEIGHT - 29 - 8; + + INT32 i; + + V_DrawScaledPatch(startx, starty, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, kp_rankemeraldback); + + for (i = 0; i < 7; i++) + { + UINT32 emeraldFlag = (1 << i); + UINT16 emeraldColor = SKINCOLOR_CHAOSEMERALD1 + i; + + if (stplyr->powers[pw_emeralds] & emeraldFlag) + { + boolean whiteFlash = (leveltime & 1); + UINT8 *colormap; + + if (i & 1) + { + whiteFlash = !whiteFlash; + } + + colormap = R_GetTranslationColormap(TC_DEFAULT, emeraldColor, GTC_CACHE); + V_DrawMappedPatch( + startx + emeraldOffsets[i][0], starty + emeraldOffsets[i][1], + V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, + kp_rankemerald, colormap + ); + + if (whiteFlash == true) + { + V_DrawScaledPatch( + startx + emeraldOffsets[i][0], starty + emeraldOffsets[i][1], + V_HUDTRANSHALF|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, + kp_rankemeraldflash + ); + } + } + } +} + // // HU_DrawTabRankings -- moved here to take advantage of kart stuff! // @@ -1720,11 +1796,11 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); V_DrawMappedPatch(x, y-4, 0, faceprefix[players[tab[i].num].skin][FACE_RANK], colormap); - /*if (gametype == GT_BATTLE && players[tab[i].num].kartstuff[k_bumper] > 0) -- not enough space for this + /*if (gametype == GT_BATTLE && players[tab[i].num].bumpers > 0) -- not enough space for this { INT32 bumperx = x+19; V_DrawMappedPatch(bumperx-2, y-4, 0, kp_tinybumper[0], colormap); - for (j = 1; j < players[tab[i].num].kartstuff[k_bumper]; j++) + for (j = 1; j < players[tab[i].num].bumpers; j++) { bumperx += 5; V_DrawMappedPatch(bumperx, y-4, 0, kp_tinybumper[1], colormap); @@ -1735,7 +1811,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN if (tab[i].num == whiteplayer) V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); - if (gametype == GT_BATTLE && players[tab[i].num].kartstuff[k_bumper] <= 0) + if (gametype == GT_BATTLE && players[tab[i].num].bumpers <= 0) V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); else { @@ -1903,11 +1979,7 @@ static void K_drawKartLapsAndRings(void) { // Laps V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_lapsticker); - - if (stplyr->exiting) - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, "FIN"); - else - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value)); + V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", min(stplyr->laps, cv_numlaps.value), cv_numlaps.value)); // Rings if (!uselives) @@ -1951,7 +2023,7 @@ static void K_drawKartSpeedometer(void) UINT8 labeln = 0; UINT8 numbers[3]; INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN; - UINT8 battleoffset = 0; + INT32 battleoffset = 0; if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line! { @@ -1986,7 +2058,7 @@ static void K_drawKartSpeedometer(void) numbers[2] = (convSpeed % 10); if (gametype == GT_BATTLE) - battleoffset = 8; + battleoffset = -4; V_DrawScaledPatch(LAPS_X, LAPS_Y-25 + battleoffset, V_HUDTRANS|V_SLIDEIN|splitflags, kp_speedometersticker); V_DrawScaledPatch(LAPS_X+7, LAPS_Y-25 + battleoffset, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[numbers[0]]); @@ -1995,6 +2067,36 @@ static void K_drawKartSpeedometer(void) V_DrawScaledPatch(LAPS_X+29, LAPS_Y-25 + battleoffset, V_HUDTRANS|V_SLIDEIN|splitflags, kp_speedometerlabel[labeln]); } +static void K_drawBlueSphereMeter(void) +{ + const UINT8 maxBars = 4; + const UINT8 segColors[] = {73, 64, 52, 54, 55, 35, 34, 33, 202, 180, 181, 182, 164, 165, 166, 153, 152}; + const UINT8 sphere = max(min(stplyr->spheres, 40), 0); + + UINT8 numBars = min((sphere / 10), maxBars); + UINT8 colorIndex = (sphere * sizeof(segColors)) / (40 + 1); + INT32 x = LAPS_X + 25; + UINT8 i; + + V_DrawScaledPatch(LAPS_X, LAPS_Y - 22, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN, kp_spheresticker); + + for (i = 0; i <= numBars; i++) + { + UINT8 segLen = 10; + + if (i == numBars) + { + segLen = (sphere % 10); + } + + V_DrawFill(x, LAPS_Y - 16, segLen, 3, segColors[max(colorIndex-1, 0)] | V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN); + V_DrawFill(x, LAPS_Y - 15, segLen, 1, segColors[max(colorIndex-2, 0)] | V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN); + V_DrawFill(x, LAPS_Y - 13, segLen, 3, segColors[colorIndex] | V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN); + + x += 15; + } +} + static void K_drawKartBumpersOrKarma(void) { UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); @@ -2058,37 +2160,28 @@ static void K_drawKartBumpersOrKarma(void) } else { - if (stplyr->kartstuff[k_bumper] <= 0) + INT32 maxbumper = K_StartingBumperCount(); + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankbumper, colormap); + + if (stplyr->bumpers > 9 || maxbumper > 9) { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_splitkarmabomb, colormap); - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->kartstuff[k_comebackpoints]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[2]); + UINT8 ln[2]; + ln[0] = (stplyr->bumpers / 10 % 10); + ln[1] = (stplyr->bumpers % 10); + + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + + ln[0] = ((abs(maxbumper) / 10) % 10); + ln[1] = (abs(maxbumper) % 10); + + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); } else { - INT32 maxbumper = K_StartingBumperCount(); - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankbumper, colormap); - - if (stplyr->kartstuff[k_bumper] > 9 || maxbumper > 9) - { - UINT8 ln[2]; - ln[0] = ((abs(stplyr->kartstuff[k_bumper]) / 10) % 10); - ln[1] = (abs(stplyr->kartstuff[k_bumper]) % 10); - - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - - ln[0] = ((abs(maxbumper) / 10) % 10); - ln[1] = (abs(maxbumper) % 10); - - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->kartstuff[k_bumper]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(maxbumper) % 10]); - } + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->bumpers) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(maxbumper) % 10]); } } } @@ -2104,22 +2197,14 @@ static void K_drawKartBumpersOrKarma(void) } else { - if (stplyr->kartstuff[k_bumper] <= 0) - { - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_karmasticker, colormap); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints])); - } + INT32 maxbumper = K_StartingBumperCount(); + + if (stplyr->bumpers > 9 && maxbumper > 9) + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumperstickerwide, colormap); else - { - INT32 maxbumper = K_StartingBumperCount(); + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap); - if (stplyr->kartstuff[k_bumper] > 9 && maxbumper > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumperstickerwide, colormap); - else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap); - - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], maxbumper)); - } + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper)); } } } @@ -2130,6 +2215,9 @@ static void K_drawKartWanted(void) UINT8 *colormap = NULL; INT32 basex = 0, basey = 0; + if (!splitscreen) + return; + if (stplyr != &players[displayplayers[0]]) return; @@ -2542,6 +2630,18 @@ static void K_drawKartNameTags(void) continue; } + if (ntplayer->mo->drawflags & K_GetPlayerDontDrawFlag(stplyr)) + { + // Invisible on this screen + continue; + } + + if ((gametyperules & GTR_BUMPERS) && (ntplayer->bumpers <= 0)) + { + // Dead in Battle + continue; + } + if (!P_CheckSight(stplyr->mo, ntplayer->mo)) { // Can't see @@ -2864,7 +2964,7 @@ static void K_drawKartMinimap(void) if (i != displayplayers[0] || r_splitscreen) { - if (gametype == GT_BATTLE && players[i].kartstuff[k_bumper] <= 0) + if (gametype == GT_BATTLE && players[i].bumpers <= 0) continue; if (players[i].kartstuff[k_hyudorotimer] > 0) @@ -3300,9 +3400,9 @@ static void K_drawBattleFullscreen(void) else K_drawKartFinish(); } - else if (stplyr->kartstuff[k_bumper] <= 0 && stplyr->kartstuff[k_comebacktimer] && comeback && !stplyr->spectator && drawcomebacktimer) + else if (stplyr->bumpers <= 0 && stplyr->karmadelay && comeback && !stplyr->spectator && drawcomebacktimer) { - UINT16 t = stplyr->kartstuff[k_comebacktimer]/(10*TICRATE); + UINT16 t = stplyr->karmadelay/(10*TICRATE); INT32 txoff, adjust = (r_splitscreen > 1) ? 4 : 6; // normal string is 8, kart string is 12, half of that for ease INT32 ty = (BASEVIDHEIGHT/2)+66; @@ -3332,11 +3432,11 @@ static void K_drawBattleFullscreen(void) V_DrawFixedPatch(x< 1) - V_DrawString(x-txoff, ty, 0, va("%d", stplyr->kartstuff[k_comebacktimer]/TICRATE)); + V_DrawString(x-txoff, ty, 0, va("%d", stplyr->karmadelay/TICRATE)); else { V_DrawFixedPatch(x<kartstuff[k_comebacktimer]/TICRATE)); + V_DrawKartString(x-txoff, ty, 0, va("%d", stplyr->karmadelay/TICRATE)); } } @@ -3523,7 +3623,7 @@ static void K_drawInput(void) #define BUTTH 11 #define drawbutt(xoffs, butt, symb)\ - if (stplyr->cmd.buttons & butt)\ + if (!stplyr->exiting && (cmd->buttons & butt))\ {\ offs = 2;\ col = accent1;\ @@ -3549,7 +3649,7 @@ static void K_drawInput(void) y -= 1; - if (!cmd->turning) // no turn + if (stplyr->exiting || !cmd->turning) // no turn target = 0; else // turning of multiple strengths! { @@ -3757,8 +3857,8 @@ static void K_drawDistributionDebugger(void) if (!playeringame[i] || players[i].spectator) continue; pingame++; - if (players[i].kartstuff[k_bumper] > bestbumper) - bestbumper = players[i].kartstuff[k_bumper]; + if (players[i].bumpers > bestbumper) + bestbumper = players[i].bumpers; } // lovely double loop...... @@ -3879,11 +3979,12 @@ void K_drawKartHUD(void) return; } - battlefullscreen = ((gametype == GT_BATTLE) + battlefullscreen = ((gametyperules & (GTR_BUMPERS|GTR_KARMA)) == (GTR_BUMPERS|GTR_KARMA) && (stplyr->exiting - || (stplyr->kartstuff[k_bumper] <= 0 - && stplyr->kartstuff[k_comebacktimer] - && comeback + || (stplyr->bumpers <= 0 + && stplyr->karmadelay > 0 + && stplyr->eliminated == false + && comeback == true && stplyr->playerstate == PST_LIVE))); if (!demo.title && (!battlefullscreen || r_splitscreen)) @@ -3995,6 +4096,11 @@ void K_drawKartHUD(void) if (LUA_HudEnabled(hud_gametypeinfo)) K_drawKartBumpersOrKarma(); } + + if (gametyperules & GTR_SPHERES) + { + K_drawBlueSphereMeter(); + } } // Draw the countdowns after everything else. @@ -4084,4 +4190,9 @@ void K_drawKartHUD(void) } K_DrawWaypointDebugger(); + + if (gametype == GT_BATTLE) + { + K_drawKartEmeralds(); + } } diff --git a/src/k_kart.c b/src/k_kart.c index 9f6a803da..bc91e6ea9 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -207,7 +207,7 @@ boolean K_IsPlayerLosing(player_t *player) INT32 winningpos = 1; UINT8 i, pcount = 0; - if (battlecapsules && player->kartstuff[k_bumper] <= 0) + if (battlecapsules && player->bumpers <= 0) return true; // DNF in break the capsules if (player->kartstuff[k_position] == 1) @@ -316,22 +316,22 @@ static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][2] = /*Eggman Monitor*/ { 1, 0 }, // Eggman Monitor /*Orbinaut*/ { 8, 0 }, // Orbinaut /*Jawz*/ { 8, 1 }, // Jawz - /*Mine*/ { 4, 1 }, // Mine + /*Mine*/ { 6, 1 }, // Mine /*Ballhog*/ { 2, 1 }, // Ballhog /*Self-Propelled Bomb*/ { 0, 0 }, // Self-Propelled Bomb /*Grow*/ { 2, 1 }, // Grow /*Shrink*/ { 0, 0 }, // Shrink - /*Thunder Shield*/ { 0, 0 }, // Thunder Shield - /*Bubble Shield*/ { 0, 0 }, // Bubble Shield + /*Thunder Shield*/ { 4, 0 }, // Thunder Shield + /*Bubble Shield*/ { 1, 0 }, // Bubble Shield /*Flame Shield*/ { 0, 0 }, // Flame Shield /*Hyudoro*/ { 2, 0 }, // Hyudoro /*Pogo Spring*/ { 2, 0 }, // Pogo Spring /*Super Ring*/ { 0, 0 }, // Super Ring /*Kitchen Sink*/ { 0, 0 }, // Kitchen Sink /*Sneaker x2*/ { 0, 0 }, // Sneaker x2 - /*Sneaker x3*/ { 0, 1 }, // Sneaker x3 + /*Sneaker x3*/ { 1, 1 }, // Sneaker x3 /*Banana x3*/ { 1, 0 }, // Banana x3 - /*Banana x10*/ { 0, 1 }, // Banana x10 + /*Banana x10*/ { 1, 1 }, // Banana x10 /*Orbinaut x3*/ { 2, 0 }, // Orbinaut x3 /*Orbinaut x4*/ { 1, 1 }, // Orbinaut x4 /*Jawz x2*/ { 2, 1 } // Jawz x2 @@ -456,7 +456,7 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, if (!playeringame[i] || players[i].spectator) continue; - if (!(gametyperules & GTR_BUMPERS) || players[i].kartstuff[k_bumper]) + if (!(gametyperules & GTR_BUMPERS) || players[i].bumpers) pingame++; if (players[i].exiting) @@ -735,8 +735,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) pingame++; if (players[i].exiting) dontforcespb = true; - if (players[i].kartstuff[k_bumper] > bestbumper) - bestbumper = players[i].kartstuff[k_bumper]; + if (players[i].bumpers > bestbumper) + bestbumper = players[i].bumpers; } // No forced SPB in 1v1s, it has to be randomly rolled @@ -897,7 +897,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) // SPECIAL CASE No. 4: // Being in ring debt occasionally forces Super Ring on you if you mashed - if ((gametyperules & GTR_RINGS) && mashed && player->rings < 0 && cv_superring.value) + if (!(gametyperules & GTR_SPHERES) && mashed && player->rings < 0 && cv_superring.value) { INT32 debtamount = min(20, abs(player->rings)); if (P_RandomChance((debtamount*FRACUNIT)/20)) @@ -1036,7 +1036,7 @@ fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) return FixedMul(weight, mobj->scale); } -void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) +boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) { mobj_t *fx; fixed_t momdifx, momdify; @@ -1045,16 +1045,16 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) fixed_t mass1, mass2; if (!mobj1 || !mobj2) - return; + return false; // Don't bump when you're being reborn if ((mobj1->player && mobj1->player->playerstate != PST_LIVE) || (mobj2->player && mobj2->player->playerstate != PST_LIVE)) - return; + return false; if ((mobj1->player && mobj1->player->respawn.state != RESPAWNST_NONE) || (mobj2->player && mobj2->player->respawn.state != RESPAWNST_NONE)) - return; + return false; { // Don't bump if you're flashing INT32 flash; @@ -1064,7 +1064,7 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) { if (mobj1->player->powers[pw_flashing] < flash-1) mobj1->player->powers[pw_flashing]++; - return; + return false; } flash = K_GetKartFlashing(mobj2->player); @@ -1072,7 +1072,7 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) { if (mobj2->player->powers[pw_flashing] < flash-1) mobj2->player->powers[pw_flashing]++; - return; + return false; } } @@ -1080,13 +1080,13 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) if (mobj1->player && mobj1->player->kartstuff[k_justbumped]) { mobj1->player->kartstuff[k_justbumped] = bumptime; - return; + return false; } if (mobj2->player && mobj2->player->kartstuff[k_justbumped]) { mobj2->player->kartstuff[k_justbumped] = bumptime; - return; + return false; } mass1 = K_GetMobjWeight(mobj1, mobj2); @@ -1104,8 +1104,10 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) disty = (mobj1->y + mobj2->momy*3) - (mobj2->y + mobj1->momy*3); if (distx == 0 && disty == 0) + { // if there's no distance between the 2, they're directly on top of each other, don't run this - return; + return false; + } { // Normalize distance to the sum of the two objects' radii, since in a perfect world that would be the distance at the point of collision... fixed_t dist = P_AproxDistance(distx, disty); @@ -1142,7 +1144,7 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) if (dot >= 0) { // They're moving away from each other - return; + return false; } force = FixedDiv(dot, FixedMul(distx, distx)+FixedMul(disty, disty)); @@ -1233,6 +1235,8 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) P_PlayerRingBurst(mobj2->player, 1); } } + + return true; } /** \brief Checks that the player is on an offroad subsector for realsies. Also accounts for line riding to prevent cheese. @@ -1457,8 +1461,8 @@ static void K_UpdateDraft(player_t *player) continue; #ifndef EASYDRAFTTEST - yourangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - theirangle = R_PointToAngle2(0, 0, players[i].mo->momx, players[i].mo->momy); + yourangle = K_MomentumAngle(player->mo); + theirangle = K_MomentumAngle(players[i].mo); diff = R_PointToAngle2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y) - yourangle; if (diff > ANGLE_180) @@ -2015,7 +2019,7 @@ void K_PlayPowerGloatSound(mobj_t *source) void K_MomentumToFacing(player_t *player) { - angle_t dangle = player->mo->angle - R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + angle_t dangle = player->mo->angle - K_MomentumAngle(player->mo); if (dangle > ANGLE_180) dangle = InvAngle(dangle); @@ -2044,11 +2048,11 @@ static fixed_t K_FlameShieldDashVar(INT32 val) return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); } -tic_t K_GetSpindashChargeTime(player_t *player) +INT16 K_GetSpindashChargeTime(player_t *player) { // more charge time for higher speed // Tails = 2s, Mighty = 3s, Fang = 4s, Metal = 4s - return (player->kartspeed + 4) * (TICRATE/3); + return (player->kartspeed + 4) * (TICRATE/3); } fixed_t K_GetSpindashChargeSpeed(player_t *player) @@ -2214,17 +2218,19 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) { fixed_t finalspeed; - UINT8 kartspeed = player->kartspeed; - if ((gametyperules & (GTR_BUMPERS|GTR_KARMA)) == (GTR_BUMPERS|GTR_KARMA) && player->kartstuff[k_bumper] <= 0) - kartspeed = 1; + finalspeed = K_GetKartSpeedFromStat(player->kartspeed); - finalspeed = K_GetKartSpeedFromStat(kartspeed); + if (player->spheres > 0) + { + fixed_t sphereAdd = (FRACUNIT/80); // 50% at max + finalspeed = FixedMul(finalspeed, FRACUNIT + (sphereAdd * player->spheres)); + } if (K_PlayerUsesBotMovement(player)) { // Give top speed a buff for bots, since it's a fairly weak stat without drifting - fixed_t speedmul = ((kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9 + fixed_t speedmul = ((player->kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9 if (player->botvars.rival == true) { @@ -2253,14 +2259,15 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) fixed_t K_GetKartAccel(player_t *player) { fixed_t k_accel = 32; // 36; - UINT8 kartspeed = player->kartspeed; - if ((gametyperules & (GTR_BUMPERS|GTR_KARMA)) == (GTR_BUMPERS|GTR_KARMA) && player->kartstuff[k_bumper] <= 0) - kartspeed = 1; - - //k_accel += 3 * (9 - kartspeed); // 36 - 60 - k_accel += 4 * (9 - kartspeed); // 32 - 64 + //k_accel += 3 * (9 - player->kartspeed); // 36 - 60 + k_accel += 4 * (9 - player->kartspeed); // 32 - 64 + if (player->spheres > 0) + { + fixed_t sphereAdd = (FRACUNIT/10); // 500% at max + k_accel = FixedMul(k_accel, FRACUNIT + (sphereAdd * player->spheres)); + } if (K_PlayerUsesBotMovement(player)) { @@ -2281,9 +2288,6 @@ UINT16 K_GetKartFlashing(player_t *player) tics += (tics/8) * (player->kartspeed); - if (gametype == GT_BATTLE) - tics *= 2; - return tics; } @@ -2350,6 +2354,83 @@ fixed_t K_3dKartMovement(player_t *player) return finalspeed; } +angle_t K_MomentumAngle(mobj_t *mo) +{ + if (mo->momx || mo->momy) + { + return R_PointToAngle2(0, 0, mo->momx, mo->momy); + } + else + { + return mo->angle; // default to facing angle, rather than 0 + } +} + +void K_SetHitLagForObjects(mobj_t *mo1, mobj_t *mo2, INT32 tics) +{ + boolean mo1valid = (mo1 && !P_MobjWasRemoved(mo1)); + boolean mo2valid = (mo2 && !P_MobjWasRemoved(mo2)); + + INT32 tics1 = tics; + INT32 tics2 = tics; + + if (mo1valid == true && mo2valid == true) + { + const fixed_t ticaddfactor = mapobjectscale * 8; + const INT32 mintics = tics; + + const fixed_t mo1speed = FixedHypot(FixedHypot(mo1->momx, mo1->momy), mo1->momz); + const fixed_t mo2speed = FixedHypot(FixedHypot(mo2->momx, mo2->momy), mo2->momz); + const fixed_t speeddiff = mo2speed - mo1speed; + + const fixed_t scalediff = mo2->scale - mo1->scale; + + const angle_t mo1angle = K_MomentumAngle(mo1); + const angle_t mo2angle = K_MomentumAngle(mo2); + + angle_t anglediff = mo1angle - mo2angle; + fixed_t anglemul = FRACUNIT; + + if (anglediff > ANGLE_180) + { + anglediff = InvAngle(anglediff); + } + + anglemul = FRACUNIT + (AngleFixed(anglediff) / 180); // x1.0 at 0, x1.5 at 90, x2.0 at 180 + + /* + CONS_Printf("anglemul: %f\n", FIXED_TO_FLOAT(anglemul)); + CONS_Printf("speeddiff: %f\n", FIXED_TO_FLOAT(speeddiff)); + CONS_Printf("scalediff: %f\n", FIXED_TO_FLOAT(scalediff)); + */ + + tics1 += FixedMul(speeddiff, FixedMul(anglemul, FRACUNIT + scalediff)) / ticaddfactor; + tics2 += FixedMul(-speeddiff, FixedMul(anglemul, FRACUNIT - scalediff)) / ticaddfactor; + + if (tics1 < mintics) + { + tics1 = mintics; + } + + if (tics2 < mintics) + { + tics2 = mintics; + } + } + + //CONS_Printf("tics1: %d, tics2: %d\n", tics1, tics2); + + if (mo1valid == true) + { + mo1->hitlag += tics1; + } + + if (mo2valid == true) + { + mo2->hitlag += tics2; + } +} + void K_DoInstashield(player_t *player) { mobj_t *layera; @@ -2368,88 +2449,51 @@ void K_DoInstashield(player_t *player) P_SetTarget(&layerb->target, player->mo); } -void K_BattleHitPlayer(player_t *player, player_t *victim, UINT8 points, boolean reducewanted) +void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved) { - if (reducewanted == false) - points = 1; // Force to 1 + UINT8 points = 1; + boolean trapItem = false; + + if (player == NULL || victim == NULL) + { + // Invalid player or victim + return; + } + + if (player == victim) + { + // You cannot give yourself points + return; + } + + if ((inflictor && !P_MobjWasRemoved(inflictor)) && (inflictor->type == MT_BANANA && inflictor->health > 1)) + { + trapItem = true; + } + + // Only apply score bonuses to non-bananas + if (trapItem == false) + { + if (K_IsPlayerWanted(victim)) + { + // +3 points for hitting a wanted player + points = 3; + } + else if (gametyperules & GTR_BUMPERS) + { + if ((victim->bumpers > 0) && (victim->bumpers <= bumpersRemoved)) + { + // +2 points for finishing off a player + points = 2; + } + } + } if (gametyperules & GTR_POINTLIMIT) { P_AddPlayerScore(player, points); K_SpawnBattlePoints(player, victim, points); } - - if ((gametyperules & GTR_WANTED) && (reducewanted == true)) - { - // Seems a little backwards, but the WANTED system is meant to prevent camping. - // If you don't want people to go after you, then be proactive! - player->kartstuff[k_wanted] -= wantedreduce; - victim->kartstuff[k_wanted] -= (wantedreduce/2); - } -} - -void K_RemoveBumper(player_t *player, mobj_t *inflictor, mobj_t *source) -{ - UINT8 score = 1; - boolean trapitem = false; - - if (!(gametyperules & GTR_BUMPERS)) - return; - - if (player->powers[pw_flashing] || P_PlayerInPain(player)) - return; - - if (inflictor && !P_MobjWasRemoved(inflictor)) - { - if (inflictor->type == MT_BANANA && inflictor->health <= 1) - { - trapitem = true; - } - } - - if (gametyperules & GTR_POINTLIMIT) - { - if (K_IsPlayerWanted(player)) - score = 3; - else if ((gametyperules & GTR_BUMPERS) && player->kartstuff[k_bumper] == 1) - score = 2; - } - - if (source && source->player && player != source->player) - { - K_BattleHitPlayer(source->player, player, score, trapitem); - } - - if (player->kartstuff[k_bumper] > 0) - { - if (player->kartstuff[k_bumper] == 1) - { - mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!! - P_SetTarget(&karmahitbox->target, player->mo); - karmahitbox->destscale = player->mo->scale; - P_SetScale(karmahitbox, player->mo->scale); - CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]); - } - - player->kartstuff[k_bumper]--; - - if (K_IsPlayerWanted(player)) - K_CalculateBattleWanted(); - } - - if (player->kartstuff[k_bumper] == 0) - { - player->kartstuff[k_comebacktimer] = comebacktime; - - if (player->kartstuff[k_comebackmode] == 2) - { - mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE); - S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound); - player->kartstuff[k_comebackmode] = 0; - } - } - - K_CheckBumpers(); } void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type) @@ -2562,59 +2606,149 @@ void K_DebtStingPlayer(player_t *player, mobj_t *source) P_SetPlayerMobjState(player->mo, S_KART_SPINOUT); } -void K_StealBumper(player_t *player, player_t *victim) +void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers) { - INT32 newbumper; - angle_t newangle, diff; - fixed_t newx, newy; - mobj_t *newmo; + if (!(gametyperules & GTR_BUMPERS)) + { + // Bumpers aren't being used + return; + } + + // TODO: replace all console text print-outs with a real visual + + if (player->bumpers > 0 && prevBumpers == 0) + { + if (player->kartstuff[k_comebackmode] == 2) + { + mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE); + S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound); + } + + player->kartstuff[k_comebackmode] = 0; + + if (netgame) + { + CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]); + } + } + else if (player->bumpers == 0 && prevBumpers > 0) + { + mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); + P_SetTarget(&karmahitbox->target, player->mo); + + karmahitbox->destscale = player->mo->destscale; + P_SetScale(karmahitbox, player->mo->scale); + + if (netgame) + { + CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]); + } + } + + player->karmadelay = comebacktime; + K_CalculateBattleWanted(); + K_CheckBumpers(); +} + +void K_DestroyBumpers(player_t *player, UINT8 amount) +{ + UINT8 oldBumpers = player->bumpers; if (!(gametyperules & GTR_BUMPERS)) + { return; + } - if (netgame && player->kartstuff[k_bumper] <= 0) - CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]); + amount = min(amount, player->bumpers); - newbumper = player->kartstuff[k_bumper]; - if (newbumper <= 1) - diff = 0; - else - diff = FixedAngle(360*FRACUNIT/newbumper); + if (amount == 0) + { + return; + } - newangle = player->mo->angle; - newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, 64*FRACUNIT); - newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, 64*FRACUNIT); + player->bumpers -= amount; + K_HandleBumperChanges(player, oldBumpers); +} - newmo = P_SpawnMobj(newx, newy, player->mo->z, MT_BATTLEBUMPER); - newmo->threshold = newbumper; - P_SetTarget(&newmo->tracer, victim->mo); - P_SetTarget(&newmo->target, player->mo); - newmo->angle = (diff * (newbumper-1)); - newmo->color = victim->skincolor; +void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) +{ + UINT8 oldPlayerBumpers = player->bumpers; + UINT8 oldVictimBumpers = victim->bumpers; - if (newbumper+1 < 2) - P_SetMobjState(newmo, S_BATTLEBUMPER3); - else if (newbumper+1 < 3) - P_SetMobjState(newmo, S_BATTLEBUMPER2); - else - P_SetMobjState(newmo, S_BATTLEBUMPER1); + UINT8 tookBumpers = 0; + if (!(gametyperules & GTR_BUMPERS)) + { + return; + } + + amount = min(amount, victim->bumpers); + + if (amount == 0) + { + return; + } + + while ((tookBumpers < amount) && (victim->bumpers > 0)) + { + UINT8 newbumper = player->bumpers; + + angle_t newangle, diff; + fixed_t newx, newy; + + mobj_t *newmo; + + if (newbumper <= 1) + { + diff = 0; + } + else + { + diff = FixedAngle(360*FRACUNIT/newbumper); + } + + newangle = player->mo->angle; + newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, 64*FRACUNIT); + newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, 64*FRACUNIT); + + newmo = P_SpawnMobj(newx, newy, player->mo->z, MT_BATTLEBUMPER); + newmo->threshold = newbumper; + + P_SetTarget(&newmo->tracer, victim->mo); + P_SetTarget(&newmo->target, player->mo); + + newmo->angle = (diff * (newbumper-1)); + newmo->color = victim->skincolor; + + if (newbumper+1 < 2) + { + P_SetMobjState(newmo, S_BATTLEBUMPER3); + } + else if (newbumper+1 < 3) + { + P_SetMobjState(newmo, S_BATTLEBUMPER2); + } + else + { + P_SetMobjState(newmo, S_BATTLEBUMPER1); + } + + player->bumpers++; + victim->bumpers--; + tookBumpers++; + } + + if (tookBumpers == 0) + { + // No change occured. + return; + } + + // Play steal sound S_StartSound(player->mo, sfx_3db06); - player->kartstuff[k_bumper]++; - player->kartstuff[k_comebackpoints] = 0; - player->powers[pw_flashing] = K_GetKartFlashing(player); - player->kartstuff[k_comebacktimer] = comebacktime; - - /*victim->powers[pw_flashing] = K_GetKartFlashing(victim); - victim->kartstuff[k_comebacktimer] = comebacktime;*/ - - victim->kartstuff[k_instashield] = 15; - if (cv_kartdebughuddrop.value && !modeattacking) - K_DropItems(victim); - else - K_DropHnextList(victim, false); - return; + K_HandleBumperChanges(player, oldPlayerBumpers); + K_HandleBumperChanges(victim, oldVictimBumpers); } // source is the mobj that originally threw the bomb that exploded etc. @@ -2726,7 +2860,7 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) if (!bombflashtimer && P_CheckSight(p->mo, source)) { bombflashtimer = TICRATE*2; - P_FlashPal(p, 1, 1); + P_FlashPal(p, PAL_WHITE, 1); } break; // we can break right now because quakes are global to all split players somehow. } @@ -2858,8 +2992,10 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I } th->angle = an; + th->momx = FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); th->momy = FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); + th->momz = source->momz; switch (type) { @@ -3119,7 +3255,7 @@ static void K_SpawnAIZDust(player_t *player) if (player->speed <= K_GetKartSpeed(player, false)) return; - travelangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + travelangle = K_MomentumAngle(player->mo); //S_StartSound(player->mo, sfx_s3k47); { @@ -3152,7 +3288,7 @@ void K_SpawnBoostTrail(player_t *player) if (!P_IsObjectOnGround(player->mo) || player->kartstuff[k_hyudorotimer] != 0 - || ((gametyperules & GTR_BUMPERS) && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer])) + || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0 && player->karmadelay)) return; if (player->mo->eflags & MFE_VERTICALFLIP) @@ -3163,7 +3299,7 @@ void K_SpawnBoostTrail(player_t *player) if (player->kartstuff[k_drift] != 0) travelangle = player->mo->angle; else - travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy); + travelangle = K_MomentumAngle(player->mo); for (i = 0; i < 2; i++) { @@ -3255,7 +3391,7 @@ void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent) mo->z, MT_WIPEOUTTRAIL); P_SetTarget(&dust->target, mo); - dust->angle = R_PointToAngle2(0,0,mo->momx,mo->momy); + dust->angle = K_MomentumAngle(mo); dust->destscale = mo->scale; P_SetScale(dust, mo->scale); K_FlipFromObject(dust, mo); @@ -3388,7 +3524,7 @@ void K_DriftDustHandling(mobj_t *spawner) if (P_AproxDistance(spawner->momx, spawner->momy) < 5*spawner->scale) return; - anglediff = abs((signed)(spawner->angle - R_PointToAngle2(0, 0, spawner->momx, spawner->momy))); + anglediff = abs((signed)(spawner->angle - K_MomentumAngle(spawner))); } if (anglediff > ANGLE_180) @@ -3690,7 +3826,7 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map void K_PuntMine(mobj_t *thismine, mobj_t *punter) { - angle_t fa = R_PointToAngle2(0, 0, punter->momx, punter->momy) >> ANGLETOFINESHIFT; + angle_t fa = K_MomentumAngle(punter) >> ANGLETOFINESHIFT; fixed_t z = 30*mapobjectscale + punter->momz; fixed_t spd; mobj_t *mine; @@ -3727,6 +3863,9 @@ void K_PuntMine(mobj_t *thismine, mobj_t *punter) if (!mine || P_MobjWasRemoved(mine)) return; + if (mine->threshold > 0 || mine->hitlag > 0) + return; + spd = (82 + ((gamespeed-1) * 14))*mapobjectscale; // Avg Speed is 41 in Normal mine->flags |= MF_NOCLIPTHING; @@ -3736,6 +3875,8 @@ void K_PuntMine(mobj_t *thismine, mobj_t *punter) mine->extravalue1 = 0; mine->reactiontime = mine->info->reactiontime; + K_SetHitLagForObjects(punter, mine, 5); + mine->momx = punter->momx + FixedMul(FINECOSINE(fa), spd); mine->momy = punter->momy + FixedMul(FINESINE(fa), spd); mine->momz = P_MobjFlip(mine) * z; @@ -3836,7 +3977,7 @@ static void K_DoHyudoroSteal(player_t *player) // Can steal from this player && (gametype == GT_RACE //&& players[i].kartstuff[k_position] < player->kartstuff[k_position]) - || ((gametyperules & GTR_BUMPERS) && players[i].kartstuff[k_bumper] > 0)) + || ((gametyperules & GTR_BUMPERS) && players[i].bumpers > 0)) // Has an item && (players[i].kartstuff[k_itemtype] @@ -4068,6 +4209,8 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) { thrust = FixedMul(thrust, 9*FRACUNIT/8); } + + mo->player->trickmomx = mo->player->trickmomy = mo->player->trickmomz = 0; // Reset post-hitlag momentums. } mo->momz = FixedMul(thrust, vscale); @@ -4301,6 +4444,118 @@ void K_DropHnextList(player_t *player, boolean keepshields) } } +mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount) +{ + mobj_t *drop = P_SpawnMobj(x, y, z, MT_FLOATINGITEM); + P_SetScale(drop, drop->scale>>4); + drop->destscale = (3*drop->destscale)/2; + + drop->angle = angle; + P_Thrust(drop, + FixedAngle(P_RandomFixed() * 180) + angle, + 16*mapobjectscale); + + drop->momz = flip * 3 * mapobjectscale; + if (drop->eflags & MFE_UNDERWATER) + drop->momz = (117 * drop->momz) / 200; + + if (type == 0) + { + UINT8 useodds = 0; + INT32 spawnchance[NUMKARTRESULTS]; + INT32 totalspawnchance = 0; + INT32 i; + + memset(spawnchance, 0, sizeof (spawnchance)); + + useodds = amount; + + for (i = 1; i < NUMKARTRESULTS; i++) + spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, 0, false, false, false)); + + if (totalspawnchance > 0) + { + UINT8 newType; + UINT8 newAmount; + + totalspawnchance = P_RandomKey(totalspawnchance); + for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++); + + // TODO: this is bad! + // K_KartGetItemResult requires a player + // but item roulette will need rewritten to change this + + switch (i) + { + // Special roulettes first, then the generic ones are handled by default + case KRITEM_DUALSNEAKER: // Sneaker x2 + newType = KITEM_SNEAKER; + newAmount = 2; + break; + case KRITEM_TRIPLESNEAKER: // Sneaker x3 + newType = KITEM_SNEAKER; + newAmount = 3; + break; + case KRITEM_TRIPLEBANANA: // Banana x3 + newType = KITEM_BANANA; + newAmount = 3; + break; + case KRITEM_TENFOLDBANANA: // Banana x10 + newType = KITEM_BANANA; + newAmount = 10; + break; + case KRITEM_TRIPLEORBINAUT: // Orbinaut x3 + newType = KITEM_ORBINAUT; + newAmount = 3; + break; + case KRITEM_QUADORBINAUT: // Orbinaut x4 + newType = KITEM_ORBINAUT; + newAmount = 4; + break; + case KRITEM_DUALJAWZ: // Jawz x2 + newType = KITEM_JAWZ; + newAmount = 2; + break; + default: + newType = i; + newAmount = 1; + break; + } + + if (newAmount > 1) + { + UINT8 j; + + for (j = 0; j < newAmount-1; j++) + { + K_CreatePaperItem( + x, y, z, + angle, flip, + newType, 1 + ); + } + } + + drop->threshold = newType; + drop->movecount = 1; + } + else + { + drop->threshold = 1; + drop->movecount = 1; + } + } + else + { + drop->threshold = type; + drop->movecount = amount; + } + + drop->flags |= MF_NOCLIPTHING; + + return drop; +} + // For getting EXTRA hit! void K_DropItems(player_t *player) { @@ -4308,24 +4563,13 @@ void K_DropItems(player_t *player) if (player->mo && !P_MobjWasRemoved(player->mo) && player->kartstuff[k_itemamount] > 0) { - mobj_t *drop = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FLOATINGITEM); - P_SetScale(drop, drop->scale>>4); - drop->destscale = (3*drop->destscale)/2; - - drop->angle = player->mo->angle + ANGLE_90; - P_Thrust(drop, - FixedAngle(P_RandomFixed()*180) + player->mo->angle + ANGLE_90, - 16*mapobjectscale); - drop->momz = P_MobjFlip(player->mo)*3*mapobjectscale; - if (drop->eflags & MFE_UNDERWATER) - drop->momz = (117 * drop->momz) / 200; - - drop->threshold = player->kartstuff[k_itemtype]; - drop->movecount = player->kartstuff[k_itemamount]; + mobj_t *drop = K_CreatePaperItem( + player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, + player->mo->angle + ANGLE_90, P_MobjFlip(player->mo), + player->kartstuff[k_itemtype], player->kartstuff[k_itemamount] + ); K_FlipFromObject(drop, player->mo); - - drop->flags |= MF_NOCLIPTHING; } K_StripItems(player); @@ -4556,10 +4800,7 @@ static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z } //mobj->standingslope = slope; - -#ifdef HWRENDER - mobj->modeltilt = slope; -#endif + P_SetPitchRollFromSlope(mobj, slope); } // Move the hnext chain! @@ -4852,9 +5093,10 @@ static void K_MoveHeldObjects(player_t *player) P_TeleportMove(cur, targx, targy, targz); K_FlipFromObject(cur, player->mo); // Update graviflip in real time thanks. -#ifdef HWRENDER - cur->modeltilt = player->mo->modeltilt; -#endif + + cur->roll = player->mo->roll; + cur->pitch = player->mo->pitch; + num = (num+1) % 2; cur = cur->hnext; } @@ -4908,7 +5150,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source) thisang = InvAngle(thisang); // Jawz only go after the person directly ahead of you in race... sort of literally now! - if (gametype == GT_RACE) + if (gametyperules & GTR_CIRCUIT) { // Don't go for people who are behind you if (thisang > ANGLE_67h) @@ -4932,7 +5174,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source) continue; // Don't pay attention to dead players - if (player->kartstuff[k_bumper] <= 0) + if (player->bumpers <= 0) continue; // Z pos too high/low @@ -5209,7 +5451,7 @@ void K_KartPlayerHUDUpdate(player_t *player) player->karthud[khud_ringspblock] = (leveltime % 14); // reset to normal anim next time } - if ((gametyperules & GTR_BUMPERS) && (player->exiting || player->kartstuff[k_comebacktimer])) + if ((gametyperules & GTR_BUMPERS) && (player->exiting || player->karmadelay)) { if (player->exiting) { @@ -5222,9 +5464,9 @@ void K_KartPlayerHUDUpdate(player_t *player) } else { - if (player->kartstuff[k_comebacktimer] < 6*TICRATE) + if (player->karmadelay < 6*TICRATE) player->karthud[khud_cardanimation] -= ((164-player->karthud[khud_cardanimation])/8)+1; - else if (player->kartstuff[k_comebacktimer] < 9*TICRATE) + else if (player->karmadelay < 9*TICRATE) player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; } @@ -5352,7 +5594,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) MT_FASTLINE); P_SetTarget(&fast->target, player->mo); - fast->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + fast->angle = K_MomentumAngle(player->mo); fast->momx = 3*player->mo->momx/4; fast->momy = 3*player->mo->momy/4; fast->momz = 3*player->mo->momz/4; @@ -5570,14 +5812,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_wipeoutslow] >= 1) player->mo->friction = ORIG_FRICTION; player->kartstuff[k_wipeoutslow] = 0; - if (!comeback) - player->kartstuff[k_comebacktimer] = comebacktime; - else if (player->kartstuff[k_comebacktimer]) - { - player->kartstuff[k_comebacktimer]--; - if (P_IsDisplayPlayer(player) && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer] <= 0) - comebackshowninfo = true; // client has already seen the message - } } if (player->rings > 20) @@ -5585,6 +5819,34 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) else if (player->rings < -20) player->rings = -20; + if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0)) + { + // Deplete 1 every tic when removed from the game. + player->spheres--; + } + else + { + // Deplete 1 every second when playing. + if ((leveltime % TICRATE) == 0) + player->spheres--; + } + + if (player->spheres > 40) + player->spheres = 40; + else if (player->spheres < 0) + player->spheres = 0; + + if (comeback == false || !(gametyperules & GTR_KARMA) || player->eliminated == true) + { + player->karmadelay = comebacktime; + } + else if (player->karmadelay > 0 && !P_PlayerInPain(player)) + { + player->karmadelay--; + if (P_IsDisplayPlayer(player) && player->bumpers <= 0 && player->karmadelay <= 0) + comebackshowninfo = true; // client has already seen the message + } + if (player->kartstuff[k_ringdelay]) player->kartstuff[k_ringdelay]--; @@ -5699,26 +5961,25 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_KartPlayerHUDUpdate(player); - if ((gametyperules & GTR_BUMPERS) && player->kartstuff[k_bumper] > 0 && !P_PlayerInPain(player) && !player->powers[pw_flashing]) + if ((gametyperules & GTR_WANTED) && player->bumpers > 0 && !P_PlayerInPain(player) && !player->powers[pw_flashing]) { player->kartstuff[k_wanted]++; - if (battleovertime.enabled >= 10*TICRATE) + } + + if ((battleovertime.enabled >= 10*TICRATE) && (player->eliminated == false)) + { + fixed_t distanceToBarrier = 0; + + if (battleovertime.radius > 0) { - if (P_AproxDistance(player->mo->x - battleovertime.x, player->mo->y - battleovertime.y) > battleovertime.radius) - { - player->kartstuff[k_killfield]++; - if (player->kartstuff[k_killfield] > 4*TICRATE) - { - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL); - //player->kartstuff[k_killfield] = 1; - } - } - else if (player->kartstuff[k_killfield] > 0) - player->kartstuff[k_killfield]--; + distanceToBarrier = R_PointToDist2(player->mo->x, player->mo->y, battleovertime.x, battleovertime.y) - (player->mo->radius * 2); + } + + if (distanceToBarrier > battleovertime.radius) + { + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_TIMEOVER); } } - else if (player->kartstuff[k_killfield] > 0) - player->kartstuff[k_killfield]--; if (P_IsObjectOnGround(player->mo)) player->kartstuff[k_waterskip] = 0; @@ -5728,7 +5989,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_eggmanexplode]) { - if (player->spectator || (gametype == GT_BATTLE && !player->kartstuff[k_bumper])) + if (player->spectator || (gametype == GT_BATTLE && !player->bumpers)) player->kartstuff[k_eggmanexplode] = 0; else { @@ -5771,7 +6032,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_FlameDashLeftoverSmoke(player->mo); } - if (player->kartstuff[k_comebacktimer]) + if (player->karmadelay) player->kartstuff[k_comebackmode] = 0; if (P_IsObjectOnGround(player->mo) && player->trickpanel != 0) @@ -5895,18 +6156,12 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { boolean finishlinehack = false; angle_t playerangle = player->mo->angle; - angle_t momangle = player->mo->angle; + angle_t momangle = K_MomentumAngle(player->mo); angle_t angletowaypoint = R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); angle_t angledelta = ANGLE_MAX; angle_t momdelta = ANGLE_MAX; - if (player->mo->momx != 0 || player->mo->momy != 0) - { - // Defaults to facing angle if you're not moving. - momangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - } - angledelta = playerangle - angletowaypoint; if (angledelta > ANGLE_180) { @@ -6352,10 +6607,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) INT32 K_GetKartDriftSparkValue(player_t *player) { - UINT8 kartspeed = (gametype == GT_BATTLE && player->kartstuff[k_bumper] <= 0) - ? 1 - : player->kartspeed; - return (26*4 + kartspeed*2 + (9 - player->kartweight))*8; + return (26*4 + player->kartspeed*2 + (9 - player->kartweight))*8; } /* @@ -6412,7 +6664,7 @@ static void K_KartDrift(player_t *player, boolean onground) { if (player->kartstuff[k_driftcharge] < 0 || player->kartstuff[k_driftcharge] >= dsone) { - angle_t pushdir = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + angle_t pushdir = K_MomentumAngle(player->mo); S_StartSound(player->mo, sfx_s23c); //K_SpawnDashDustRelease(player); @@ -6642,7 +6894,7 @@ void K_KartUpdatePosition(player_t *player) if (!playeringame[i] || players[i].spectator || !players[i].mo) continue; - if (gametype == GT_RACE) + if (gametyperules & GTR_CIRCUIT) { if (player->exiting) // End of match standings { @@ -6661,7 +6913,7 @@ void K_KartUpdatePosition(player_t *player) } } } - else if (gametype == GT_BATTLE) + else { if (player->exiting) // End of match standings { @@ -6671,11 +6923,30 @@ void K_KartUpdatePosition(player_t *player) } else { - // I have less points than but the same bumpers as this player OR - // I have less bumpers than this player - if ((players[i].kartstuff[k_bumper] == player->kartstuff[k_bumper] && players[i].marescore > player->marescore) - || (players[i].kartstuff[k_bumper] > player->kartstuff[k_bumper])) + UINT8 myEmeralds = K_NumEmeralds(player); + UINT8 yourEmeralds = K_NumEmeralds(&players[i]); + + if (yourEmeralds > myEmeralds) + { + // Emeralds matter above all position++; + } + else if (yourEmeralds == myEmeralds) + { + // Bumpers are a tie breaker + if (players[i].bumpers > player->bumpers) + { + position++; + } + else if (players[i].bumpers == player->bumpers) + { + // Score is the second tier tie breaker + if (players[i].marescore > player->marescore) + { + position++; + } + } + } } } } @@ -6775,10 +7046,67 @@ boolean K_PlayerEBrake(player_t *player) && player->powers[pw_nocontrol] == 0; } +static void K_KartSpindashDust(mobj_t *parent) +{ + fixed_t rad = FixedDiv(FixedHypot(parent->radius, parent->radius), parent->scale); + INT32 i; + + for (i = 0; i < 2; i++) + { + fixed_t hmomentum = P_RandomRange(6, 12) * parent->scale; + fixed_t vmomentum = P_RandomRange(2, 6) * parent->scale; + + angle_t ang = parent->player->drawangle + ANGLE_180; + SINT8 flip = 1; + + mobj_t *dust; + + if (i & 1) + ang -= ANGLE_45; + else + ang += ANGLE_45; + + dust = P_SpawnMobjFromMobj(parent, + FixedMul(rad, FINECOSINE(ang >> ANGLETOFINESHIFT)), + FixedMul(rad, FINESINE(ang >> ANGLETOFINESHIFT)), + 0, MT_SPINDASHDUST + ); + flip = P_MobjFlip(dust); + + dust->momx = FixedMul(hmomentum, FINECOSINE(ang >> ANGLETOFINESHIFT)); + dust->momy = FixedMul(hmomentum, FINESINE(ang >> ANGLETOFINESHIFT)); + dust->momz = vmomentum * flip; + } +} + +static void K_KartSpindashWind(mobj_t *parent) +{ + mobj_t *wind = P_SpawnMobjFromMobj(parent, + P_RandomRange(-36,36) * FRACUNIT, + P_RandomRange(-36,36) * FRACUNIT, + FixedDiv(parent->height / 2, parent->scale) + (P_RandomRange(-20,20) * FRACUNIT), + MT_SPINDASHWIND + ); + + P_SetTarget(&wind->target, parent); + + if (parent->momx || parent->momy) + wind->angle = R_PointToAngle2(0, 0, parent->momx, parent->momy); + else + wind->angle = parent->player->drawangle; + + wind->momx = 3 * parent->momx / 4; + wind->momy = 3 * parent->momy / 4; + wind->momz = 3 * parent->momz / 4; + + K_MatchGenericExtraFlags(wind, parent); +} + static void K_KartSpindash(player_t *player) { - const tic_t MAXCHARGETIME = K_GetSpindashChargeTime(player); + const INT16 MAXCHARGETIME = K_GetSpindashChargeTime(player); ticcmd_t *cmd = &player->cmd; + boolean spawnWind = (leveltime % 2 == 0); if (player->kartstuff[k_spindash] > 0 && (cmd->buttons & (BT_DRIFT|BT_BRAKE)) != (BT_DRIFT|BT_BRAKE)) { @@ -6793,7 +7121,7 @@ static void K_KartSpindash(player_t *player) mobj_t *grease; grease = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_TIREGREASE); P_SetTarget(&grease->target, player->mo); - grease->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + grease->angle = K_MomentumAngle(player->mo); grease->extravalue1 = i; } } @@ -6804,6 +7132,17 @@ static void K_KartSpindash(player_t *player) S_StartSound(player->mo, sfx_s23c); } + + if ((player->kartstuff[k_spindashboost] > 0) && (spawnWind == true)) + { + K_KartSpindashWind(player->mo); + } + + if (player->kartstuff[k_spindashboost] > (TICRATE/2)) + { + K_KartSpindashDust(player->mo); + } + if (K_PlayerEBrake(player) == false) { player->kartstuff[k_spindash] = 0; @@ -6818,26 +7157,36 @@ static void K_KartSpindash(player_t *player) if ((cmd->buttons & (BT_DRIFT|BT_BRAKE)) == (BT_DRIFT|BT_BRAKE)) { INT16 chargetime = MAXCHARGETIME - ++player->kartstuff[k_spindash]; + boolean spawnOldEffect = true; + + if (chargetime <= (MAXCHARGETIME / 2)) + { + K_KartSpindashDust(player->mo); + spawnOldEffect = false; + } + + if (chargetime <= (MAXCHARGETIME / 4) && spawnWind == true) + { + K_KartSpindashWind(player->mo); + } + if (chargetime > 0) { UINT16 soundcharge = 0; UINT8 add = 0; + while ((soundcharge += ++add) < chargetime); + if (soundcharge == chargetime) { - K_SpawnDashDustRelease(player); + if (spawnOldEffect == true) + K_SpawnDashDustRelease(player); S_StartSound(player->mo, sfx_s3kab); } } else if (chargetime < -TICRATE) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL); - else { - if (player->kartstuff[k_spindash] % 4 == 0) - { - K_SpawnDashDustRelease(player); - K_FlameDashLeftoverSmoke(player->mo); - } + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL); } } } @@ -6877,12 +7226,6 @@ void K_AdjustPlayerFriction(player_t *player) } */ - // Karma ice physics - if (gametype == GT_BATTLE && player->kartstuff[k_bumper] <= 0) - { - player->mo->friction += 1228; - } - // Water gets ice physics too if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) { @@ -7002,7 +7345,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) newitem->fuse = 15*TICRATE; // selected randomly. player->kartstuff[k_comebackmode] = 0; - player->kartstuff[k_comebacktimer] = comebacktime; + player->karmadelay = comebacktime; S_StartSound(player->mo, sfx_s254); } } @@ -7437,7 +7780,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (!onground) { P_Thrust( - player->mo, R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy), + player->mo, K_MomentumAngle(player->mo), FixedMul(player->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) ); } @@ -7489,7 +7832,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_PlayBoostTaunt(player->mo); K_DoPogoSpring(player->mo, 32<trickpanel = 1; - player->trickdelay = TICRATE/2; + player->trickdelay = 1; player->kartstuff[k_itemamount]--; } break; @@ -7591,61 +7934,113 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->drawflags &= ~MFD_DONTDRAW; } - if (gametype == GT_BATTLE && player->kartstuff[k_bumper] <= 0) // dead in match? you da bomb + if (gametype == GT_BATTLE && player->bumpers <= 0) // dead in match? you da bomb { K_DropItems(player); //K_StripItems(player); K_StripOther(player); player->mo->drawflags |= MFD_SHADOW; - player->powers[pw_flashing] = player->kartstuff[k_comebacktimer]; + player->powers[pw_flashing] = player->karmadelay; } - else if (gametype == GT_RACE || player->kartstuff[k_bumper] > 0) + else if (gametype == GT_RACE || player->bumpers > 0) { player->mo->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); } - if (player->trickpanel == 1 && player->trickdelay <= 0) + if (player->trickpanel == 1) { const angle_t lr = ANGLE_45; - fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); + fixed_t momz = FixedDiv(player->mo->momz, mapobjectscale); // bring momz back to scale... + fixed_t speedmult = max(0, FRACUNIT - abs(momz)/TRICKMOMZRAMP); // TRICKMOMZRAMP momz is minimum speed (Should be 20) + fixed_t basespeed = P_AproxDistance(player->mo->momx, player->mo->momy); // at WORSE, keep your normal speed when tricking. + fixed_t speed = FixedMul(speedmult, P_AproxDistance(player->mo->momx, player->mo->momy)); - if (cmd->turning > 0) + // debug shit + //CONS_Printf("%d\n", player->mo->momz / mapobjectscale); + + if (player->trickdelay <= 0) { - P_InstaThrust(player->mo, player->mo->angle + lr, speed*2); - player->trickpanel = 2; - } - else if (cmd->turning < 0) - { - P_InstaThrust(player->mo, player->mo->angle - lr, speed*2); - player->trickpanel = 3; - } - else if (player->kartstuff[k_throwdir] == 1) - { - if (player->mo->momz * P_MobjFlip(player->mo) > 0) + + if (cmd->turning > 0) { + P_InstaThrust(player->mo, player->mo->angle + lr, max(basespeed, speed*5/2)); + + player->trickmomx = player->mo->momx; + player->trickmomy = player->mo->momy; + player->trickmomz = player->mo->momz; + P_InstaThrust(player->mo, 0, 0); // Sike, you have no speed :) player->mo->momz = 0; + + player->trickpanel = 2; + player->mo->hitlag = TRICKLAG; } - - P_InstaThrust(player->mo, player->mo->angle, speed*3); - player->trickpanel = 2; - } - else if (player->kartstuff[k_throwdir] == -1) - { - boolean relative = true; - - player->mo->momx /= 3; - player->mo->momy /= 3; - - if (player->mo->momz * P_MobjFlip(player->mo) <= 0) + else if (cmd->turning < 0) { - relative = false; + P_InstaThrust(player->mo, player->mo->angle - lr, max(basespeed, speed*5/2)); + + player->trickmomx = player->mo->momx; + player->trickmomy = player->mo->momy; + player->trickmomz = player->mo->momz; + P_InstaThrust(player->mo, 0, 0); // Sike, you have no speed :) + player->mo->momz = 0; + + player->trickpanel = 3; + player->mo->hitlag = TRICKLAG; } + else if (player->kartstuff[k_throwdir] == 1) + { + if (player->mo->momz * P_MobjFlip(player->mo) > 0) + { + player->mo->momz = 0; + } - P_SetObjectMomZ(player->mo, 48*FRACUNIT, relative); + P_InstaThrust(player->mo, player->mo->angle, max(basespeed, speed*3)); - player->trickpanel = 3; + player->trickmomx = player->mo->momx; + player->trickmomy = player->mo->momy; + player->trickmomz = player->mo->momz; + P_InstaThrust(player->mo, 0, 0); // Sike, you have no speed :) + player->mo->momz = 0; + + player->trickpanel = 2; + player->mo->hitlag = TRICKLAG; + } + else if (player->kartstuff[k_throwdir] == -1) + { + boolean relative = true; + + player->mo->momx /= 3; + player->mo->momy /= 3; + + if (player->mo->momz * P_MobjFlip(player->mo) <= 0) + { + relative = false; + } + + P_SetObjectMomZ(player->mo, 48*FRACUNIT, relative); + + player->trickmomx = player->mo->momx; + player->trickmomy = player->mo->momy; + player->trickmomz = player->mo->momz; + P_InstaThrust(player->mo, 0, 0); // Sike, you have no speed :) + player->mo->momz = 0; + + player->trickpanel = 3; + player->mo->hitlag = TRICKLAG; + } } } + // After hitlag, we will get here and will be able to apply the desired momentums! + else if (player->trickmomx || player->trickmomy || player->trickmomz) + { + player->mo->momx = player->trickmomx; + player->mo->momy = player->trickmomy; + player->mo->momz = player->trickmomz; + player->trickmomx = player->trickmomy = player->trickmomz = 0; + + } + + // Wait until we let go off the control stick to remove the delay if (player->trickdelay > 0) { player->trickdelay--; diff --git a/src/k_kart.h b/src/k_kart.h index 2c1236368..cadb8b772 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -27,7 +27,7 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival); INT32 K_GetShieldFromItem(INT32 item); fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against); -void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid); +boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid); void K_KartPainEnergyFling(player_t *player); void K_FlipFromObject(mobj_t *mo, mobj_t *master); void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master); @@ -39,14 +39,17 @@ void K_KartMoveAnimation(player_t *player); void K_KartPlayerHUDUpdate(player_t *player); void K_KartPlayerThink(player_t *player, ticcmd_t *cmd); void K_KartPlayerAfterThink(player_t *player); +angle_t K_MomentumAngle(mobj_t *mo); +void K_SetHitLagForObjects(mobj_t *mo1, mobj_t *mo2, INT32 tics); void K_DoInstashield(player_t *player); -void K_BattleHitPlayer(player_t *player, player_t *victim, UINT8 points, boolean reducewanted); -void K_RemoveBumper(player_t *player, mobj_t *inflictor, mobj_t *source); +void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved); void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type); void K_SquishPlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); -void K_StealBumper(player_t *player, player_t *victim); +void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers); +void K_DestroyBumpers(player_t *player, UINT8 amount); +void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle, boolean spawncenter, boolean ghostit, mobj_t *source); void K_SpawnMineExplosion(mobj_t *source, UINT8 color); UINT16 K_DriftSparkColor(player_t *player, INT32 charge); @@ -70,6 +73,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue); INT32 K_GetKartDriftSparkValue(player_t *player); void K_SpawnDriftBoostExplosion(player_t *player, int stage); void K_KartUpdatePosition(player_t *player); +mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount); void K_DropItems(player_t *player); void K_DropRocketSneaker(player_t *player); void K_DropKitchenSink(player_t *player); @@ -77,7 +81,7 @@ void K_StripItems(player_t *player); void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); boolean K_ApplyOffroad(player_t *player); -tic_t K_GetSpindashChargeTime(player_t *player); +INT16 K_GetSpindashChargeTime(player_t *player); fixed_t K_GetSpindashChargeSpeed(player_t *player); fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed); fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 0ee1a7a02..d689dc848 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -387,7 +387,8 @@ static int lib_pAproxDistance(lua_State *L) fixed_t dx = luaL_checkfixed(L, 1); fixed_t dy = luaL_checkfixed(L, 2); //HUDSAFE - lua_pushfixed(L, P_AproxDistance(dx, dy)); + LUA_Deprecated(L, "P_AproxDistance", "FixedHypot"); + lua_pushfixed(L, FixedHypot(dx, dy)); return 1; } @@ -3431,16 +3432,17 @@ static int lib_kExplodePlayer(lua_State *L) return 0; } -static int lib_kStealBumper(lua_State *L) +static int lib_kTakeBumpersFromPlayer(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *victim = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + UINT8 amount = (UINT8)luaL_optinteger(L, 3, 1); NOHUD if (!player) return LUA_ErrInvalid(L, "player_t"); if (!victim) return LUA_ErrInvalid(L, "player_t"); - K_StealBumper(player, victim); + K_TakeBumpersFromPlayer(player, victim, amount); return 0; } @@ -3917,7 +3919,7 @@ static luaL_Reg lib[] = { {"K_SpinPlayer",lib_kSpinPlayer}, {"K_SquishPlayer",lib_kSquishPlayer}, {"K_ExplodePlayer",lib_kExplodePlayer}, - {"K_StealBumper",lib_kStealBumper}, + {"K_TakeBumpersFromPlayer",lib_kTakeBumpersFromPlayer}, {"K_SpawnKartExplosion",lib_kSpawnKartExplosion}, {"K_SpawnMineExplosion",lib_kSpawnMineExplosion}, {"K_SpawnBoostTrail",lib_kSpawnBoostTrail}, diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index 2ef17841f..6e3a3d331 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -536,7 +536,6 @@ static luaL_Reg lib[] = { {"CV_StealthSet", lib_cvStealthSet}, {"CV_AddValue", lib_cvAddValue}, {"CONS_Printf", lib_consPrintf}, - {"CV_FindVar", lib_cvFindVar}, {NULL, NULL} }; diff --git a/src/lua_hook.h b/src/lua_hook.h index d99d70327..915863626 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -109,10 +109,10 @@ void LUAh_PlayerQuit(player_t *plr, kickreason_t reason); // Hook for player qui boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms); // Hook for music changes -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Allows to write to player cmd before the game does anything with them. +boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Allows to write to player cmd before the game does anything with them. void LUAh_IntermissionThinker(void); // Hook for Y_Ticker -void LUAh_VoteThinker(void); // Hook for Y_VoteTicker +void LUAh_VoteThinker(void); // Hook for Y_VoteTicker #define LUAh_ShieldSpawn(player) LUAh_PlayerHook(player, hook_ShieldSpawn) // Hook for P_SpawnShieldOrb #define LUAh_ShieldSpecial(player) LUAh_PlayerHook(player, hook_ShieldSpecial) // Hook for shield abilities @@ -128,4 +128,3 @@ boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend); // Hook for MT_ #define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname); // Hook for whether a jingle of the given music should continue playing void LUAh_GameQuit(void); // Hook for game quitting -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Hook for building player's ticcmd struct (Ported from SRB2Kart) diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 5e5a1dbc4..7e2445b0c 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -313,8 +313,6 @@ static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, in pivot[idx].x = (INT32)value; else if (ikey == 2 || (key && fastcmp(key, "y"))) pivot[idx].y = (INT32)value; - else if (ikey == 3 || (key && fastcmp(key, "rotaxis"))) - pivot[idx].rotaxis = (UINT8)value; else if (ikey == -1 && (key != NULL)) FIELDERROR("pivot key", va("invalid option %s", key)); okcool = 1; @@ -579,8 +577,6 @@ static int framepivot_get(lua_State *L) lua_pushinteger(L, framepivot->x); else if (fastcmp("y", field)) lua_pushinteger(L, framepivot->y); - else if (fastcmp("rotaxis", field)) - lua_pushinteger(L, (UINT8)framepivot->rotaxis); else return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field)); @@ -605,8 +601,6 @@ static int framepivot_set(lua_State *L) framepivot->x = luaL_checkinteger(L, 3); else if (fastcmp("y", field)) framepivot->y = luaL_checkinteger(L, 3); - else if (fastcmp("rotaxis", field)) - framepivot->rotaxis = luaL_checkinteger(L, 3); else return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field)); diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c index c6c07fc7e..621f421ea 100644 --- a/src/lua_mathlib.c +++ b/src/lua_mathlib.c @@ -14,7 +14,7 @@ //#include "fastcmp.h" #include "tables.h" #include "p_local.h" -#include "doomstat.h" // for ALL7EMERALDS +#include "doomstat.h" // for ALLCHAOSEMERALDS #include "lua_script.h" #include "lua_libs.h" @@ -162,7 +162,7 @@ static int lib_getsecspecial(lua_State *L) static int lib_all7emeralds(lua_State *L) { - lua_pushboolean(L, ALL7EMERALDS(luaL_checkinteger(L, 1))); + lua_pushboolean(L, ALLCHAOSEMERALDS(luaL_checkinteger(L, 1))); return 1; } diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index f37fb93dc..a299f9c83 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -95,7 +95,8 @@ enum mobj_e { mobj_whiteshadow, mobj_sprxoff, mobj_spryoff, - mobj_sprzoff + mobj_sprzoff, + mobj_hitlag }; static const char *const mobj_opt[] = { @@ -170,6 +171,7 @@ static const char *const mobj_opt[] = { "sprxoff", "spryoff", "sprzoff", + "hitlag", NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field]) @@ -430,6 +432,9 @@ static int mobj_get(lua_State *L) case mobj_sprzoff: lua_pushfixed(L, mo->sprzoff); break; + case mobj_hitlag: + lua_pushinteger(L, mo->hitlag); + break; default: // extra custom variables in Lua memory lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); @@ -787,6 +792,9 @@ static int mobj_set(lua_State *L) case mobj_sprzoff: mo->sprzoff = luaL_checkfixed(L, 3); break; + case mobj_hitlag: + mo->hitlag = luaL_checkinteger(L, 3); + break; default: lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 7a6e7d93e..daf1666c8 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -26,25 +26,35 @@ static int lib_iteratePlayers(lua_State *L) { INT32 i = -1; + if (lua_gettop(L) < 2) { //return luaL_error(L, "Don't call players.iterate() directly, use it as 'for player in players.iterate do end'."); lua_pushcfunction(L, lib_iteratePlayers); return 1; } + lua_settop(L, 2); lua_remove(L, 1); // state is unused. + if (!lua_isnil(L, 1)) i = (INT32)(*((player_t **)luaL_checkudata(L, 1, META_PLAYER)) - players); - for (i++; i < MAXPLAYERS; i++) + + i++; + + if (i == serverplayer) + { + return LUA_PushServerPlayer(L); + } + + for (; i < MAXPLAYERS; i++) { if (!playeringame[i]) continue; - if (!players[i].mo) - continue; LUA_PushUserdata(L, &players[i], META_PLAYER); return 1; } + return 0; } @@ -57,10 +67,10 @@ static int lib_getPlayer(lua_State *L) lua_Integer i = luaL_checkinteger(L, 2); if (i < 0 || i >= MAXPLAYERS) return luaL_error(L, "players[] index %d out of range (0 - %d)", i, MAXPLAYERS-1); + if (i == serverplayer) + return LUA_PushServerPlayer(L); if (!playeringame[i]) return 0; - if (!players[i].mo) - return 0; LUA_PushUserdata(L, &players[i], META_PLAYER); return 1; } @@ -121,8 +131,6 @@ static int lib_iterateDisplayplayers(lua_State *L) if (i > splitscreen || !playeringame[displayplayers[i]]) return 0; // Stop! There are no more players for us to go through. There will never be a player gap in displayplayers. - if (!players[displayplayers[i]].mo) - continue; LUA_PushUserdata(L, &players[displayplayers[i]], META_PLAYER); lua_pushinteger(L, i); // push this to recall what number we were on for the next function call. I suppose this also means you can retrieve the splitscreen player number with 'for p, n in displayplayers.iterate'! return 2; @@ -143,8 +151,6 @@ static int lib_getDisplayplayers(lua_State *L) return 0; if (!playeringame[displayplayers[i]]) return 0; - if (!players[displayplayers[i]].mo) - return 0; LUA_PushUserdata(L, &players[displayplayers[i]], META_PLAYER); return 1; } @@ -182,17 +188,8 @@ static int player_get(lua_State *L) lua_pushboolean(L, true); else if (fastcmp(field,"name")) lua_pushstring(L, player_names[plr-players]); - else if (fastcmp(field,"realmo")) - LUA_PushUserdata(L, plr->mo, META_MOBJ); - // Kept for backward-compatibility - // Should be fixed to work like "realmo" later else if (fastcmp(field,"mo")) - { - if (plr->spectator) - lua_pushnil(L); - else - LUA_PushUserdata(L, plr->mo, META_MOBJ); - } + LUA_PushUserdata(L, plr->mo, META_MOBJ); else if (fastcmp(field,"cmd")) LUA_PushUserdata(L, &plr->cmd, META_TICCMD); else if (fastcmp(field,"playerstate")) @@ -209,6 +206,8 @@ static int player_get(lua_State *L) lua_pushangle(L, plr->drawangle); else if (fastcmp(field,"rings")) lua_pushinteger(L, plr->rings); + else if (fastcmp(field,"spheres")) + lua_pushinteger(L, plr->spheres); else if (fastcmp(field,"powers")) LUA_PushUserdata(L, plr->powers, META_POWERS); else if (fastcmp(field,"kartstuff")) @@ -219,6 +218,12 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->trickpanel); else if (fastcmp(field,"trickdelay")) lua_pushinteger(L, plr->trickdelay); + else if (fastcmp(field,"trickmomx")) + lua_pushfixed(L, plr->trickmomx); + else if (fastcmp(field,"trickmomy")) + lua_pushfixed(L, plr->trickmomy); + else if (fastcmp(field,"trickmomz")) + lua_pushfixed(L, plr->trickmomz); else if (fastcmp(field,"pflags")) lua_pushinteger(L, plr->pflags); else if (fastcmp(field,"panim")) @@ -451,7 +456,7 @@ static int player_set(lua_State *L) if (hook_cmd_running) return luaL_error(L, "Do not alter player_t in BuildCMD code!"); - if (fastcmp(field,"mo") || fastcmp(field,"realmo")) { + if (fastcmp(field,"mo")) { mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); plr->mo->player = NULL; // remove player pointer from old mobj (newmo->player = plr)->mo = newmo; // set player pointer for new mobj, and set new mobj as the player's mobj @@ -482,6 +487,8 @@ static int player_set(lua_State *L) plr->drawangle = luaL_checkangle(L, 3); else if (fastcmp(field,"rings")) plr->rings = (INT32)luaL_checkinteger(L, 3); + else if (fastcmp(field,"spheres")) + plr->spheres = (INT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"powers")) return NOSET; else if (fastcmp(field,"pflags")) @@ -510,6 +517,12 @@ static int player_set(lua_State *L) plr->trickpanel = luaL_checkinteger(L, 3); else if (fastcmp(field,"trickdelay")) plr->trickdelay = (tic_t)luaL_checkinteger(L, 3); + else if (fastcmp(field,"trickmomx")) + plr->trickmomx = (fixed_t)luaL_checkfixed(L, 3); + else if (fastcmp(field,"trickmomy")) + plr->trickmomy = (fixed_t)luaL_checkfixed(L, 3); + else if (fastcmp(field,"trickmomz")) + plr->trickmomz = (fixed_t)luaL_checkfixed(L, 3); else if (fastcmp(field,"kartspeed")) plr->kartspeed = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"kartweight")) diff --git a/src/lua_script.c b/src/lua_script.c index 0b964e5ea..291a1936c 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -731,6 +731,14 @@ void LUA_PushUserdata(lua_State *L, void *data, const char *meta) lua_remove(L, -2); // remove LREG_VALID } +int LUA_PushServerPlayer(lua_State *L) +{ + if ((!multiplayer || !(netgame || demo.playback)) && !playeringame[serverplayer]) + return 0; + LUA_PushUserdata(L, &players[serverplayer], META_PLAYER); + return 1; +} + // When userdata is freed, use this function to remove it from Lua. void LUA_InvalidateUserdata(void *data) { diff --git a/src/lua_script.h b/src/lua_script.h index cf4efcccd..d90b5ef63 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -49,6 +49,7 @@ void LUA_DumpFile(const char *filename); fixed_t LUA_EvalMath(const char *word); void LUA_PushLightUserdata(lua_State *L, void *data, const char *meta); void LUA_PushUserdata(lua_State *L, void *data, const char *meta); +int LUA_PushServerPlayer(lua_State *L); void LUA_InvalidateUserdata(void *data); void LUA_InvalidateLevel(void); void LUA_InvalidateMapthings(void); diff --git a/src/m_cheat.c b/src/m_cheat.c index ff5f86968..0f39bb643 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -771,12 +771,13 @@ void Command_Savecheckpoint_f(void) } // Like M_GetAllEmeralds() but for console devmode junkies. -/*void Command_Getallemeralds_f(void) +/* +void Command_Getallemeralds_f(void) { REQUIRE_SINGLEPLAYER; REQUIRE_PANDORA; - emeralds = ((EMERALD7)*2)-1; + emeralds = EMERALD_ALL; CONS_Printf(M_GetText("You now have all 7 emeralds.\n")); } @@ -788,7 +789,8 @@ void Command_Resetemeralds_f(void) emeralds = 0; CONS_Printf(M_GetText("Emeralds reset to zero.\n")); -}*/ +} +*/ void Command_Devmode_f(void) { diff --git a/src/m_cond.c b/src/m_cond.c index 8ffe3c378..bfd463a7c 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -341,6 +341,12 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa UINT8 M_AnySecretUnlocked(void) { INT32 i; + +#ifdef DEVELOP + if (1) + return true; +#endif + for (i = 0; i < MAXUNLOCKABLES; ++i) { if (!unlockables[i].nocecho && unlockables[i].unlocked) @@ -376,6 +382,12 @@ UINT8 M_SecretUnlocked(INT32 type) UINT8 M_MapLocked(INT32 mapnum) { + +#ifdef DEVELOP + if (1) + return false; +#endif + if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0) return false; if (!unlockables[mapheaderinfo[mapnum-1]->unlockrequired].unlocked) diff --git a/src/m_fixed.c b/src/m_fixed.c index eb10fd5f8..09d6936f2 100644 --- a/src/m_fixed.c +++ b/src/m_fixed.c @@ -18,8 +18,10 @@ #define HAVE_SQRTF #endif #endif + #include "doomdef.h" #include "m_fixed.h" +#include "tables.h" // ANGLETOFINESHIFT #ifdef __USE_C_FIXEDMUL__ @@ -105,20 +107,34 @@ fixed_t FixedSqrt(fixed_t x) fixed_t FixedHypot(fixed_t x, fixed_t y) { - fixed_t ax, yx, yx2, yx1; - if (abs(y) > abs(x)) // |y|>|x| + // Moved the code from R_PointToDist2 to here, + // since R_PointToDist2 did the same thing, + // except less prone to overflowing. + + angle_t angle; + fixed_t dist; + + x = abs(x); + y = abs(y); + + if (y > x) { - ax = abs(y); // |y| => ax - yx = FixedDiv(x, y); // (x/y) + fixed_t temp; + + temp = x; + x = y; + y = temp; } - else // |x|>|y| - { - ax = abs(x); // |x| => ax - yx = FixedDiv(y, x); // (x/y) - } - yx2 = FixedMul(yx, yx); // (x/y)^2 - yx1 = FixedSqrt(1 * FRACUNIT + yx2); // (1 + (x/y)^2)^1/2 - return FixedMul(ax, yx1); // |x|*((1 + (x/y)^2)^1/2) + + if (!y) + return x; + + angle = (tantoangle[FixedDiv(y, x)>>DBITS] + ANGLE_90) >> ANGLETOFINESHIFT; + + // use as cosine + dist = FixedDiv(x, FINESINE(angle)); + + return dist; } vector2_t *FV2_Load(vector2_t *vec, fixed_t x, fixed_t y) diff --git a/src/m_menu.c b/src/m_menu.c index 7f6f542c6..28570c699 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6374,7 +6374,7 @@ static void M_GetAllEmeralds(INT32 choice) { (void)choice; - emeralds = ((EMERALD7)*2)-1; + emeralds = EMERALD_ALL; M_StartMessage(M_GetText("You now have all 7 emeralds.\nUse them wisely.\nWith great power comes great ring drain.\n"),NULL,MM_NOTHING); G_SetGameModified(multiplayer, true); @@ -8139,11 +8139,11 @@ static void M_ReplayTimeAttack(INT32 choice) break; case 3: // guest // srb2/replay/main/map01-guest.lmp - G_DoPlayDemo(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value))); + G_DoPlayDemo(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value))); return; } // srb2/replay/main/map01-sonic-time-best.lmp - G_DoPlayDemo(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), cv_chooseskin.string, which)); + G_DoPlayDemo(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), cv_chooseskin.string, which)); } /*else if (currentMenu == &SP_NightsReplayDef) { @@ -8165,13 +8165,13 @@ static void M_ReplayTimeAttack(INT32 choice) break; } // srb2/replay/main/map01-score-best.lmp - G_DoPlayDemo(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), which)); + G_DoPlayDemo(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), which)); }*/ } static void M_EraseGuest(INT32 choice) { - const char *rguest = va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); + const char *rguest = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); (void)choice; if (FIL_FileExists(rguest)) remove(rguest); @@ -8186,10 +8186,10 @@ static void M_EraseGuest(INT32 choice) static void M_OverwriteGuest(const char *which) { - char *rguest = Z_StrDup(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value))); + char *rguest = Z_StrDup(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value))); UINT8 *buf; size_t len; - len = FIL_ReadFile(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), cv_chooseskin.string, which), &buf); + len = FIL_ReadFile(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), cv_chooseskin.string, which), &buf); if (!len) { return; } @@ -8258,7 +8258,7 @@ static void M_SetGuestReplay(INT32 choice) M_StartMessage(M_GetText("Are you sure you want to\ndelete the guest replay data?\n\n(Press 'Y' to confirm)\n"),M_EraseGuest,MM_YESNO); return; } - if (FIL_FileExists(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)))) + if (FIL_FileExists(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)))) M_StartMessage(M_GetText("Are you sure you want to\noverwrite the guest replay data?\n\n(Press 'Y' to confirm)\n"),which,MM_YESNO); else which(0); diff --git a/src/m_swap.h b/src/m_swap.h index b44d6de8c..faa54e0b2 100644 --- a/src/m_swap.h +++ b/src/m_swap.h @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2018 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -14,30 +14,34 @@ #ifndef __M_SWAP__ #define __M_SWAP__ -// Endianess handling. -// WAD files are stored little endian. #include "endian.h" -// Little to big endian +#define SWAP_SHORT(x) ((INT16)(\ +(((UINT16)(x) & (UINT16)0x00ffU) << 8) \ +| \ +(((UINT16)(x) & (UINT16)0xff00U) >> 8))) \ + +#define SWAP_LONG(x) ((INT32)(\ +(((UINT32)(x) & (UINT32)0x000000ffUL) << 24) \ +| \ +(((UINT32)(x) & (UINT32)0x0000ff00UL) << 8) \ +| \ +(((UINT32)(x) & (UINT32)0x00ff0000UL) >> 8) \ +| \ +(((UINT32)(x) & (UINT32)0xff000000UL) >> 24))) + +// Endianess handling. +// WAD files are stored little endian. #ifdef SRB2_BIG_ENDIAN - - #define SHORT(x) ((INT16)(\ - (((UINT16)(x) & (UINT16)0x00ffU) << 8) \ - | \ - (((UINT16)(x) & (UINT16)0xff00U) >> 8))) \ - - #define LONG(x) ((INT32)(\ - (((UINT32)(x) & (UINT32)0x000000ffUL) << 24) \ - | \ - (((UINT32)(x) & (UINT32)0x0000ff00UL) << 8) \ - | \ - (((UINT32)(x) & (UINT32)0x00ff0000UL) >> 8) \ - | \ - (((UINT32)(x) & (UINT32)0xff000000UL) >> 24))) - +#define SHORT SWAP_SHORT +#define LONG SWAP_LONG +#define MSBF_SHORT(x) ((INT16)(x)) +#define MSBF_LONG(x) ((INT32)(x)) #else - #define SHORT(x) ((INT16)(x)) - #define LONG(x) ((INT32)(x)) +#define SHORT(x) ((INT16)(x)) +#define LONG(x) ((INT32)(x)) +#define MSBF_SHORT SWAP_SHORT +#define MSBF_LONG SWAP_LONG #endif // Big to little endian diff --git a/src/mserv.c b/src/mserv.c index 88cede440..c91c59167 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -68,6 +68,7 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { }; consvar_t cv_masterserver = CVAR_INIT ("masterserver", "https://ms.kartkrew.org/ms/api", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange); +consvar_t cv_rendezvousserver = CVAR_INIT ("rendezvousserver", "jart-dev.jameds.org", CV_SAVE, NULL, NULL); consvar_t cv_servername = CVAR_INIT ("servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters); consvar_t cv_server_contact = CVAR_INIT ("server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters); @@ -99,6 +100,7 @@ void AddMServCommands(void) CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_masterserver_token); CV_RegisterVar(&cv_advertise); + CV_RegisterVar(&cv_rendezvousserver); CV_RegisterVar(&cv_servername); CV_RegisterVar(&cv_server_contact); #ifdef MASTERSERVER diff --git a/src/mserv.h b/src/mserv.h index 2a0afd1b3..eb1152876 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -58,6 +58,7 @@ extern consvar_t cv_masterserver_update_rate; extern consvar_t cv_masterserver_timeout; extern consvar_t cv_masterserver_debug; extern consvar_t cv_masterserver_token; +extern consvar_t cv_rendezvousserver; extern consvar_t cv_advertise; diff --git a/src/p_enemy.c b/src/p_enemy.c index c71de030b..0cb5b7a84 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -4428,7 +4428,7 @@ static inline boolean PIT_GrenadeRing(mobj_t *thing) return true; if (thing->player && (thing->player->kartstuff[k_hyudorotimer] - || ((gametyperules & GTR_BUMPERS) && thing->player && thing->player->kartstuff[k_bumper] <= 0 && thing->player->kartstuff[k_comebacktimer]))) + || ((gametyperules & GTR_BUMPERS) && thing->player && thing->player->bumpers <= 0 && thing->player->karmadelay))) return true; // see if it went over / under @@ -4457,6 +4457,9 @@ void A_GrenadeRing(mobj_t *actor) if (actor->flags2 & MF2_DEBRIS) return; + if (actor->hitlag > 0) + return; + if (actor->state == &states[S_SSMINE_DEPLOY8]) explodedist = (3*explodedist)/2; @@ -4493,7 +4496,7 @@ static inline boolean PIT_MineExplode(mobj_t *thing) if (netgame && thing->player && thing->player->spectator) return true; - if ((gametyperules & GTR_BUMPERS) && grenade->target && grenade->target->player && grenade->target->player->kartstuff[k_bumper] <= 0 && thing == grenade->target) + if ((gametyperules & GTR_BUMPERS) && grenade->target && grenade->target->player && grenade->target->player->bumpers <= 0 && thing == grenade->target) return true; // see if it went over / under @@ -4524,6 +4527,9 @@ void A_SSMineExplode(mobj_t *actor) if (actor->flags2 & MF2_DEBRIS) return; + if (actor->hitlag > 0) + return; + type = (mobjtype_t)locvar1; // Use blockmap to check for nearby shootables @@ -8642,7 +8648,7 @@ void A_ItemPop(mobj_t *actor) if (actor->info->deathsound) S_StartSound(remains, actor->info->deathsound); - if (!((gametyperules & GTR_BUMPERS) && actor->target->player->kartstuff[k_bumper] <= 0)) + if (!((gametyperules & GTR_BUMPERS) && actor->target->player->bumpers <= 0)) actor->target->player->kartstuff[k_itemroulette] = 1; remains->flags2 &= ~MF2_AMBUSH; @@ -8676,7 +8682,7 @@ void A_JawzChase(mobj_t *actor) angle_t angledelta = actor->angle - targetangle; boolean turnclockwise = true; - if ((gametyperules & GTR_CIRCUIT)) + if (gametyperules & GTR_CIRCUIT) { const fixed_t distbarrier = FixedMul(512*mapobjectscale, FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))); const fixed_t distaway = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); @@ -8757,7 +8763,7 @@ void A_JawzChase(mobj_t *actor) if (!actor->tracer) { - actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy); + actor->angle = K_MomentumAngle(actor); } P_Thrust(actor, actor->angle, thrustamount); @@ -8887,7 +8893,7 @@ static void SpawnSPBAIZDust(mobj_t *mo, INT32 dir) if (mo->eflags & MFE_VERTICALFLIP) sz = mo->ceilingz; - travelangle = R_PointToAngle2(0, 0, mo->momx, mo->momy); + travelangle = K_MomentumAngle(mo); if (leveltime & 1 && abs(mo->z - sz) < FRACUNIT*64) { newx = mo->x + P_ReturnThrustX(mo, travelangle - (dir*ANGLE_45), FixedMul(24*FRACUNIT, mo->scale)); @@ -8917,7 +8923,7 @@ static void SpawnSPBSpeedLines(mobj_t *actor) MT_FASTLINE); P_SetTarget(&fast->target, actor); - fast->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy); + fast->angle = K_MomentumAngle(actor); fast->color = SKINCOLOR_RED; fast->colorized = true; K_MatchGenericExtraFlags(fast, actor); @@ -9708,7 +9714,7 @@ void A_ReaperThinker(mobj_t *actor) continue; player = &players[i]; - if (player && player->mo && player->kartstuff[k_bumper] && player->score >= maxscore) + if (player && player->mo && player->bumpers && player->score >= maxscore) { targetplayermo = player->mo; maxscore = player->score; diff --git a/src/p_floor.c b/src/p_floor.c index f8f7fef2d..0ad27d580 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -2371,7 +2371,7 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) P_SetThingPosition(thing); if (thing->flags & MF_SHOOTABLE) P_DamageMobj(thing, puncher, puncher, 1, DMG_NORMAL); - else if (thing->type == MT_RING || thing->type == MT_COIN || thing->type == MT_RANDOMITEM) + else if (thing->type == MT_RING || thing->type == MT_RANDOMITEM) { thing->momz = FixedMul(3*FRACUNIT, thing->scale); P_TouchSpecialThing(thing, puncher, false); diff --git a/src/p_inter.c b/src/p_inter.c index 09eb659a2..4a798d4d0 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -30,7 +30,7 @@ #include "f_finale.h" // SRB2kart -#include "k_kart.h" +#include "k_kart.h" #include "k_battle.h" #include "k_pwrlv.h" #include "k_grandprix.h" @@ -108,11 +108,13 @@ void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End) // boolean P_CanPickupItem(player_t *player, UINT8 weapon) { - if (player->exiting || mapreset) + if (player->exiting || mapreset || player->eliminated) return false; - /*if ((gametyperules & GTR_BUMPERS) && player->kartstuff[k_bumper] <= 0) // No bumpers in Match - return false;*/ +#ifndef OTHERKARMAMODES + if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) // No bumpers in Match + return false; +#endif if (weapon) { @@ -182,15 +184,24 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (heightcheck) { + fixed_t toucher_bottom = toucher->z; + fixed_t special_bottom = special->z; + + if (toucher->flags & MF_PICKUPFROMBELOW) + toucher_bottom -= toucher->height; + + if (special->flags & MF_PICKUPFROMBELOW) + special_bottom -= special->height; + if (toucher->momz < 0) { - if (toucher->z + toucher->momz > special->z + special->height) + if (toucher_bottom + toucher->momz > special->z + special->height) return; - } else if (toucher->z > special->z + special->height) + } else if (toucher_bottom > special->z + special->height) return; if (toucher->momz > 0) { - if (toucher->z + toucher->height + toucher->momz < special->z) + if (toucher->z + toucher->height + toucher->momz < special_bottom) return; - } else if (toucher->z + toucher->height < special->z) + } else if (toucher->z + toucher->height < special_bottom) return; } @@ -231,11 +242,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_SetObjectMomZ(player->mo, 12<mo, player->mo->angle, 20<kartstuff[k_itemamount] && player->kartstuff[k_itemtype] != special->threshold)) return; - if ((gametyperules & GTR_BUMPERS) && player->kartstuff[k_bumper] <= 0) + if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) return; player->kartstuff[k_itemtype] = special->threshold; @@ -252,15 +263,19 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->flags &= ~MF_SPECIAL; return; - case MT_RANDOMITEM: // SRB2kart + case MT_RANDOMITEM: if (!P_CanPickupItem(player, 1)) return; - if ((gametyperules & GTR_BUMPERS) && player->kartstuff[k_bumper] <= 0) + if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) { - if (player->kartstuff[k_comebackmode] || player->kartstuff[k_comebacktimer]) +#ifdef OTHERKARMAMODES + if (player->kartstuff[k_comebackmode] || player->karmadelay) return; player->kartstuff[k_comebackmode] = 1; +#else + return; +#endif } special->momx = special->momy = special->momz = 0; @@ -272,7 +287,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; if (player == special->target->player) return; - if (player->kartstuff[k_bumper] <= 0) + if (player->bumpers <= 0) return; if (special->target->player->exiting || player->exiting) return; @@ -280,53 +295,38 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (P_PlayerInPain(special->target->player)) return; + if (special->target->player->karmadelay > 0) + return; + +#ifdef OTHERKARMAMODES if (!special->target->player->kartstuff[k_comebackmode]) { - if (player->kartstuff[k_growshrinktimer] || player->kartstuff[k_squishedtimer] - || player->kartstuff[k_hyudorotimer] || P_PlayerInPain(player) - || player->kartstuff[k_invincibilitytimer] || player->powers[pw_flashing]) - return; - else +#endif { - mobj_t *boom = P_SpawnMobj(special->target->x, special->target->y, special->target->z, MT_BOOMEXPLODE); - UINT8 ptadd = (K_IsPlayerWanted(player) ? 2 : 1); + mobj_t *boom; + + if (P_DamageMobj(toucher, special, special->target, 1, DMG_KARMA) == false) + { + return; + } + + boom = P_SpawnMobj(special->target->x, special->target->y, special->target->z, MT_BOOMEXPLODE); boom->scale = special->target->scale; boom->destscale = special->target->scale; boom->momz = 5*FRACUNIT; + if (special->target->color) boom->color = special->target->color; else boom->color = SKINCOLOR_KETCHUP; + S_StartSound(boom, special->info->attacksound); - if (player->kartstuff[k_bumper] == 1) // If you have only one bumper left, and see if it's a 1v1 - { - INT32 numingame = 0; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].kartstuff[k_bumper] <= 0) - continue; - numingame++; - } - - if (numingame <= 2) // If so, then an extra karma point so they are 100% certain to switch places; it's annoying to end matches with a bomb kill - ptadd++; - } - - special->target->player->kartstuff[k_comebackpoints] += ptadd; - - if (ptadd > 1) - special->target->player->karthud[khud_yougotem] = 2*TICRATE; - - if (special->target->player->kartstuff[k_comebackpoints] >= 2) - K_StealBumper(special->target->player, player); - - special->target->player->kartstuff[k_comebacktimer] = comebacktime; - - P_DamageMobj(toucher, special, special->target, 1, DMG_EXPLODE); + special->target->player->karthud[khud_yougotem] = 2*TICRATE; + special->target->player->karmadelay = comebacktime; } +#ifdef OTHERKARMAMODES } else if (special->target->player->kartstuff[k_comebackmode] == 1 && P_CanPickupItem(player, 1)) { @@ -349,8 +349,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->target->player->kartstuff[k_comebackpoints]++; if (special->target->player->kartstuff[k_comebackpoints] >= 2) - K_StealBumper(special->target->player, player); - special->target->player->kartstuff[k_comebacktimer] = comebacktime; + K_StealBumper(special->target->player, player, 1); + special->target->player->karmadelay = comebacktime; player->kartstuff[k_itemroulette] = 1; player->kartstuff[k_roulettetype] = 1; @@ -362,13 +362,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) S_StartSound(poof, special->info->seesound); - if (player->kartstuff[k_bumper] == 1) // If you have only one bumper left, and see if it's a 1v1 + if (player->bumpers == 1) // If you have only one bumper left, and see if it's a 1v1 { INT32 numingame = 0; for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator || players[i].kartstuff[k_bumper] <= 0) + if (!playeringame[i] || players[i].spectator || players[i].bumpers <= 0) continue; numingame++; } @@ -384,9 +384,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->target->player->karthud[khud_yougotem] = 2*TICRATE; if (special->target->player->kartstuff[k_comebackpoints] >= 2) - K_StealBumper(special->target->player, player); + K_StealBumper(special->target->player, player, 1); - special->target->player->kartstuff[k_comebacktimer] = comebacktime; + special->target->player->karmadelay = comebacktime; K_DropItems(player); //K_StripItems(player); //K_StripOther(player); @@ -404,6 +404,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->target->player->kartstuff[k_eggmanblame] = -1; } +#endif return; case MT_SPB: if ((special->target == toucher || special->target == toucher->target) && (special->threshold > 0)) @@ -441,10 +442,25 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_DamageMobj(player->mo, special, special->target, 1, DMG_NORMAL); } return; - /*case MT_EERIEFOG: + case MT_EMERALD: + if (!P_CanPickupItem(player, 0)) + return; + + if (special->threshold > 0) + return; + + if (toucher->hitlag > 0) + return; + + player->powers[pw_emeralds] |= special->extravalue1; + K_CheckEmeralds(player); + break; + /* + case MT_EERIEFOG: special->frame &= ~FF_TRANS80; special->frame |= FF_TRANS90; - return;*/ + return; + */ case MT_SMK_MOLE: if (special->target && !P_MobjWasRemoved(special->target)) return; @@ -474,7 +490,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) S_StartSound(special, sfx_s1a2); return; case MT_CDUFO: // SRB2kart - if (special->fuse || !P_CanPickupItem(player, 1) || ((gametyperules & GTR_BUMPERS) && player->kartstuff[k_bumper] <= 0)) + if (special->fuse || !P_CanPickupItem(player, 1) || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)) return; player->kartstuff[k_itemroulette] = 1; @@ -553,6 +569,22 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; + case MT_BLUESPHERE: + if (!(P_CanPickupItem(player, 0))) + return; + + // Reached the cap, don't waste 'em! + if (player->spheres >= 40) + return; + + // Not alive + if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0)) + return; + + special->momx = special->momy = special->momz = 0; + player->spheres++; + break; + // Secret emblem thingy case MT_EMBLEM: { @@ -714,7 +746,7 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost) } // Easily make it so that overtime works offline -//#define TESTOVERTIMEINFREEPLAY +#define TESTOVERTIMEINFREEPLAY /** Checks if the level timer is over the timelimit and the round should end, * unless you are in overtime. In which case leveltime may stretch out beyond @@ -731,11 +763,10 @@ void P_CheckTimeLimit(void) if (!cv_timelimit.value) return; - if (!(multiplayer || netgame)) - return; - +#ifndef TESTOVERTIMEINFREEPLAY if (battlecapsules) // capsules override any time limit settings return; +#endif if (!(gametyperules & GTR_TIMELIMIT)) return; @@ -754,58 +785,53 @@ void P_CheckTimeLimit(void) { if (!playeringame[i] || players[i].spectator) continue; + if (foundone) { #endif // Initiate the kill zone if (!battleovertime.enabled) { - INT32 b = 0; thinker_t *th; - mobj_t *item = NULL; + mobj_t *center = NULL; - P_RespawnBattleBoxes(); // FORCE THESE TO BE RESPAWNED FOR THIS!!!!!!! - - // Find us an item box to center on. for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) { mobj_t *thismo; + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) continue; + thismo = (mobj_t *)th; - if (thismo->type != MT_RANDOMITEM) - continue; - if (thismo->threshold == 69) // Disappears - continue; - - b++; - - // Only select items that are on the ground, ignore ones in the air. Ambush flag inverts this rule. - if ((!P_IsObjectOnGround(thismo)) != (thismo->flags2 & MF2_AMBUSH)) - continue; - - if (item == NULL || (b < nummapboxes && P_RandomChance(((nummapboxes-b)*FRACUNIT)/nummapboxes))) // This is to throw off the RNG some - item = thismo; - if (b >= nummapboxes) // end early if we've found them all already + if (thismo->type == MT_OVERTIME_CENTER) + { + center = thismo; break; + } } - if (item == NULL) // no item found, could happen if every item is in the air or has ambush flag, or the map has none + if (center == NULL || P_MobjWasRemoved(center)) { - CONS_Alert(CONS_WARNING, "No usuable items for Battle overtime!\n"); - return; + CONS_Alert(CONS_WARNING, "No center point for overtime!\n"); + + battleovertime.x = 0; + battleovertime.y = 0; + battleovertime.z = 0; + } + else + { + battleovertime.x = center->x; + battleovertime.y = center->y; + battleovertime.z = center->z; } - item->threshold = 70; // Set constant respawn - battleovertime.x = item->x; - battleovertime.y = item->y; - battleovertime.z = item->z; - battleovertime.radius = 4096*mapobjectscale; - battleovertime.minradius = (cv_overtime.value == 2 ? 40 : 512) * mapobjectscale; + battleovertime.radius = 4096 * mapobjectscale; battleovertime.enabled = 1; + S_StartSound(NULL, sfx_kc47); } + return; #ifndef TESTOVERTIMEINFREEPLAY } @@ -1039,12 +1065,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget { mobj_t *mo; - //if (inflictor && (inflictor->type == MT_SHELL || inflictor->type == MT_FIREBALL)) - // P_SetTarget(&target->tracer, inflictor); - - if (G_IsSpecialStage(gamemap) && target->player && target->player->nightstime > 6) - target->player->nightstime = 6; // Just let P_Ticker take care of the rest. - if (target->flags & (MF_ENEMY|MF_BOSS)) target->momx = target->momy = target->momz = 0; @@ -1070,11 +1090,17 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SPECIAL); target->flags2 &= ~(MF2_SKULLFLY|MF2_NIGHTSPULL); target->health = 0; // This makes it easy to check if something's dead elsewhere. - target->shadowscale = 0; + + if (target->type != MT_BATTLEBUMPER) + { + target->shadowscale = 0; + } if (LUAh_MobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target)) return; + //K_SetHitLagForObjects(target, inflictor, 15); + // SRB2kart // I wish I knew a better way to do this if (target->target && target->target->player && target->target->player->mo) @@ -1191,7 +1217,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget } } - if ((gametyperules & GTR_BUMPERS)) + if (gametyperules & GTR_BUMPERS) K_CheckBumpers(); target->player->trickpanel = 0; @@ -1421,6 +1447,35 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget } break; + case MT_BATTLEBUMPER: + { + mobj_t *owner = target->target; + mobj_t *overlay; + + S_StartSound(target, sfx_kc52); + target->flags &= ~MF_NOGRAVITY; + + target->destscale = (3 * target->destscale) / 2; + target->scalespeed = FRACUNIT/100; + + if (owner && !P_MobjWasRemoved(owner)) + { + P_Thrust(target, R_PointToAngle2(owner->x, owner->y, target->x, target->y), 4 * target->scale); + } + + target->momz += (24 * target->scale) * P_MobjFlip(target); + target->fuse = 8; + + overlay = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_OVERLAY); + + P_SetTarget(&target->tracer, overlay); + P_SetTarget(&overlay->target, target); + + overlay->color = target->color; + P_SetMobjState(overlay, S_INVISIBLE); + } + break; + default: break; } @@ -1646,8 +1701,10 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou return true; } -static boolean P_KillPlayer(player_t *player, UINT8 type) +static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 type) { + (void)source; + if (player->exiting) { player->mo->destscale = 1; @@ -1655,7 +1712,7 @@ static boolean P_KillPlayer(player_t *player, UINT8 type) return false; } - K_RemoveBumper(player, NULL, NULL); + K_DestroyBumpers(player, 1); switch (type) { @@ -1668,12 +1725,12 @@ static boolean P_KillPlayer(player_t *player, UINT8 type) break; } + K_DropEmeraldsFromPlayer(player, player->powers[pw_emeralds]); + K_SetHitLagForObjects(player->mo, inflictor, 15); + player->pflags &= ~PF_SLIDING; player->powers[pw_carry] = CR_NONE; - // Get rid of shield - player->powers[pw_shield] = SH_NONE; - player->mo->color = player->skincolor; player->mo->colorized = false; @@ -1688,15 +1745,21 @@ static boolean P_KillPlayer(player_t *player, UINT8 type) if (type == DMG_TIMEOVER) { - mobj_t *boom; + if (gametyperules & GTR_CIRCUIT) + { + mobj_t *boom; - player->mo->flags |= (MF_NOGRAVITY|MF_NOCLIP); - player->mo->drawflags |= MFD_DONTDRAW; + player->mo->flags |= (MF_NOGRAVITY|MF_NOCLIP); + player->mo->drawflags |= MFD_DONTDRAW; - boom = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_FZEROBOOM); - boom->scale = player->mo->scale; - boom->angle = player->mo->angle; - P_SetTarget(&boom->target, player->mo); + boom = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_FZEROBOOM); + boom->scale = player->mo->scale; + boom->angle = player->mo->angle; + P_SetTarget(&boom->target, player->mo); + } + + K_DestroyBumpers(player, player->bumpers); + player->eliminated = true; } return true; @@ -1753,6 +1816,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da player_t *player; boolean force = false; + INT32 laglength = 10; + if (objectplacing) return false; @@ -1760,13 +1825,16 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; // Spectator handling - if (multiplayer) - { - if (damagetype != DMG_SPECTATOR && target->player && target->player->spectator) - return false; + if (damagetype != DMG_SPECTATOR && target->player && target->player->spectator) + return false; - if (source && source->player && source->player->spectator) - return false; + if (source && source->player && source->player->spectator) + return false; + + if (((damagetype & DMG_TYPEMASK) == DMG_STING) + || ((inflictor && !P_MobjWasRemoved(inflictor)) && inflictor->type == MT_BANANA && inflictor->health <= 1)) + { + laglength = 5; } // Everything above here can't be forced. @@ -1830,7 +1898,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da // Instant-Death if ((damagetype & DMG_DEATHMASK)) { - if (!P_KillPlayer(player, damagetype)) + if (!P_KillPlayer(player, inflictor, source, damagetype)) return false; } else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) @@ -1845,9 +1913,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da { if (gametyperules & GTR_BUMPERS) { - if ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1) + if ((player->bumpers <= 0 && player->karmadelay) || (player->kartstuff[k_comebackmode] == 1)) { - // No bumpers, can't be hurt + // No bumpers & in WAIT, can't be hurt K_DoInstashield(player); return false; } @@ -1880,20 +1948,60 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } } - // We successfully hit 'em! + // We successfully damaged them! Give 'em some bumpers! if (type != DMG_STING) { + UINT8 takeBumpers = 1; + + if (damagetype & DMG_STEAL) + { + takeBumpers = 2; + + if (type == DMG_KARMA) + { + takeBumpers = player->bumpers; + } + } + else + { + if (type == DMG_KARMA) + { + // Take half of their bumpers for karma comeback damage + takeBumpers = max(1, player->bumpers / 2); + } + } + if (source && source != player->mo && source->player) { K_PlayHitEmSound(source); + K_BattleAwardHit(source->player, player, inflictor, takeBumpers); + K_TakeBumpersFromPlayer(source->player, player, takeBumpers); + + if (type == DMG_KARMA) + { + // Destroy any remainder bumpers from the player for karma comeback damage + K_DestroyBumpers(player, player->bumpers); + } + if (damagetype & DMG_STEAL) { - K_StealBumper(source->player, player); + // Give them ALL of your emeralds instantly :) + source->player->powers[pw_emeralds] |= player->powers[pw_emeralds]; + player->powers[pw_emeralds] = 0; + K_CheckEmeralds(source->player); } } + else + { + K_DestroyBumpers(player, takeBumpers); + } - K_RemoveBumper(player, inflictor, source); + if (!(damagetype & DMG_STEAL)) + { + // Drop all of your emeralds + K_DropEmeraldsFromPlayer(player, player->powers[pw_emeralds]); + } } player->kartstuff[k_sneakertimer] = player->kartstuff[k_numsneakers] = 0; @@ -1908,6 +2016,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da ringburst = 0; break; case DMG_EXPLODE: + case DMG_KARMA: K_ExplodePlayer(player, inflictor, source); break; case DMG_WIPEOUT: @@ -1924,14 +2033,20 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } if (type != DMG_STING) + { player->powers[pw_flashing] = K_GetKartFlashing(player); + } P_PlayRinglossSound(player->mo); + if (ringburst > 0) + { P_PlayerRingBurst(player, ringburst); + } + K_PlayPainSound(player->mo); - if ((type == DMG_EXPLODE) || (cv_kartdebughuddrop.value && !modeattacking)) + if ((type == DMG_EXPLODE || type == DMG_KARMA) || (cv_kartdebughuddrop.value && !modeattacking)) { K_DropItems(player); } @@ -1941,6 +2056,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } player->kartstuff[k_instashield] = 15; + K_SetHitLagForObjects(target, inflictor, laglength); return true; } } @@ -1962,12 +2078,16 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (source && source->player && target) G_GhostAddHit((INT32) (source->player - players), target); + K_SetHitLagForObjects(target, inflictor, laglength); + if (target->health <= 0) { P_KillMobj(target, inflictor, source, damagetype); return true; } + //K_SetHitLagForObjects(target, inflictor, laglength); + if (player) P_ResetPlayer(target->player); else @@ -2000,7 +2120,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) fixed_t momxy = 5<x, object->y, object->z, MT_TIREGREASE); P_SetTarget(&grease->target, object); - grease->angle = R_PointToAngle2(0, 0, object->momx, object->momy); + grease->angle = K_MomentumAngle(object); grease->extravalue1 = i; } } @@ -449,7 +449,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) if (object->player) { object->player->trickpanel = 1; - object->player->trickdelay = TICRATE/2; + object->player->trickdelay = 1; } K_DoPogoSpring(object, 32<player->ctfteam != thing->player->ctfteam) { if (tmthing->scale > thing->scale + (mapobjectscale/8)) // SRB2kart - Handle squishes first! + { P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SQUISH); + } else if (thing->scale > tmthing->scale + (mapobjectscale/8)) + { P_DamageMobj(tmthing, thing, thing, 1, DMG_SQUISH); + } else if (tmthing->player->kartstuff[k_invincibilitytimer] && !thing->player->kartstuff[k_invincibilitytimer]) // SRB2kart - Then invincibility! + { P_DamageMobj(thing, tmthing, tmthing, 1, DMG_WIPEOUT); + } else if (thing->player->kartstuff[k_invincibilitytimer] && !tmthing->player->kartstuff[k_invincibilitytimer]) + { P_DamageMobj(tmthing, thing, thing, 1, DMG_WIPEOUT); + } else if ((tmthing->player->kartstuff[k_flamedash] && tmthing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD) && !(thing->player->kartstuff[k_flamedash] && thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD)) // SRB2kart - Then flame shield! + { P_DamageMobj(thing, tmthing, tmthing, 1, DMG_WIPEOUT); + } else if ((thing->player->kartstuff[k_flamedash] && thing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD) && !(tmthing->player->kartstuff[k_flamedash] && tmthing->player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD)) + { P_DamageMobj(tmthing, thing, thing, 1, DMG_WIPEOUT); + } } } @@ -1301,8 +1313,8 @@ static boolean PIT_CheckThing(mobj_t *thing) } if ((gametyperules & GTR_BUMPERS) - && ((thing->player->kartstuff[k_bumper] && !tmthing->player->kartstuff[k_bumper]) - || (tmthing->player->kartstuff[k_bumper] && !thing->player->kartstuff[k_bumper]))) + && ((thing->player->bumpers && !tmthing->player->bumpers) + || (tmthing->player->bumpers && !thing->player->bumpers))) { return true; } @@ -1329,17 +1341,13 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->player->trickpanel) P_DamageMobj(tmthing, thing, thing, 1, DMG_WIPEOUT|DMG_STEAL); } - - if ((gametyperules & GTR_BUMPERS)) + else if (thing->player->kartstuff[k_sneakertimer] && !(tmthing->player->kartstuff[k_sneakertimer]) && !(thing->player->powers[pw_flashing])) // Don't steal bumpers while intangible { - if (thing->player->kartstuff[k_sneakertimer] && !(tmthing->player->kartstuff[k_sneakertimer]) && !(thing->player->powers[pw_flashing])) // Don't steal bumpers while intangible - { - P_DamageMobj(tmthing, thing, thing, 1, DMG_WIPEOUT|DMG_STEAL); - } - else if (tmthing->player->kartstuff[k_sneakertimer] && !(thing->player->kartstuff[k_sneakertimer]) && !(tmthing->player->powers[pw_flashing])) - { - P_DamageMobj(thing, tmthing, tmthing, 1, DMG_WIPEOUT|DMG_STEAL); - } + P_DamageMobj(tmthing, thing, thing, 1, DMG_WIPEOUT|DMG_STEAL); + } + else if (tmthing->player->kartstuff[k_sneakertimer] && !(thing->player->kartstuff[k_sneakertimer]) && !(tmthing->player->powers[pw_flashing])) + { + P_DamageMobj(thing, tmthing, tmthing, 1, DMG_WIPEOUT|DMG_STEAL); } K_KartBouncing(mo1, mo2, zbounce, false); @@ -1621,6 +1629,23 @@ static boolean PIT_CheckCameraLine(line_t *ld) return true; } +static boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing) +{ + // missiles can cross uncrossable lines + if ((thing->flags & MF_MISSILE)) + return false; + else + { + return + ( + (ld->flags & ML_IMPASSABLE) || // block objects from moving through this linedef. + (thing->player && !thing->player->spectator && + ld->flags & ML_BLOCKPLAYERS) || // SRB2Kart: Only block players, not items + ((thing->flags & (MF_ENEMY|MF_BOSS)) && ld->special == 81) // case 81: block monsters only + ); + } +} + // // PIT_CheckLine // Adjusts tmfloorz and tmceilingz as lines are contacted @@ -1696,14 +1721,8 @@ static boolean PIT_CheckLine(line_t *ld) return false; } - // missiles can cross uncrossable lines - if (!(tmthing->flags & MF_MISSILE)) - { - if (ld->flags & ML_IMPASSABLE) // block objects from moving through this linedef. - return false; - if (tmthing->player && !tmthing->player->spectator && ld->flags & ML_BLOCKPLAYERS) - return false; // SRB2Kart: Only block players, not items - } + if (P_IsLineBlocking(ld, tmthing)) + return false; // set openrange, opentop, openbottom P_LineOpening(ld, tmthing); @@ -2272,7 +2291,7 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam) { subsector_t *s = R_PointInSubsector(x, y); boolean retval = true; - + UINT8 i; floatok = false; @@ -2477,6 +2496,12 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (radius < mapobjectscale) radius = mapobjectscale; + if (thing->hitlag > 0) + { + // Do not move during hitlag + return false; + } + do { if (thing->flags & MF_NOCLIP) { tryx = x; @@ -2638,9 +2663,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (thing->momz <= 0) { thing->standingslope = tmfloorslope; -#ifdef HWRENDER - thing->modeltilt = thing->standingslope; -#endif + P_SetPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) P_PlayerHitFloor(thing->player, true); @@ -2653,9 +2676,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (thing->momz >= 0) { thing->standingslope = tmceilingslope; -#ifdef HWRENDER - thing->modeltilt = thing->standingslope; -#endif + P_SetPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) P_PlayerHitFloor(thing->player, true); @@ -3167,14 +3188,8 @@ static boolean PTR_LineIsBlocking(line_t *li) if (!li->backsector) return !P_PointOnLineSide(slidemo->x, slidemo->y, li); - if (!(slidemo->flags & MF_MISSILE)) - { - if (li->flags & ML_IMPASSABLE) - return true; - - if (slidemo->player && !slidemo->player->spectator && li->flags & ML_BLOCKPLAYERS) - return true; - } + if (P_IsLineBlocking(li, slidemo)) + return true; // set openrange, opentop, openbottom P_LineOpening(li, slidemo); diff --git a/src/p_maputl.c b/src/p_maputl.c index 10115e4a1..9131c24bb 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -24,19 +24,6 @@ #include "p_slopes.h" #include "z_zone.h" -// -// P_AproxDistance -// Gives an estimation of distance (not exact) -// -fixed_t P_AproxDistance(fixed_t dx, fixed_t dy) -{ - dx = abs(dx); - dy = abs(dy); - if (dx < dy) - return dx + dy - (dx>>1); - return dx + dy - (dy>>1); -} - // // P_ClosestPointOnLine // Finds the closest point on a given line to the supplied point diff --git a/src/p_maputl.h b/src/p_maputl.h index 9349d0e53..ce4509ca9 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -41,7 +41,7 @@ typedef boolean (*traverser_t)(intercept_t *in); boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2, INT32 pflags, traverser_t ptrav); -FUNCMATH fixed_t P_AproxDistance(fixed_t dx, fixed_t dy); +#define P_AproxDistance(dx, dy) FixedHypot(dx, dy) void P_ClosestPointOnLine(fixed_t x, fixed_t y, line_t *line, vertex_t *result); void P_ClosestPointOnLine3D(const vector3_t *p, const vector3_t *line, vector3_t *result); INT32 P_PointOnLineSide(fixed_t x, fixed_t y, line_t *line); diff --git a/src/p_mobj.c b/src/p_mobj.c index 1280cbce5..30e968466 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1130,7 +1130,6 @@ fixed_t P_GetMobjGravity(mobj_t *mo) case MT_FLINGCOIN: case MT_FLINGBLUESPHERE: case MT_FLINGNIGHTSCHIP: - case MT_FLINGEMERALD: case MT_BOUNCERING: case MT_RAILRING: case MT_INFINITYRING: @@ -1158,18 +1157,19 @@ fixed_t P_GetMobjGravity(mobj_t *mo) break; case MT_WATERDROP: case MT_CYBRAKDEMON: + case MT_BATTLEBUMPER: gravityadd /= 2; break; case MT_BANANA: case MT_EGGMANITEM: case MT_SSMINE: case MT_SINK: + case MT_EMERALD: if (mo->extravalue2 > 0) + { gravityadd *= mo->extravalue2; - /* FALLTHRU */ - case MT_ORBINAUT: - case MT_JAWZ: - case MT_JAWZ_DUD: + } + gravityadd = (5*gravityadd)/2; break; case MT_KARMAFIREWORK: @@ -1216,6 +1216,26 @@ void P_CheckGravity(mobj_t *mo, boolean affect) } } +// +// P_SetPitchRollFromSlope +// +void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope) +{ + if (slope) + { + fixed_t tempz = slope->normal.z; + fixed_t tempy = slope->normal.y; + fixed_t tempx = slope->normal.x; + + mo->pitch = R_PointToAngle2(0, 0, FixedSqrt(FixedMul(tempy, tempy) + FixedMul(tempz, tempz)), tempx); + mo->roll = R_PointToAngle2(0, 0, tempz, tempy); + } + else + { + mo->pitch = mo->roll = 0; + } +} + #define STOPSPEED (FRACUNIT) // @@ -1620,8 +1640,10 @@ void P_XYMovement(mobj_t *mo) 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; @@ -1696,7 +1718,7 @@ void P_XYMovement(mobj_t *mo) if (oldslope != mo->standingslope) { // First, compare different slopes angle_t oldangle, newangle; - angle_t moveangle = R_PointToAngle2(0, 0, mo->momx, mo->momy); + angle_t moveangle = K_MomentumAngle(mo); oldangle = FixedMul((signed)oldslope->zangle, FINECOSINE((moveangle - oldslope->xydirection) >> ANGLETOFINESHIFT)); @@ -1708,9 +1730,7 @@ void P_XYMovement(mobj_t *mo) // Now compare the Zs of the different quantizations if (oldangle-newangle > ANG30 && oldangle-newangle < ANGLE_180) { // Allow for a bit of sticking - this value can be adjusted later mo->standingslope = oldslope; -#ifdef HWRENDER - mo->modeltilt = mo->standingslope; -#endif + P_SetPitchRollFromSlope(mo, mo->standingslope); P_SlopeLaunch(mo); //CONS_Printf("launched off of slope - "); @@ -1728,7 +1748,7 @@ void P_XYMovement(mobj_t *mo) P_SlopeLaunch(mo); } } else if (moved && mo->standingslope && predictedz) { - angle_t moveangle = R_PointToAngle2(0, 0, mo->momx, mo->momy); + angle_t moveangle = K_MomentumAngle(mo); angle_t newangle = FixedMul((signed)mo->standingslope->zangle, FINECOSINE((moveangle - mo->standingslope->xydirection) >> ANGLETOFINESHIFT)); /*CONS_Printf("flat to angle %f - predicted z of %f\n", @@ -2093,6 +2113,7 @@ boolean P_ZMovement(mobj_t *mo) return false; } break; + case MT_REDFLAG: case MT_BLUEFLAG: // Remove from death pits. DON'T FUCKING DESPAWN IT DAMMIT @@ -2103,19 +2124,23 @@ boolean P_ZMovement(mobj_t *mo) } break; + case MT_EMERALD: + if (P_CheckDeathPitCollide(mo)) + { + P_RemoveMobj(mo); + return false; + } + + if (mo->z <= mo->floorz || mo->z + mo->height >= mo->ceilingz) + { + // Stop when hitting the floor + mo->momx = mo->momy = 0; + } + break; + case MT_RING: // Ignore still rings - case MT_COIN: case MT_BLUESPHERE: - case MT_BOMBSPHERE: - case MT_NIGHTSCHIP: - case MT_NIGHTSSTAR: - case MT_REDTEAMRING: - case MT_BLUETEAMRING: case MT_FLINGRING: - case MT_FLINGCOIN: - case MT_FLINGBLUESPHERE: - case MT_FLINGNIGHTSCHIP: - case MT_FLINGEMERALD: // Remove flinged stuff from death pits. if (P_CheckDeathPitCollide(mo)) { @@ -2228,9 +2253,7 @@ boolean P_ZMovement(mobj_t *mo) if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM)) { mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope; -#ifdef HWRENDER - mo->modeltilt = mo->standingslope; -#endif + P_SetPitchRollFromSlope(mo, mo->standingslope); P_ReverseQuantizeMomentumToSlope(&mom, mo->standingslope); } @@ -4881,8 +4904,6 @@ static boolean P_ParticleGenSceneryThink(mobj_t *mobj) mobj->angle += mobj->movedir; } - - mobj->angle += (angle_t)mobj->movecount; } return true; @@ -5277,7 +5298,117 @@ static void P_MobjSceneryThink(mobj_t *mobj) } break; case MT_BATTLEBUMPER: - if (mobj->health > 0 && mobj->target && mobj->target->player + if (mobj->health <= 0) + { + mobj->fuse--; + + if (!S_SoundPlaying(mobj, sfx_cdfm71)) + { + S_StartSound(mobj, sfx_cdfm71); + } + + if (mobj->fuse <= 0) + { + statenum_t curState = (mobj->state - states); + + if (curState == S_BATTLEBUMPER1) + { + P_SetMobjState(mobj, S_BATTLEBUMPER_EXCRYSTALA1); + + if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer)) + { + P_SetMobjState(mobj->tracer, S_BATTLEBUMPER_EXSHELLA1); + } + + mobj->shadowscale *= 2; + mobj->fuse = 12; + } + else if (curState >= S_BATTLEBUMPER_EXCRYSTALA1 && curState <= S_BATTLEBUMPER_EXCRYSTALA4) + { + P_SetMobjState(mobj, S_BATTLEBUMPER_EXCRYSTALB1); + + if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer)) + { + P_SetMobjState(mobj->tracer, S_BATTLEBUMPER_EXSHELLB1); + } + + mobj->shadowscale *= 2; + mobj->fuse = 24; + break; + } + else if (curState >= S_BATTLEBUMPER_EXCRYSTALB1 && curState <= S_BATTLEBUMPER_EXCRYSTALB4) + { + P_SetMobjState(mobj, S_BATTLEBUMPER_EXCRYSTALC1); + + if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer)) + { + P_SetMobjState(mobj->tracer, S_BATTLEBUMPER_EXSHELLC1); + } + + mobj->shadowscale *= 2; + mobj->fuse = 32; + break; + } + else + { + const INT16 spacing = 64; + UINT8 i; + + for (i = 0; i < 10; i++) + { + mobj_t *debris = P_SpawnMobjFromMobj( + mobj, + P_RandomRange(-spacing, spacing) * FRACUNIT, + P_RandomRange(-spacing, spacing) * FRACUNIT, + P_RandomRange(-spacing, spacing) * FRACUNIT, + MT_BATTLEBUMPER_DEBRIS + ); + + P_SetScale(debris, (debris->destscale *= 2)); + debris->color = mobj->color; + + debris->momz = -debris->scale * P_MobjFlip(debris); + } + + for (i = 0; i < 2; i++) + { + mobj_t *blast = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_BATTLEBUMPER_BLAST); + + blast->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy) + ANGLE_45; + blast->destscale *= 4; + + if (i & 1) + { + blast->angle += ANGLE_90; + S_StartSound(blast, sfx_cdfm64); + } + } + + for (i = 0; i < 10; i++) + { + mobj_t *puff = P_SpawnMobjFromMobj( + mobj, + P_RandomRange(-spacing, spacing) * FRACUNIT, + P_RandomRange(-spacing, spacing) * FRACUNIT, + P_RandomRange(-spacing, spacing) * FRACUNIT, + MT_SPINDASHDUST + ); + + P_SetScale(puff, (puff->destscale *= 5)); + puff->momz = puff->scale * P_MobjFlip(puff); + + P_Thrust(puff, R_PointToAngle2(mobj->x, mobj->y, puff->x, puff->y), puff->scale); + } + + P_RemoveMobj(mobj); + return; + } + } + + break; + } + + if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player && mobj->target->health > 0 && !mobj->target->player->spectator) { fixed_t rad = 32*mobj->target->scale; @@ -5285,14 +5416,14 @@ static void P_MobjSceneryThink(mobj_t *mobj) angle_t ang, diff; if (!((mobj->target->player-players) & 1)) - ang = (FixedAngle(mobj->info->speed) * -1); + ang = -FixedAngle(mobj->info->speed); else ang = FixedAngle(mobj->info->speed); - if (mobj->target->player->kartstuff[k_bumper] <= 1) + if (mobj->target->player->bumpers <= 1) diff = 0; else - diff = FixedAngle(360*FRACUNIT/mobj->target->player->kartstuff[k_bumper]); + diff = FixedAngle(360*FRACUNIT/mobj->target->player->bumpers); ang = (ang*leveltime) + (diff * (mobj->threshold-1)); @@ -5311,51 +5442,66 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->drawflags = (mobj->target->drawflags & MFD_DONTDRAW); if (mobj->target->eflags & MFE_VERTICALFLIP) + { offz += 4*FRACUNIT; + } else + { offz -= 4*FRACUNIT; + } - if (mobj->tracer && mobj->tracer->player && mobj->tracer->player->mo + if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer) && mobj->tracer->player && mobj->tracer->health > 0 && !mobj->tracer->player->spectator) // STOLEN - mobj->color = mobj->tracer->player->skincolor; // don't do star flashing for stolen bumpers + { + mobj->color = mobj->tracer->color; + } else - mobj->color = mobj->target->color; // but do so if it belongs to you :B + { + mobj->color = mobj->target->color; + } - if (mobj->target->player->kartstuff[k_bumper] < 2) + if (mobj->target->player->bumpers < 2) P_SetMobjState(mobj, S_BATTLEBUMPER3); - else if (mobj->target->player->kartstuff[k_bumper] < 3) + else if (mobj->target->player->bumpers < 3) P_SetMobjState(mobj, S_BATTLEBUMPER2); else P_SetMobjState(mobj, S_BATTLEBUMPER1); // Shrink your items if the player shrunk too. - mobj->scale = mobj->target->scale; + P_SetScale(mobj, mobj->target->scale); P_UnsetThingPosition(mobj); { - const angle_t fa = ang>>ANGLETOFINESHIFT; + const angle_t fa = ang >> ANGLETOFINESHIFT; mobj->x = mobj->target->x + FixedMul(FINECOSINE(fa), rad); mobj->y = mobj->target->y + FixedMul(FINESINE(fa), rad); mobj->z = mobj->target->z + offz; P_SetThingPosition(mobj); } - // Was this so hard? - if (mobj->target->player->kartstuff[k_bumper] <= mobj->threshold) + if (mobj->target->player->bumpers <= mobj->threshold) { - P_RemoveMobj(mobj); - return; + // Do bumper destruction + P_KillMobj(mobj, NULL, NULL, DMG_NORMAL); + break; } } - else if ((mobj->health > 0 - && (!mobj->target || !mobj->target->player || !mobj->target->player->mo || mobj->target->health <= 0 || mobj->target->player->spectator)) - || (mobj->health <= 0 && P_IsObjectOnGround(mobj)) - || P_CheckDeathPitCollide(mobj)) // When in death state + else { + // Sliently remove P_RemoveMobj(mobj); return; } + break; + + case MT_BATTLEBUMPER_DEBRIS: + if (mobj->state == states + S_BATTLEBUMPER_EXDEBRIS2) + { + mobj->drawflags ^= MFD_DONTDRAW; + } + break; + case MT_PLAYERARROW: if (mobj->target && mobj->target->health && mobj->target->player && !mobj->target->player->spectator @@ -5366,7 +5512,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->color = mobj->target->color; K_MatchGenericExtraFlags(mobj, mobj->target); - if ((gametype == GT_RACE || mobj->target->player->kartstuff[k_bumper] <= 0) + if ((gametype == GT_RACE || mobj->target->player->bumpers <= 0) #if 1 // Set to 0 to test without needing to host || (P_IsDisplayPlayer(mobj->target->player)) #endif @@ -5623,6 +5769,9 @@ static void P_MobjSceneryThink(mobj_t *mobj) if (mobj->tics > 0) mobj->drawflags ^= MFD_DONTDRAW; break; + case MT_SPINDASHWIND: + mobj->drawflags ^= MFD_DONTDRAW; + break; case MT_VWREF: case MT_VWREB: { @@ -6139,7 +6288,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) frictionsafety = FRACUNIT; } - mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); + mobj->angle = K_MomentumAngle(mobj); if (mobj->health <= 5) { INT32 i; @@ -6194,6 +6343,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (P_MobjTouchingSectorSpecial(mobj, 3, 1, true)) K_DoPogoSpring(mobj, 0, 1); + if (!(gametyperules & GTR_CIRCUIT)) + mobj->friction = max(0, 3 * mobj->friction / 4); + break; } case MT_JAWZ_DUD: @@ -6241,7 +6393,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) thrustamount = beatfriction + FixedDiv(mobj->movefactor - currentspeed, frictionsafety); } - mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); + mobj->angle = K_MomentumAngle(mobj); P_Thrust(mobj, mobj->angle, thrustamount); if (P_MobjTouchingSectorSpecial(mobj, 3, 1, true)) @@ -6395,6 +6547,37 @@ static boolean P_MobjRegularThink(mobj_t *mobj) S_StartSound(mobj, sfx_s3k4e); mobj->health--; break; + case MT_EMERALD: + { + if (battleovertime.enabled >= 10*TICRATE) + { + fixed_t distance = R_PointToDist2(mobj->x, mobj->y, battleovertime.x, battleovertime.y); + + if (distance > battleovertime.radius) + { + // Delete emeralds to let them reappear + P_KillMobj(mobj, NULL, NULL, DMG_NORMAL); + } + } + + if (leveltime % 3 == 0) + { + mobj_t *sparkle = P_SpawnMobjFromMobj( + mobj, + P_RandomRange(-48, 48) * FRACUNIT, + P_RandomRange(-48, 48) * FRACUNIT, + P_RandomRange(0, 64) * FRACUNIT, + MT_EMERALDSPARK + ); + + sparkle->color = mobj->color; + sparkle->momz += 8 * mobj->scale * P_MobjFlip(mobj); + } + + if (mobj->threshold > 0) + mobj->threshold--; + } + break; case MT_DRIFTEXPLODE: if (!mobj->target || !mobj->target->health) { @@ -6404,7 +6587,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) //mobj->angle = mobj->target->angle; { - angle_t angle = R_PointToAngle2(0, 0, mobj->target->momx, mobj->target->momy); + angle_t angle = K_MomentumAngle(mobj->target); fixed_t nudge; mobj->angle = angle; @@ -6426,9 +6609,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->target->z); } P_SetScale(mobj, mobj->target->scale); -#ifdef HWRENDER - mobj->modeltilt = mobj->target->modeltilt; -#endif + + mobj->roll = mobj->target->roll; + mobj->pitch = mobj->target->pitch; if (mobj->fuse <= 16) { @@ -6442,7 +6625,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->color = K_RainbowColor( (SKINCOLOR_PURPLE - SKINCOLOR_PINK) // Smoothly transition into the other state + ((mobj->fuse - 32) * 2) // Make the color flashing slow down while it runs out - ); + ); switch (mobj->extravalue1) { @@ -6490,9 +6673,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) P_TeleportMove(mobj, mobj->target->x + P_ReturnThrustX(mobj, mobj->angle+ANGLE_180, mobj->target->radius), mobj->target->y + P_ReturnThrustY(mobj, mobj->angle+ANGLE_180, mobj->target->radius), mobj->target->z); P_SetScale(mobj, mobj->target->scale); -#ifdef HWRENDER - mobj->modeltilt = mobj->target->modeltilt; -#endif + + mobj->roll = mobj->target->roll; + mobj->pitch = mobj->target->pitch; { player_t *p = NULL; @@ -6672,7 +6855,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { const angle_t off = FixedAngle(40*FRACUNIT); - angle_t ang = mobj->target->angle; + angle_t ang = K_MomentumAngle(mobj->target); fixed_t z; UINT8 trans = (mobj->target->player->kartstuff[k_tiregrease] * (NUMTRANSMAPS+1)) / greasetics; @@ -6685,9 +6868,6 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (mobj->eflags & MFE_VERTICALFLIP) z += mobj->target->height; - if (mobj->target->momx || mobj->target->momy) - ang = R_PointToAngle2(0, 0, mobj->target->momx, mobj->target->momy); - if (mobj->extravalue1) ang = (signed)(ang - off); else @@ -6982,10 +7162,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } P_TeleportMove(mobj, destx, desty, mobj->target->z); - if (mobj->target->momx || mobj->target->momy) - mobj->angle = R_PointToAngle2(0, 0, mobj->target->momx, mobj->target->momy); - else - mobj->angle = mobj->target->angle; + mobj->angle = K_MomentumAngle(mobj->target); if (underlayst != S_NULL) { @@ -7022,7 +7199,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) statenum_t state = (mobj->state-states); if (!mobj->target || !mobj->target->health || !mobj->target->player || mobj->target->player->spectator - || (gametype == GT_RACE || mobj->target->player->kartstuff[k_bumper])) + || (gametype == GT_RACE || mobj->target->player->bumpers)) { P_RemoveMobj(mobj); return false; @@ -7043,11 +7220,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->radius = 24*mobj->target->scale; mobj->height = 2*mobj->radius; - if (mobj->target->player->kartstuff[k_comebacktimer] > 0) + if (mobj->target->player->karmadelay > 0) { if (state < S_PLAYERBOMB1 || state > S_PLAYERBOMB20) P_SetMobjState(mobj, S_PLAYERBOMB1); - if (mobj->target->player->kartstuff[k_comebacktimer] < TICRATE && (leveltime & 1)) + if (mobj->target->player->karmadelay < TICRATE && (leveltime & 1)) mobj->drawflags &= ~MFD_DONTDRAW; else mobj->drawflags |= MFD_DONTDRAW; @@ -7862,7 +8039,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->momx = (23*mobj->momx)/24; mobj->momy = (23*mobj->momy)/24; - mobj->angle = R_PointToAngle2(0,0,mobj->momx,mobj->momy); + mobj->angle = K_MomentumAngle(mobj); if ((mobj->z - mobj->floorz) < (24*mobj->scale) && (leveltime % 3 != 0)) { @@ -8354,6 +8531,7 @@ static boolean P_FuseThink(mobj_t *mobj) if (mobj->threshold == 70) newmobj->threshold = 70; } + P_RemoveMobj(mobj); // make sure they disappear return false; case MT_SMK_ICEBLOCK: @@ -8416,6 +8594,13 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->hprev && P_MobjWasRemoved(mobj->hprev)) P_SetTarget(&mobj->hprev, NULL); + // Don't run any thinker code while in hitlag + if (mobj->hitlag > 0) + { + mobj->hitlag--; + return; + } + mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG|MFE_JUSTBOUNCEDWALL); tmfloorthing = tmhitthing = NULL; @@ -8433,7 +8618,7 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->scale != mobj->destscale) P_MobjScaleThink(mobj); // Slowly scale up/down to reach your destscale. - if ((mobj->type == MT_GHOST || mobj->type == MT_THOK) && mobj->fuse > 0) // Not guaranteed to be MF_SCENERY or not MF_SCENERY! + if (mobj->type == MT_GHOST && mobj->fuse > 0) // Not guaranteed to be MF_SCENERY or not MF_SCENERY! { if (mobj->flags2 & MF2_BOSSNOTRAP) // "fast" flag { @@ -8577,7 +8762,7 @@ void P_MobjThinker(mobj_t *mobj) || mobj->type == MT_FLINGCOIN || mobj->type == MT_FLINGBLUESPHERE || mobj->type == MT_FLINGNIGHTSCHIP - || mobj->type == MT_FLINGEMERALD + || mobj->type == MT_EMERALD || mobj->type == MT_BIGTUMBLEWEED || mobj->type == MT_LITTLETUMBLEWEED || mobj->type == MT_CANNONBALLDECOR @@ -8909,6 +9094,8 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) break; case MT_RING: case MT_FLOATINGITEM: + case MT_BLUESPHERE: + case MT_EMERALD: thing->shadowscale = FRACUNIT/2; break; case MT_DRIFTCLIP: @@ -9017,6 +9204,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->colorized = false; + mobj->hitlag = 0; + // Set shadowscale here, before spawn hook so that Lua can change it P_DefaultMobjShadowScale(mobj); @@ -9198,8 +9387,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->color = skincolor_blueteam; break; case MT_RING: - case MT_COIN: - case MT_NIGHTSSTAR: if (nummaprings >= 0) nummaprings++; break; @@ -9557,10 +9744,7 @@ void P_RemoveMobj(mobj_t *mobj) // Rings only, please! if (mobj->spawnpoint && (mobj->type == MT_RING - || mobj->type == MT_COIN - || mobj->type == MT_NIGHTSSTAR - || mobj->type == MT_REDTEAMRING - || mobj->type == MT_BLUETEAMRING) + || mobj->type == MT_BLUESPHERE) && !(mobj->flags2 & MF2_DONTRESPAWN)) { itemrespawnque[iquehead] = mobj->spawnpoint; @@ -9959,11 +10143,11 @@ mobjtype_t P_GetMobjtype(UINT16 mthingtype) void P_RespawnSpecials(void) { UINT8 p, pcount = 0; - tic_t time = 30*TICRATE; // Respawn things in empty dedicated servers + INT32 time = 30*TICRATE; // Respawn things in empty dedicated servers mapthing_t *mthing = NULL; - if (!(gametyperules & GTR_CIRCUIT) && numgotboxes >= (4*nummapboxes/5)) // Battle Mode respawns all boxes in a different way - P_RespawnBattleBoxes(); + //if (!(gametyperules & GTR_CIRCUIT) && numgotboxes >= (4*nummapboxes/5)) // Battle Mode respawns all boxes in a different way + //P_RespawnBattleBoxes(); // wait time depends on player count for (p = 0; p < MAXPLAYERS; p++) @@ -9972,22 +10156,33 @@ void P_RespawnSpecials(void) pcount++; } - if (pcount == 1) // No respawn when alone - return; - else if (pcount > 1) + if (gametyperules & GTR_SPHERES) { - time = (120 - ((pcount-2) * 10))*TICRATE; + if (pcount > 2) + time -= (5*TICRATE) * (pcount-2); - // If the map is longer or shorter than 3 laps, then adjust ring respawn to account for this. - // 5 lap courses would have more retreaded ground, while 2 lap courses would have less. - if ((mapheaderinfo[gamemap-1]->numlaps != 3) - && !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE)) - time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps); - - if (time < 10*TICRATE) + if (time < 5*TICRATE) + time = 5*TICRATE; + } + else + { + if (pcount == 1) // No respawn when alone + return; + else if (pcount > 1) { - // Ensure it doesn't go into absurdly low values - time = 10*TICRATE; + time = (120 - ((pcount-2) * 10)) * TICRATE; + + // If the map is longer or shorter than 3 laps, then adjust ring respawn to account for this. + // 5 lap courses would have more retreaded ground, while 2 lap courses would have less. + if ((mapheaderinfo[gamemap-1]->numlaps != 3) + && !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE)) + time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps); + + if (time < 10*TICRATE) + { + // Ensure it doesn't go into absurdly low values + time = 10*TICRATE; + } } } @@ -9996,7 +10191,7 @@ void P_RespawnSpecials(void) return; // the first item in the queue is the first to respawn - if (leveltime - itemrespawntime[iquetail] < time) + if (leveltime - itemrespawntime[iquetail] < (tic_t)time) return; mthing = itemrespawnque[iquetail]; @@ -10048,7 +10243,7 @@ void P_SpawnPlayer(INT32 playernum) /* if (bonusgame || specialstage) { - // Bots should avoid + // Bots should avoid p->spectator = true; } */ @@ -10145,30 +10340,35 @@ void P_SpawnPlayer(INT32 playernum) P_SetScale(mobj, mobj->destscale); P_FlashPal(p, 0, 0); // Resets - if ((gametyperules & GTR_BUMPERS)) // SRB2kart + if (gametyperules & GTR_BUMPERS) { mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height + 16*FRACUNIT, MT_PLAYERARROW); P_SetTarget(&overheadarrow->target, mobj); overheadarrow->drawflags |= MFD_DONTDRAW; P_SetScale(overheadarrow, mobj->destscale); - if (p->spectator && pcount > 1) // HEY! No being cheap... - p->kartstuff[k_bumper] = 0; - else if (p->kartstuff[k_bumper] > 0 || leveltime < 1 - || (p->jointime <= 1 && pcount <= 1)) + if (p->spectator) { - if (leveltime < 1 || (p->jointime <= 1 && pcount <= 1)) // Start of the map? - p->kartstuff[k_bumper] = K_StartingBumperCount(); // Reset those bumpers! - - if (p->kartstuff[k_bumper]) + // HEY! No being cheap... + p->bumpers = 0; + } + else if ((p->bumpers > 0) || (leveltime < starttime) || (pcount <= 1)) + { + if ((leveltime < starttime) || (pcount <= 1)) // Start of the map? { - angle_t diff = FixedAngle(360*FRACUNIT/p->kartstuff[k_bumper]); + // Reset those bumpers! + p->bumpers = K_StartingBumperCount(); + } + + if (p->bumpers) + { + angle_t diff = FixedAngle(360*FRACUNIT/p->bumpers); angle_t newangle = mobj->angle; fixed_t newx = mobj->x + P_ReturnThrustX(mobj, newangle + ANGLE_180, 64*FRACUNIT); fixed_t newy = mobj->y + P_ReturnThrustY(mobj, newangle + ANGLE_180, 64*FRACUNIT); mobj_t *mo; - for (i = 0; i < p->kartstuff[k_bumper]; i++) + for (i = 0; i < p->bumpers; i++) { mo = P_SpawnMobj(newx, newy, mobj->z, MT_BATTLEBUMPER); mo->threshold = i; @@ -10182,7 +10382,7 @@ void P_SpawnPlayer(INT32 playernum) } } } - else if (p->kartstuff[k_bumper] <= 0) + else if (p->bumpers <= 0) { mobj_t *karmahitbox = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_KARMAHITBOX); // Player hitbox is too small!! P_SetTarget(&karmahitbox->target, mobj); @@ -10403,6 +10603,7 @@ fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mt case MT_SPIKEBALL: case MT_EMBLEM: case MT_RING: + case MT_BLUESPHERE: offset += mthing->options & MTF_AMBUSH ? 24*mapobjectscale : 0; break; @@ -10509,8 +10710,14 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i) { - // Don't need this for Kart YET! (void)mthing; + + if ((gametyperules & GTR_SPHERES) && (i == MT_RING)) + return MT_BLUESPHERE; + + if ((gametyperules & GTR_PAPERITEMS) && (i == MT_RANDOMITEM)) + return MT_PAPERITEMSPOT; + return i; } @@ -11594,8 +11801,11 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean static void P_SetAmbush(mobj_t *mobj) { - if (mobj->type == MT_YELLOWDIAG || mobj->type == MT_REDDIAG || mobj->type == MT_BLUEDIAG) - mobj->angle += ANGLE_22h; + if (mobj->flags & MF_SPRING) + { + // gravity toggle + mobj->flags ^= MF_NOGRAVITY; + } if (mobj->flags & MF_NIGHTSITEM) { @@ -11624,9 +11834,6 @@ static void P_SetAmbush(mobj_t *mobj) static void P_SetObjectSpecial(mobj_t *mobj) { - if (mobj->type == MT_YELLOWDIAG || mobj->type == MT_REDDIAG || mobj->type == MT_BLUEDIAG) - mobj->flags |= MF_NOGRAVITY; - if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0) { // flag for strong/weak random boxes diff --git a/src/p_mobj.h b/src/p_mobj.h index 6ffa393aa..93b4c7c6b 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -157,6 +157,8 @@ typedef enum MF_RUNSPAWNFUNC = 1<<27, // Don't remap in Encore mode. (Not a drawflag so that it's settable by mobjinfo.) MF_DONTENCOREMAP = 1<<28, + // Hitbox extends just as far below as above. + MF_PICKUPFROMBELOW = 1<<29, // free: to and including 1<<31 } mobjflag_t; @@ -408,9 +410,6 @@ typedef struct mobj_s INT32 cvmem; struct pslope_s *standingslope; // The slope that the object is standing on (shouldn't need synced in savegames, right?) -#ifdef HWRENDER - struct pslope_s *modeltilt; // Slope used for model tilting. Also is not synched, this is totally visual. -#endif boolean colorized; // Whether the mobj uses the rainbow colormap boolean mirrored; // The object's rotations will be mirrored left to right, e.g., see frame AL from the right and AR from the left @@ -420,6 +419,8 @@ typedef struct mobj_s fixed_t sprxoff, spryoff, sprzoff; // Sprite offsets in real space, does NOT affect position or collision + INT32 hitlag; // Sal-style hit lag, straight from Captain Fetch's jowls + // WARNING: New fields must be added separately to savegame and Lua. } mobj_t; @@ -530,7 +531,6 @@ boolean P_ZMovement(mobj_t *mo); void P_RingZMovement(mobj_t *mo); boolean P_SceneryZMovement(mobj_t *mo); void P_PlayerZMovement(mobj_t *mo); -void P_EmeraldManager(void); extern INT32 modulothing; diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 6733fca79..7975a22f1 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -876,6 +876,10 @@ static void Polyobj_carryThings(polyobj_t *po, fixed_t dx, fixed_t dy) for (; mo; mo = mo->bnext) { + // lastlook is used by the SPB to determine targets, do not let it affect it + if (mo->type == MT_SPB) + continue; + if (mo->lastlook == pomovecount) continue; @@ -1106,6 +1110,10 @@ static void Polyobj_rotateThings(polyobj_t *po, vector2_t origin, angle_t delta, for (; mo; mo = mo->bnext) { + // lastlook is used by the SPB to determine targets, do not let it affect it + if (mo->type == MT_SPB) + continue; + if (mo->lastlook == pomovecount) continue; diff --git a/src/p_saveg.c b/src/p_saveg.c index 7fc996333..4e6e31d88 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -111,6 +111,7 @@ static void P_NetArchivePlayers(void) WRITEANGLE(save_p, players[i].awayviewaiming); WRITEINT32(save_p, players[i].awayviewtics); WRITEINT16(save_p, players[i].rings); + WRITEINT16(save_p, players[i].spheres); for (j = 0; j < NUMPOWERS; j++) WRITEUINT16(save_p, players[i].powers[j]); @@ -234,7 +235,7 @@ static void P_NetArchivePlayers(void) if (flags & FOLLOWITEM) WRITEUINT32(save_p, players[i].followmobj->mobjnum); - + WRITEUINT32(save_p, (UINT32)players[i].followitem); WRITEUINT32(save_p, players[i].charflags); @@ -256,7 +257,14 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); WRITEUINT32(save_p, players[i].airtime); WRITEUINT8(save_p, players[i].trickpanel); - WRITEUINT32(save_p, players[i].trickdelay); + WRITEUINT8(save_p, players[i].trickdelay); + WRITEUINT32(save_p, players[i].trickmomx); + WRITEUINT32(save_p, players[i].trickmomy); + WRITEUINT32(save_p, players[i].trickmomz); + + WRITEUINT8(save_p, players[i].bumpers); + WRITEINT16(save_p, players[i].karmadelay); + WRITEUINT8(save_p, players[i].eliminated); // respawnvars_t WRITEUINT8(save_p, players[i].respawn.state); @@ -306,6 +314,7 @@ static void P_NetUnArchivePlayers(void) players[i].awayviewaiming = READANGLE(save_p); players[i].awayviewtics = READINT32(save_p); players[i].rings = READINT16(save_p); + players[i].spheres = READINT16(save_p); for (j = 0; j < NUMPOWERS; j++) players[i].powers[j] = READUINT16(save_p); @@ -420,7 +429,7 @@ static void P_NetUnArchivePlayers(void) if (flags & FOLLOWITEM) players[i].followmobj = (mobj_t *)(size_t)READUINT32(save_p); - + players[i].followitem = (mobjtype_t)READUINT32(save_p); //SetPlayerSkinByNum(i, players[i].skin); @@ -443,7 +452,14 @@ static void P_NetUnArchivePlayers(void) players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p); players[i].airtime = READUINT32(save_p); players[i].trickpanel = READUINT8(save_p); - players[i].trickdelay = READUINT32(save_p); + players[i].trickdelay = READUINT8(save_p); + players[i].trickmomx = READUINT32(save_p); + players[i].trickmomy = READUINT32(save_p); + players[i].trickmomz = READUINT32(save_p); + + players[i].bumpers = READUINT8(save_p); + players[i].karmadelay = READINT16(save_p); + players[i].eliminated = (boolean)READUINT8(save_p); // respawnvars_t players[i].respawn.state = READUINT8(save_p); @@ -1373,9 +1389,10 @@ typedef enum MD2_ROLLANGLE = 1<<14, MD2_SHADOWSCALE = 1<<15, MD2_DRAWFLAGS = 1<<16, - MD2_WAYPOINTCAP = 1<<17, - MD2_KITEMCAP = 1<<18, - MD2_ITNEXT = 1<<19, + MD2_HITLAG = 1<<17, + MD2_WAYPOINTCAP = 1<<18, + MD2_KITEMCAP = 1<<19, + MD2_ITNEXT = 1<<20, } mobj_diff2_t; typedef enum @@ -1590,6 +1607,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_SHADOWSCALE; if (mobj->drawflags) diff2 |= MD2_DRAWFLAGS; + if (mobj->hitlag) + diff2 |= MD2_HITLAG; if (mobj == waypointcap) diff2 |= MD2_WAYPOINTCAP; if (mobj == kitemcap) @@ -1754,6 +1773,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) WRITEUINT16(save_p, df); } + if (diff2 & MD2_HITLAG) + WRITEINT32(save_p, mobj->hitlag); WRITEUINT32(save_p, mobj->mobjnum); } @@ -2799,12 +2820,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) if (diff2 & MD2_ITNEXT) mobj->itnext = (mobj_t *)(size_t)READUINT32(save_p); if (diff2 & MD2_SLOPE) - { mobj->standingslope = P_SlopeById(READUINT16(save_p)); -#ifdef HWRENDER - mobj->modeltilt = mobj->standingslope; -#endif - } if (diff2 & MD2_COLORIZED) mobj->colorized = READUINT8(save_p); if (diff2 & MD2_MIRRORED) @@ -2818,6 +2834,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) } if (diff2 & MD2_DRAWFLAGS) mobj->drawflags = READUINT16(save_p); + if (diff2 & MD2_HITLAG) + mobj->hitlag = READINT32(save_p); if (diff & MD_REDFLAG) { @@ -4121,7 +4139,6 @@ static void P_NetArchiveMisc(void) // battleovertime_t WRITEUINT16(save_p, battleovertime.enabled); WRITEFIXED(save_p, battleovertime.radius); - WRITEFIXED(save_p, battleovertime.minradius); WRITEFIXED(save_p, battleovertime.x); WRITEFIXED(save_p, battleovertime.y); WRITEFIXED(save_p, battleovertime.z); @@ -4255,7 +4272,6 @@ static inline boolean P_NetUnArchiveMisc(void) // battleovertime_t battleovertime.enabled = READUINT16(save_p); battleovertime.radius = READFIXED(save_p); - battleovertime.minradius = READFIXED(save_p); battleovertime.x = READFIXED(save_p); battleovertime.y = READFIXED(save_p); battleovertime.z = READFIXED(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 50f39c5fa..15e6767a5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3924,40 +3924,40 @@ boolean P_LoadLevel(boolean fromnetsave) } */ - // Make sure all sounds are stopped before Z_FreeTags. - S_StopSounds(); - S_ClearSfx(); - - // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0. - // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug. - if (!titlemapinaction) - S_FadeMusic(0, FixedMul( - FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)); - - // Reset the palette now all fades have been done - if (rendermode != render_none) - V_SetPaletteLump(GetPalette()); // Set the level palette - - if (!titlemapinaction) - { - if (ranspecialwipe == 2) - { - pausedelay = -3; // preticker plus one - S_StartSound(NULL, sfx_s3k73); - } - - // As oddly named as this is, this handles music only. - // We should be fine starting it here. - // Don't do this during titlemap, because the menu code handles music by itself. - S_Start(); - } - - levelfadecol = (encoremode ? 0 : 31); - // Let's fade to white here // But only if we didn't do the encore startup wipe if (!demo.rewinding) { + // Make sure all sounds are stopped before Z_FreeTags. + S_StopSounds(); + S_ClearSfx(); + + // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0. + // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug. + if (!titlemapinaction) + S_FadeMusic(0, FixedMul( + FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)); + + // Reset the palette now all fades have been done + if (rendermode != render_none) + V_SetPaletteLump(GetPalette()); // Set the level palette + + if (!titlemapinaction) + { + if (ranspecialwipe == 2) + { + pausedelay = -3; // preticker plus one + S_StartSound(NULL, sfx_s3k73); + } + + // As oddly named as this is, this handles music only. + // We should be fine starting it here. + // Don't do this during titlemap, because the menu code handles music by itself. + S_Start(); + } + + levelfadecol = (encoremode ? 0 : 31); + if (rendermode != render_none) { F_WipeStartScreen(); diff --git a/src/p_slopes.c b/src/p_slopes.c index d903afb71..f3e1e2930 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -897,9 +897,8 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) if (P_MobjFlip(thing)*(thing->momz) < 0) // falling, land on slope { thing->standingslope = slope; -#ifdef HWRENDER - thing->modeltilt = thing->standingslope; -#endif + P_SetPitchRollFromSlope(thing, slope); + if (!thing->player || !(thing->player->pflags & PF_BOUNCING)) thing->momz = -P_MobjFlip(thing); } @@ -916,9 +915,7 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope) thing->momx = mom.x; thing->momy = mom.y; thing->standingslope = slope; -#ifdef HWRENDER - thing->modeltilt = thing->standingslope; -#endif + P_SetPitchRollFromSlope(thing, slope); if (!thing->player || !(thing->player->pflags & PF_BOUNCING)) thing->momz = -P_MobjFlip(thing); } diff --git a/src/p_spec.c b/src/p_spec.c index 81f1526d4..62dffaf67 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1612,7 +1612,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller { if (GETSECSPECIAL(caller->special, 2) == 6) { - if (!(ALL7EMERALDS(emeralds))) + if (!(ALLCHAOSEMERALDS(emeralds))) return false; } @@ -2028,6 +2028,14 @@ static void K_HandleLapIncrement(player_t *player) player->karthud[khud_laphand] = 0; // No hands in FREE PLAY player->karthud[khud_lapanimation] = 80; + + // save best lap for record attack + if (player == &players[consoleplayer]) + { + if (curlap < bestlap || bestlap == 0) + bestlap = curlap; + curlap = 0; + } } if (rainbowstartavailable == true) @@ -2041,14 +2049,6 @@ static void K_HandleLapIncrement(player_t *player) if (netgame && player->laps >= (UINT8)cv_numlaps.value) CON_LogMessage(va(M_GetText("%s has finished the race.\n"), player_names[player-players])); - // SRB2Kart: save best lap for record attack - if (player == &players[consoleplayer]) - { - if (curlap < bestlap || bestlap == 0) - bestlap = curlap; - curlap = 0; - } - player->starpostnum = 0; if (P_IsDisplayPlayer(player)) @@ -2133,6 +2133,7 @@ static void K_HandleLapDecrement(player_t *player) { player->starpostnum = numstarposts; player->laps--; + curlap = UINT32_MAX; } } } @@ -4617,7 +4618,7 @@ DoneSection2: } player->trickpanel = 1; - player->trickdelay = TICRATE/2; + player->trickdelay = 1; K_DoPogoSpring(player->mo, upwards, 1); // Reduce speed @@ -4667,7 +4668,7 @@ DoneSection2: } lineangle = K_ReflectAngle( - R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy), lineangle, + K_MomentumAngle(player->mo), lineangle, playerspeed, linespeed ); diff --git a/src/p_tick.c b/src/p_tick.c index 79e71c655..c7e6a946a 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -332,6 +332,11 @@ static inline void P_RunThinkers(void) ps_thlist_times[i] = I_GetTimeMicros() - ps_thlist_times[i]; } + if (gametyperules & GTR_PAPERITEMS) + K_RunPaperItemSpawners(); + + if ((gametyperules & GTR_BUMPERS) && battleovertime.enabled) + K_RunBattleOvertime(); } // @@ -516,6 +521,8 @@ void P_Ticker(boolean run) if (demo.rewinding && leveltime > 0) { leveltime = (leveltime-1) & ~3; + if (timeinmap > 0) + timeinmap = (timeinmap-1) & ~3; G_PreviewRewind(leveltime); } else if (demo.freecam && democam.cam) // special case: allow freecam to MOVE during pause! @@ -592,9 +599,6 @@ void P_Ticker(boolean run) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); - if ((gametyperules & GTR_BUMPERS) && battleovertime.enabled) - K_RunBattleOvertime(); - ps_lua_thinkframe_time = I_GetTimeMicros(); LUAh_ThinkFrame(); ps_lua_thinkframe_time = I_GetTimeMicros() - ps_lua_thinkframe_time; @@ -613,9 +617,7 @@ void P_Ticker(boolean run) if (run) leveltime++; - // as this is mostly used for HUD stuff, add the record attack specific hack to it as well! - if (!(modeattacking && !demo.playback) || leveltime >= starttime - TICRATE*4) - timeinmap++; + timeinmap++; if (G_GametypeHasTeams()) P_DoTeamStuff(); @@ -752,9 +754,6 @@ void P_PreTicker(INT32 frames) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); - if ((gametyperules & GTR_BUMPERS) && battleovertime.enabled) - K_RunBattleOvertime(); - LUAh_ThinkFrame(); // Run shield positioning diff --git a/src/p_user.c b/src/p_user.c index 821ff850f..090f75bd5 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -461,15 +461,9 @@ UINT8 P_FindHighestLap(void) // boolean P_PlayerInPain(player_t *player) { - if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_squishedtimer] || player->respawn.state != RESPAWNST_NONE) + if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_squishedtimer]) return true; - if (gametyperules & GTR_KARMA) - { - if (player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) - return true; - } - return false; } @@ -1290,6 +1284,8 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost->colorized = mobj->colorized; // Kart: they should also be colorized if their origin is ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle); + ghost->roll = mobj->roll; + ghost->pitch = mobj->pitch; ghost->sprite = mobj->sprite; ghost->sprite2 = mobj->sprite2; ghost->frame = mobj->frame; @@ -1298,13 +1294,11 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost->fuse = ghost->info->damage; ghost->skin = mobj->skin; ghost->standingslope = mobj->standingslope; -#ifdef HWRENDER - ghost->modeltilt = mobj->modeltilt; -#endif ghost->sprxoff = mobj->sprxoff; ghost->spryoff = mobj->spryoff; ghost->sprzoff = mobj->sprzoff; + ghost->rollangle = mobj->rollangle; if (mobj->flags2 & MF2_OBJECTFLIP) ghost->flags |= MF2_OBJECTFLIP; @@ -1940,11 +1934,6 @@ static void P_3dMovement(player_t *player) totalthrust.x += P_ReturnThrustX(player->mo, movepushangle, movepushforward); totalthrust.y += P_ReturnThrustY(player->mo, movepushangle, movepushforward); - - if (K_PlayerUsesBotMovement(player) == true) - { - K_MomentumToFacing(player); - } } if ((totalthrust.x || totalthrust.y) @@ -2274,7 +2263,7 @@ void P_MovePlayer(player_t *player) if (trailScale > 0) { - const angle_t forwardangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + const angle_t forwardangle = K_MomentumAngle(player->mo); const fixed_t playerVisualRadius = player->mo->radius + 8*FRACUNIT; const size_t numFrames = S_WATERTRAIL8 - S_WATERTRAIL1; const statenum_t curOverlayFrame = S_WATERTRAIL1 + (leveltime % numFrames); @@ -2647,7 +2636,7 @@ static void P_DeathThink(player_t *player) { if (player->spectator || !circuitmap) curlap = 0; - else + else if (curlap != UINT32_MAX) curlap++; // This is too complicated to sync to realtime, just sorta hope for the best :V } } @@ -4074,7 +4063,7 @@ static void P_HandleFollower(player_t *player) player->follower->drawflags |= MFD_DONTDRAW; if (player->speed && (player->follower->momx || player->follower->momy)) - player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); + player->follower->angle = K_MomentumAngle(player->follower); // if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward. // Make sure the follower itself is also moving however, otherwise we'll be facing angle 0 @@ -4220,6 +4209,11 @@ void P_PlayerThink(player_t *player) } #endif + if (player->mo->hitlag > 0) + { + return; + } + if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj)) { P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid @@ -4306,6 +4300,7 @@ void P_PlayerThink(player_t *player) if (player->playerstate == PST_DEAD) { + LUAh_PlayerThink(player); return; } } @@ -4382,7 +4377,7 @@ void P_PlayerThink(player_t *player) { if (player->spectator || !circuitmap) curlap = 0; - else + else if (curlap != UINT32_MAX) curlap++; // This is too complicated to sync to realtime, just sorta hope for the best :V } } @@ -4535,7 +4530,7 @@ void P_PlayerThink(player_t *player) || player->kartstuff[k_growshrinktimer] > 0 // Grow doesn't flash either. || (player->respawn.state != RESPAWNST_NONE) // Respawn timer (for drop dash effect) || (player->pflags & PF_GAMETYPEOVER) // NO CONTEST explosion - || ((gametyperules & GTR_BUMPERS) && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) + || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0 && player->karmadelay) || leveltime < starttime)) // Level intro { if (player->powers[pw_flashing] > 0 && player->powers[pw_flashing] < K_GetKartFlashing(player) diff --git a/src/r_main.c b/src/r_main.c index 8e05b8d27..835ed26f7 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -368,29 +368,7 @@ angle_t R_PointToAngle2(fixed_t pviewx, fixed_t pviewy, fixed_t x, fixed_t y) fixed_t R_PointToDist2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1) { - angle_t angle; - fixed_t dx, dy, dist; - - dx = abs(px1 - px2); - dy = abs(py1 - py2); - - if (dy > dx) - { - fixed_t temp; - - temp = dx; - dx = dy; - dy = temp; - } - if (!dy) - return dx; - - angle = (tantoangle[FixedDiv(dy, dx)>>DBITS] + ANGLE_90) >> ANGLETOFINESHIFT; - - // use as cosine - dist = FixedDiv(dx, FINESINE(angle)); - - return dist; + return FixedHypot(px1 - px2, py1 - py2); } // Little extra utility. Works in the same way as R_PointToAngle2 diff --git a/src/r_picformats.c b/src/r_picformats.c index 95fe23aeb..19079d3b7 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -23,6 +23,7 @@ #include "v_video.h" #include "z_zone.h" #include "w_wad.h" +#include "r_main.h" // R_PointToAngle #ifdef HWRENDER #include "hardware/hw_glob.h" @@ -1605,6 +1606,27 @@ void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps) } #ifdef ROTSPRITE +// +// R_SpriteRotationAngle +// +// Gets the rollangle for the input object. +// +angle_t R_SpriteRotationAngle(mobj_t *mobj) +{ +#if 0 + angle_t viewingAngle = R_PointToAngle(mobj->x, mobj->y); + + fixed_t pitchMul = -FINESINE(viewingAngle >> ANGLETOFINESHIFT); + fixed_t rollMul = FINECOSINE(viewingAngle >> ANGLETOFINESHIFT); + + angle_t rollOrPitch = FixedMul(mobj->pitch, pitchMul) + FixedMul(mobj->roll, rollMul); + + return (rollOrPitch + mobj->rollangle); +#else + return mobj->rollangle; +#endif +} + // // R_GetRollAngle // diff --git a/src/r_picformats.h b/src/r_picformats.h index 3ee76a92f..d677e783b 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -122,6 +122,7 @@ void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum); // Sprite rotation #ifdef ROTSPRITE +angle_t R_SpriteRotationAngle(mobj_t *mobj); INT32 R_GetRollAngle(angle_t rollangle); void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip); void R_FreeSingleRotSprite(spritedef_t *spritedef); diff --git a/src/r_skins.c b/src/r_skins.c index 951eeaad6..135221ea6 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -876,6 +876,11 @@ boolean SetPlayerFollower(INT32 playernum, const char *skinname) INT32 i; player_t *player = &players[playernum]; + if (stricmp("None", skinname) == 0) + { + SetFollower(playernum, -1); // reminder that -1 is nothing + return true; + } for (i = 0; i < numfollowers; i++) { // search in the skin list diff --git a/src/r_things.c b/src/r_things.c index df1946ba6..e1e38041b 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1230,32 +1230,6 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) return groundz; } -static void R_SetSpritePlaneHeights(vissprite_t *vis) -{ - ffloor_t *rover; - - fixed_t top; - fixed_t bot; - - vis->pt = vis->sector->floorheight; - vis->pb = vis->sector->ceilingheight; - - for (rover = vis->sector->ffloors; rover; rover = rover->next) - { - if (rover->flags & FF_EXISTS) - { - top = P_GetFFloorTopZAt (rover, vis->gx, vis->gy); - bot = P_GetFFloorBottomZAt (rover, vis->gx, vis->gy); - - if (top <= vis->gzt && top > vis->pt) - vis->pt = top; - - if (bot >= vis->gz && bot < vis->pb) - vis->pb = bot; - } - } -} - static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t tx, fixed_t tz) { vissprite_t *shadow; @@ -1344,8 +1318,6 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, shadow->xscale = FixedMul(xscale, shadowxscale); //SoM: 4/17/2000 shadow->scale = FixedMul(yscale, shadowyscale); shadow->sector = vis->sector; - shadow->pt = vis->pt; - shadow->pb = vis->pb; shadow->szt = (INT16)((centeryfrac - FixedMul(shadow->gzt - viewz, yscale))>>FRACBITS); shadow->sz = (INT16)((centeryfrac - FixedMul(shadow->gz - viewz, yscale))>>FRACBITS); shadow->cut = SC_ISSCALED|SC_SHADOW; //check this @@ -1403,9 +1375,9 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, // static void R_ProjectSprite(mobj_t *thing) { - const fixed_t thingxpos = thing->x + thing->sprxoff; - const fixed_t thingypos = thing->y + thing->spryoff; - const fixed_t thingzpos = thing->z + thing->sprzoff; + fixed_t thingxpos = thing->x + thing->sprxoff; + fixed_t thingypos = thing->y + thing->spryoff; + fixed_t thingzpos = thing->z + thing->sprzoff; mobj_t *oldthing = thing; @@ -1462,8 +1434,24 @@ static void R_ProjectSprite(mobj_t *thing) #ifdef ROTSPRITE patch_t *rotsprite = NULL; INT32 rollangle = 0; + angle_t spriterotangle = 0; #endif + // hitlag vibrating + if (thing->hitlag > 0) + { + fixed_t mul = thing->hitlag * (FRACUNIT / 10); + + if (leveltime & 1) + { + mul = -mul; + } + + thingxpos += FixedMul(thing->momx, mul); + thingypos += FixedMul(thing->momy, mul); + thingzpos += FixedMul(thing->momz, mul); + } + // transform the origin point tr_x = thingxpos - viewx; tr_y = thingypos - viewy; @@ -1587,9 +1575,11 @@ static void R_ProjectSprite(mobj_t *thing) spr_topoffset = spritecachedinfo[lump].topoffset; #ifdef ROTSPRITE - if (thing->rollangle) + spriterotangle = R_SpriteRotationAngle(thing); + + if (spriterotangle != 0) { - rollangle = R_GetRollAngle(thing->rollangle); + rollangle = R_GetRollAngle(spriterotangle); if (!(sprframe->rotsprite.cached & (1<sprite, frame, sprinfo, sprframe, rot, flip); rotsprite = sprframe->rotsprite.patch[rot][rollangle]; @@ -1845,9 +1835,6 @@ static void R_ProjectSprite(mobj_t *thing) vis->sector = thing->subsector->sector; vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS); vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS); - - R_SetSpritePlaneHeights(vis); - vis->cut = cut; if (thing->subsector->sector->numlights) vis->extra_colormap = *thing->subsector->sector->lightlist[light].extra_colormap; @@ -2062,8 +2049,6 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, yscale))>>FRACBITS); vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, yscale))>>FRACBITS); - R_SetSpritePlaneHeights(vis); - iscale = FixedDiv(FRACUNIT, xscale); vis->startfrac = 0; @@ -2443,12 +2428,12 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps // bird: if any part of the sprite peeks in front the plane if (planecameraz < viewz) { - if (rover->pt >= planeobjectz && rover->gzt >= planeobjectz) + if (rover->gzt >= planeobjectz) continue; } else if (planecameraz > viewz) { - if (rover->pb <= planeobjectz && rover->gz <= planeobjectz) + if (rover->gz <= planeobjectz) continue; } @@ -2481,7 +2466,7 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps } else if (r2->thickseg) { - fixed_t topplaneobjectz, topplanecameraz, botplaneobjectz, botplanecameraz; + //fixed_t topplaneobjectz, topplanecameraz, botplaneobjectz, botplanecameraz; if (rover->x1 > r2->thickseg->x2 || rover->x2 < r2->thickseg->x1) continue; @@ -2492,6 +2477,11 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps if (scale <= rover->sortscale) continue; + // bird: Always sort sprites behind segs. This helps the plane + // sorting above too. Basically if the sprite gets sorted behind + // the seg here, it will be behind the plane too, since planes + // are added after segs in the list. +#if 0 topplaneobjectz = P_GetFFloorTopZAt (r2->ffloor, rover->gx, rover->gy); topplanecameraz = P_GetFFloorTopZAt (r2->ffloor, viewx, viewy); botplaneobjectz = P_GetFFloorBottomZAt(r2->ffloor, rover->gx, rover->gy); @@ -2500,6 +2490,7 @@ static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean temps if ((topplanecameraz > viewz && botplanecameraz < viewz) || (topplanecameraz < viewz && rover->gzt < topplaneobjectz) || (botplanecameraz > viewz && rover->gz > botplaneobjectz)) +#endif { entry = R_CreateDrawNode(NULL); (entry->prev = r2->prev)->next = entry; diff --git a/src/r_things.h b/src/r_things.h index f95cd5c9e..400a63bcb 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -171,7 +171,6 @@ typedef struct vissprite_s // Precalculated top and bottom screen coords for the sprite. sector_t *sector; // The sector containing the thing. - fixed_t pt, pb; // plane heights, also for sorting against 3D floors INT16 sz, szt; spritecut_e cut; diff --git a/src/s_sound.c b/src/s_sound.c index a7d45e160..7c9abbf32 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -2336,7 +2336,7 @@ static boolean S_PlayMusic(boolean looping, UINT32 fadeinms) S_InitMusicVolume(); // switch between digi and sequence volume if (S_MusicNotInFocus()) - S_PauseAudio(); + I_SetMusicVolume(0); return true; } @@ -2780,9 +2780,9 @@ static void PlayMusicIfUnfocused_OnChange(void) if (window_notinfocus) { if (cv_playmusicifunfocused.value) - I_PauseSong(); + I_SetMusicVolume(0); else - I_ResumeSong(); + S_InitMusicVolume(); } } diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 5a9db4963..5e28e8643 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -651,7 +651,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) window_notinfocus = false; if (!paused) - S_ResumeAudio(); //resume it + S_InitMusicVolume(); if (cv_gamesounds.value) S_EnableSound(); @@ -670,7 +670,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) // Tell game we lost focus, pause music window_notinfocus = true; if (!cv_playmusicifunfocused.value) - I_PauseSong(); + I_SetMusicVolume(0); if (!cv_playsoundifunfocused.value) S_DisableSound(); diff --git a/src/sounds.c b/src/sounds.c index d06e521d0..dc777a088 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -740,14 +740,14 @@ sfxinfo_t S_sfx[NUMSFX] = {"cdfm61", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm62", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Speed boost"}, {"cdfm63", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, - {"cdfm64", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm64", false, 64, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm65", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm66", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm67", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm68", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm69", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm70", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, - {"cdfm71", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"cdfm71", false, 64, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm72", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm73", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"cdfm74", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, @@ -808,7 +808,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"kc4f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc50", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc51", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""}, - {"kc52", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + {"kc52", false, 64, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc53", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc54", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc55", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, diff --git a/src/st_stuff.c b/src/st_stuff.c index a8e7d2434..d6f80a9f5 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -673,10 +673,8 @@ void ST_preDrawTitleCard(void) if (lt_ticker >= (lt_endtime + TICRATE)) return; - if (!lt_exitticker) - st_translucency = 0; - else - st_translucency = max(0, min((INT32)lt_exitticker-4, cv_translucenthud.value)); + // Kart: nothing + st_translucency = cv_translucenthud.value; } // diff --git a/src/stun.c b/src/stun.c new file mode 100644 index 000000000..722d32906 --- /dev/null +++ b/src/stun.c @@ -0,0 +1,235 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by James R. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file stun.c +/// \brief RFC 5389 client implementation to fetch external IP address. + +/* https://tools.ietf.org/html/rfc5389 */ + +#if defined (__linux__) +#include +#elif defined (_WIN32) +#define _CRT_RAND_S +#elif defined (__APPLE__) +#include +#else +#error "Need CSPRNG." +#endif + +#include "doomdef.h" +#include "d_clisrv.h" +#include "command.h" +#include "i_net.h" +#include "stun.h" + +/* https://gist.github.com/zziuni/3741933 */ +/* I can only trust google to keep their shit up :y */ +consvar_t cv_stunserver = CVAR_INIT ( + "stunserver", "stun.l.google.com:19302", CV_SAVE, NULL, NULL +); + +static stun_callback_t stun_callback; + +/* 18.4 STUN UDP and TCP Port Numbers */ + +#define STUN_PORT "3478" + +/* 6. STUN Message Structure */ + +#define BIND_REQUEST 0x0001 +#define BIND_RESPONSE 0x0101 + +static const UINT32 MAGIC_COOKIE = MSBF_LONG (0x2112A442); + +static char transaction_id[12]; + +/* 18.2 STUN Attribute Registry */ + +#define XOR_MAPPED_ADDRESS 0x0020 + +/* 15.1 MAPPED-ADDRESS */ + +#define STUN_IPV4 0x01 + +static SINT8 +STUN_node (void) +{ + SINT8 node; + + char * const colon = strchr(cv_stunserver.zstring, ':'); + + const char * const host = cv_stunserver.zstring; + const char * const port = &colon[1]; + + I_Assert(I_NetMakeNodewPort != NULL); + + if (colon != NULL) + { + *colon = '\0'; + + node = I_NetMakeNodewPort(host, port); + + *colon = ':'; + } + else + { + node = I_NetMakeNodewPort(host, STUN_PORT); + } + + return node; +} + +static void +csprng +( + void * const buffer, + const size_t size +){ +#if defined (_WIN32) + size_t o; + + for (o = 0; o < size; o += sizeof (unsigned int)) + { + rand_s((unsigned int *)&((char *)buffer)[o]); + } +#elif defined (__linux__) + getrandom(buffer, size, 0U); +#elif defined (__APPLE__) + CCRandomGenerateBytes(buffer, size); +#elif defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) + arc4random_buf(buffer, size); +#endif +} + +void +STUN_bind (stun_callback_t callback) +{ + /* 6. STUN Message Structure */ + + const UINT16 type = MSBF_SHORT (BIND_REQUEST); + + const SINT8 node = STUN_node(); + + doomcom->remotenode = node; + doomcom->datalength = 20; + + csprng(transaction_id, 12U); + + memcpy(&doomcom->data[0], &type, 2U); + memset(&doomcom->data[2], 0, 2U); + memcpy(&doomcom->data[4], &MAGIC_COOKIE, 4U); + memcpy(&doomcom->data[8], transaction_id, 12U); + + stun_callback = callback; + + I_NetSend(); + Net_CloseConnection(node);/* will handle response at I_NetGet */ +} + +static size_t +STUN_xor_mapped_address (const char * const value) +{ + const UINT32 xaddr = *(const UINT32 *)&value[4]; + const UINT32 addr = xaddr ^ MAGIC_COOKIE; + + (*stun_callback)(addr); + + return 0U; +} + +static size_t +align4 (size_t n) +{ + return n + n % 4U; +} + +static size_t +STUN_parse_attribute (const char * const attribute) +{ + /* 15. STUN Attributes */ + const UINT16 type = MSBF_SHORT (*(const UINT16 *)&attribute[0]); + const UINT16 length = MSBF_SHORT (*(const UINT16 *)&attribute[2]); + + /* 15.2 XOR-MAPPED-ADDRESS */ + if ( + type == XOR_MAPPED_ADDRESS && + length == 8U && + (unsigned char)attribute[5] == STUN_IPV4 + ){ + return STUN_xor_mapped_address(&attribute[4]); + } + + return align4(4U + length); +} + +boolean +STUN_got_response +( + const char * const buffer, + const size_t size +){ + const char * const end = &buffer[size]; + + const char * p = &buffer[20]; + + UINT16 type; + UINT16 length; + + /* + Check for STUN response. + + Header is 20 bytes. + XOR-MAPPED-ADDRESS attribute is required. + Each attribute has a 2 byte header. + The XOR-MAPPED-ADDRESS attribute also has a 8 byte value. + This totals 10 bytes for the attribute. + */ + + if (size < 30U || stun_callback == NULL) + { + return false; + } + + /* 6. STUN Message Structure */ + + if ( + *(const UINT32 *)&buffer[4] == MAGIC_COOKIE && + memcmp(&buffer[8], transaction_id, 12U) == 0 + ){ + type = MSBF_SHORT (*(const UINT16 *)&buffer[0]); + length = MSBF_SHORT (*(const UINT16 *)&buffer[2]); + + if ( + (type >> 14) == 0U && + (length & 0x02) == 0U && + (20U + length) <= size + ){ + if (type == BIND_RESPONSE) + { + do + { + length = STUN_parse_attribute(p); + + if (length == 0U) + { + break; + } + + p += length; + } + while (p < end) ; + } + + stun_callback = NULL; + + return true; + } + } + + return false; +} diff --git a/src/stun.h b/src/stun.h new file mode 100644 index 000000000..de23aeb42 --- /dev/null +++ b/src/stun.h @@ -0,0 +1,20 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by James R. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file stun.h +/// \brief RFC 5389 client implementation to fetch external IP address. + +#ifndef KART_STUN_H +#define KART_STUN_H + +typedef void (*stun_callback_t)(UINT32 address); + +void STUN_bind (stun_callback_t); +boolean STUN_got_response (const char * const buffer, const size_t size); + +#endif/*KART_STUN_H*/